1# Cluster integration tests
2
3This suite runs multi-runner clusters against shared PostgreSQL and MySQL
4containers. It is excluded from the default test projects and only registered
5when `EFFECT_CLUSTER_TESTS=1`.
6
7```sh
8EFFECT_CLUSTER_TESTS=1 pnpm test-cluster
9```
10
11Docker must be running. Vitest global setup starts one `postgres:alpine` and one
12`mysql:lts` container for the entire project. Every cluster uses a unique table
13prefix, and every runner listens on an operating-system-assigned port.
14
15## Harness
16
17`harness.ts` exposes:
18
19- `make({ backend, entities, lockMode, config })` to create a scoped cluster harness.
20- `start(count, { assignedShardGroups, entities, runnerShardWeight })` to start in-process socket runners and a client over SchemaBinary, optionally overriding the entity layer for those runners.
21- `stop(runner)` for graceful deregistration and shard handoff.
22- `kill(runner)` for abrupt teardown without deregistration or explicit lock cleanup.
23- `freeze(runner)` to suspend SQL heartbeats and lock refresh while leaving the runner's sockets and reserved SQL connection open.
24- `faultLock(runner, mode)` to blackhole, stick, fail, stick while blocking release of, or clear faults on a runner's reserved lock connection.
25- `cutSocket(runner, { peer, direction })` to close one inbound or outbound client/runner connection without stopping either endpoint.
26- `insertMessage(row)` to inject a raw row into this cluster's prefixed message table.
27- `waitUntil`, `waitForStableAssignments`, and `waitForEntityOwner` for deadline-based polling with cluster diagnostics on failure.
28- `clientSharding` and `ownersOfShard` for direct shard ownership assertions.
29- `messageCounts`, `unprocessedMessageCount`, `repliedMessageCount`, and `failedMessageCount` for storage assertions scoped to the cluster prefix.
30
31Advisory locks are owned by the reserved database session. A frozen advisory-lock
32runner therefore keeps its locks until it is stopped or killed. Row-lock mode
33uses expiry-driven takeover while frozen.
34
35## Adding a test
36
37Add `*.test.ts` under this directory. Test files define entities and assertions;
38all cluster startup, lifecycle, waiting, and storage inspection belongs in the
39harness. Use the harness polling helpers instead of calling `Effect.sleep`.