Set 'daemons' or background persistent server processes receiving mirai requests. These are, by default, automatically created on the local machine. Alternatively, a client URL may be specified to receive connections from remote servers started with server for distributing tasks across the network. Daemons may take advantage of the dispatcher, which ensures that tasks are assigned to servers efficiently on a FIFO basis, or else the low-level approach of distributing tasks to servers in an even fashion.

daemons(
  n,
  url = NULL,
  dispatcher = TRUE,
  refhook = NULL,
  ...,
  .compute = "default"
)

Arguments

n

integer number of daemons (server processes) to set.

url

[default NULL] if specified (for connecting to remote servers), the client URL as a character vector, including a port accepting incoming connections (and optionally for websockets a path) e.g. 'tcp://192.168.0.2:5555' or 'ws://192.168.0.2:5555/path'.

dispatcher

[default TRUE] logical value whether to use dispatcher. Dispatcher is a background process that connects to servers on behalf of the client and ensures FIFO scheduling, queueing tasks if necessary (see Dispatcher section below).

refhook

[default NULL] maps to the 'refhook' argument of serialize and unserialize for providing a hook function to handle reference objects. As the same function is used in both cases, it should contain the necessary logic to distinguish between them as required. Accepts a function or function encoded via base64enc.

...

additional arguments passed through to dispatcher if using dispatcher and/or server if launching local daemons.

.compute

[default 'default'] character compute profile to use for creating the daemons (each compute profile has its own set of daemons for connecting to different resources).

Value

Setting daemons: integer number of daemons set, or the character client URL.

Viewing current status: a named list comprising:

  • connections - number of active connections at the client. Always 1L when using dispatcher as there is only a single connection to the dispatcher, which then in turn connects to the servers.

  • daemons - if using dispatcher: a status matrix (see Status Matrix section below), or else an integer 'errorValue' if communication with the dispatcher was unsuccessful. If not using dispatcher: the number of daemons set, or else the client URL.

Details

For viewing the currrent status, specify daemons() with no arguments.

Use daemons(0) to reset daemon connections:

  • A reset is required before revising settings for the same compute profile, otherwise changes are not registered.

  • All connected daemons and/or dispatchers exit automatically.

  • mirai reverts to the default behaviour of creating a new background process for each request.

When specifying a client URL, all daemons dialing into the client are detected automatically and resources may be added or removed at any time.

If the client session ends, for whatever reason, all connected dispatcher and daemon processes automatically exit as soon as their connections are dropped. If a daemon is processing a task, it will exit as soon as the task is complete.

If setting 'refhook', the same function must be provided for all compute profiles. Servers launched manually, i.e. other than through this function or launch_server, must use the same 'refhook' argument to ensure seamless operation.

Dispatcher

By default dispatcher = TRUE. This launches a background process running dispatcher. A dispatcher connects to servers on behalf of the client and queues tasks until a server is able to begin immediate execution of that task, ensuring FIFO scheduling. Dispatcher uses synchronisation primitives from nanonext, waiting rather than polling for tasks, which is efficient both in terms of consuming no resources while waiting, and also being fully synchronised with events (having no latency).

By specifying dispatcher = FALSE, servers connect to the client directly rather than through a dispatcher. The client sends tasks to connected servers immediately in an evenly-distributed fashion. However, optimal scheduling is not guaranteed as the duration of tasks cannot be known a priori, such that tasks can be queued at a server behind a long-running task while other servers remain idle. Nevertheless, this provides a resource-light approach suited to working with similar-length tasks, or where concurrent tasks typically do not exceed available daemons.

Local Daemons

Daemons provide a potentially more efficient solution for asynchronous operations as new processes no longer need to be created on an ad hoc basis.

Supply the argument 'n' to set the number of daemons. New background server processes are automatically created on the local machine connecting back to the client process, either directly or via a dispatcher.

Distributed Computing

Specifying 'url' allows tasks to be distributed across the network.

The client URL should be in the form of a character string such as: 'tcp://192.168.0.2:5555' at which server processes started using server should connect to.

Alternatively, to listen to port 5555 on all interfaces on the local host, specify either 'tcp://:5555', 'tcp://*:5555' or 'tcp://0.0.0.0:5555'.

Specifying the wildcard value zero for the port number e.g. 'tcp://:0' or 'ws://:0' will automatically assign a free ephemeral port. Use daemons() to inspect the actual assigned port at any time.

