Skip to main content

nexus-cluster-swoole

Swoole-specific implementations for nexus-cluster interfaces. Provides Unix domain socket transport, shared-memory actor directory, and the ClusterBootstrap entry point.

Composer: nexus-actors/cluster-swoole

Namespace: Monadial\Nexus\Cluster\Swoole\

View class diagram

Requires: Swoole PHP extension 6.0+

Classes

ClusterBootstrap

Entry point for starting a multi-process cluster. Creates a Swoole\Process\Pool and manages the lifecycle of all worker processes.

final class ClusterBootstrap
{
public static function create(ClusterConfig $config): self;
public function onWorkerStart(callable $callback): self;
public function withSerializer(ClusterSerializer $serializer): self;
public function run(): void;
}

Usage:

ClusterBootstrap::create(ClusterConfig::withWorkers(8))
->onWorkerStart(function (ClusterNode $node): void {
$node->spawn(Props::fromBehavior($behavior), 'my-actor');
})
->run();

run() blocks until the process pool exits. Each worker process runs independently with its own SwooleRuntime, ActorSystem, UnixSocketTransport, and ClusterNode.

UnixSocketTransport

Implements Monadial\Nexus\Cluster\Transport\Transport.

AF_UNIX domain socket transport for inter-worker IPC. Each worker creates a server socket at {socketDir}/worker-{id}.sock and connects as a client to all other workers.

Wire format: [4 bytes: payload length (network byte order)][N bytes: payload]

All reads and writes are non-blocking via Swoole coroutines.

final class UnixSocketTransport implements Transport
{
public function __construct(int $workerId, int $workerCount, string $socketDir);
public function bind(): void;
public function connectToPeers(): void;
public function send(int $targetWorker, string $data): void;
public function listen(callable $onMessage): void;
public function close(): void;
}

Lifecycle: bind() creates the server socket and starts an accept coroutine. After all workers bind, connectToPeers() establishes client connections to every other worker. listen() registers the message callback. close() shuts down all connections and removes the socket file.

SwooleTableDirectory

Implements Monadial\Nexus\Cluster\Directory\ActorDirectory.

Shared-memory actor directory backed by Swoole\Table. The table is created in the master process before forking and is shared across all worker processes via shared memory -- no IPC overhead for directory lookups.

final readonly class SwooleTableDirectory implements ActorDirectory
{
public static function createTable(int $size): Table;
public function __construct(Table $table);
public function register(string $path, int $workerId): void;
public function lookup(string $path): ?int;
public function remove(string $path): void;
public function has(string $path): bool;
}

createTable() creates a Swoole\Table with a single worker_id column. Pass the same Table instance to each worker's SwooleTableDirectory.

CompactClusterSerializer

Implements Monadial\Nexus\Cluster\Serialization\ClusterSerializer.

Compact binary format that sends actor paths as raw UTF-8 strings and only calls PHP serialize() on the message object. ~6x smaller wire format than full PHP serialization. Benchmarked at 1.13M serialize+deserialize cycles/sec.

This is the default serializer used by ClusterBootstrap.

PhpNativeClusterSerializer

Implements Monadial\Nexus\Cluster\Serialization\ClusterSerializer.

Uses PHP's native serialize() / unserialize() on the full Envelope. Available as an alternative when full object graph serialization is needed.

Static analysis

See nexus-psalm for Psalm rules that apply to clustered actors:

  • NonSerializableClusterMessage -- Catches messages missing #[MessageType] before they cause runtime serialization failures.
  • NonReadonlyMessage -- Ensures all actor messages are immutable.
  • BlockingCallInHandler -- Flags blocking calls (sleep, file_get_contents, etc.) that would starve the Swoole coroutine runtime.