With Dispatcher

When using dispatcher, it is recommended to use a websocket URL rather than TCP, as this requires only one port to connect to all servers: a websocket URL supports a path after the port number, which can be made unique for each server.

Specifying a single client URL such as 'ws://192.168.0.2:5555' with n = 6 will automatically append a sequence to the path, listening to the URLs 'ws://192.168.0.2:5555/1' through 'ws://192.168.0.2:5555/6'.

Alternatively, specify a vector of URLs to listen to arbitrary port numbers / paths. In this case it is optional to supply 'n' as this can be inferred by the length of vector supplied.

Individual server instances should then be started on the remote resource, which dial in to each of these client URLs. At most one server should be dialled into each URL at any given time.

The dispatcher automatically adjusts to the number of servers actually connected. Hence it is possible to dynamically scale up or down the number of servers as required, subject to the maximum number initially specified.

Alternatively, supplying a single TCP URL will listen on a block of URLs with ports starting from the supplied port number and incrementing by one for 'n' specified e.g. the client URL 'tcp://192.168.0.2:5555' with n = 6 listens to the contiguous block of ports 5555 through 5560.

Without Dispatcher

A TCP URL may be used in this case as the client listens at only one address, utilising a single port.

The network topology is such that server daemons (started with server) or indeed dispatchers (started with dispatcher) dial into the same client URL.

'n' is not required in this case, and disregarded if supplied, as network resources may be added or removed at any time. The client automatically distributes tasks to all connected servers and dispatchers.

Compute Profiles

By default, the 'default' compute profile is used. Providing a character value for '.compute' creates a new compute profile with the name specified. Each compute profile retains its own daemons settings, and may be operated independently of each other. Some usage examples follow:

local / remote daemons may be set via a client URL and creating a new compute profile by specifying '.compute' as 'remote'. Subsequent mirai calls may then be sent for local computation by not specifying its '.compute' argument, or for remote computation to connected daemons by specifying its '.compute' argument as 'remote'.

cpu / gpu some tasks may require access to different classes of server, such as those with GPUs. In this case, daemons() may be called twice to set up client URLs for CPU-only and GPU servers to dial into, specifying the '.compute' argument as 'cpu' and 'gpu' respectively. By supplying the '.compute' argument to subsequent mirai calls, tasks may be sent to either 'cpu' or 'gpu' servers as appropriate.

Note: further actions such as viewing the status of daemons or resetting via daemons(0) should be carried out with the desired '.compute' argument specified.

Status Matrix

When using dispatcher, calling daemons() returns a matrix with the following columns:

'online' shows as 1 when there is an active connection, or else 0 if a server has yet to connect or has disconnected.

'instance' increments by 1 every time there is a new connection at a URL. When this happens, the 'assigned' and 'complete' statistics reset to zero. This counter is designed to track new server instances connecting after previous ones have ended (due to time-outs etc.). 'instance' itself resets to zero if the URL is regenerated by saisei.

'assigned' shows the cumulative number of tasks assigned to the server instance by the dispatcher.

'complete' shows the cumulative number of tasks completed by the server instance.

The URLs are stored as row names to the matrix.

Timeouts

Specifying the .timeout argument in mirai will ensure that the 'mirai' always resolves.

However, the task may not have completed and still be ongoing in the daemon process. In such situations, dispatcher ensures that queued tasks are not assigned to the busy process, however overall performance may still be degraded if they remain in use. If a process hangs and cannot be restarted manually, saisei specifying force = TRUE may be used to regenerate any particular URL for a new server to connect to.

Examples

if (interactive()) {
# Only run examples in interactive R sessions

# Create 2 local daemons (using dispatcher)
daemons(2)
# View status
daemons()
# Reset to zero
daemons(0)

# Create 2 local daemons (not using dispatcher)
daemons(2, dispatcher = FALSE)
# View status
daemons()
# Reset to zero
daemons(0)

# 2 remote daemons via dispatcher (using zero wildcard)
daemons(2, url = "ws://:0")
# View status
daemons()
# Reset to zero
daemons(0)

# Set client URL for remote servers to dial into (using zero wildcard)
daemons(url = "tcp://:0", dispatcher = FALSE)
# View status
daemons()
# Reset to zero
daemons(0)

}