Skip to content

Hand cluster shards off before final cleanup on shutdown (v3) - #8946

Merged
tim-smart merged 10 commits into
Effect-TS:v3from
Tanner-Scadden:failing-tests/v3-graceful-drain
Oct 9, 2026
Merged

tim-smart merged 10 commits into
Effect-TS:v3from
Tanner-Scadden:failing-tests/v3-graceful-drain

Conversation

@Tanner-Scadden

@Tanner-Scadden Tanner-Scadden commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When Sharding shuts down, it now hands its shards off after their entities and singletons stop, releasing each lock individually before the final releaseAll. Live runners can take the shards over without waiting for lock leases to expire. There's no new public API: disposing the layer, e.g. on SIGTERM, starts the handoff. This backports the design of the main PR, #8941.

Changes

Sharding.ts:

  • Shutdown start. Once shutdown starts, including the preemptive shutdown triggered by entity finalizers, the runner stops acquiring shards. Registration and unregistration are serialized, so a registration that's in flight can't re-advertise a runner that is shutting down.

  • Handoff. The Sharding finalizer runs shutdown, then the handoff:

    • every held shard is released;
    • entities still running on those shards are forced to stop;
    • each lock is released with RunnerStorage.release once its entities and singletons have stopped.

    While handing off, the pending shards are released in one batch, so 600 shards don't each need their own release fiber. The trade-off is head-of-line blocking: one slow entity or singleton teardown delays every lock in the batch, and one slow storage release delays the ones after it. Shards whose ordinary release was already running when shutdown started are left to that release.

  • Best effort. Only the handoff wait is bounded, by the shard lock interval. If it times out, the final releaseAll attempts to release the remaining locks. Total shutdown is not bounded: unregistering and the final releaseAll can still wait on storage. Finalizers are uninterruptible, so the wait is made interruptible for its timeout. Release forks run in the FiberSet, because forks into the closing sharding scope never run.

  • Singletons. Shard releases now wait for the singletons on those shards to stop. That includes singletons whose registration scope is already closing. A singleton is not restarted while its previous instance is still stopping. Singleton teardown is awaited outside the singleton lock, so a singleton that registered another singleton can still be closed.

internal/entityManager.ts:

  • Interrupting a shard now finds entities by address. Before, an entity with the same id on another shard could hide it, and the release waited forever. This can happen with workflow shard groups.
  • interruptShard became interruptShards, which takes several shards, so each entity manager needs one fiber per batch.

Tests

In packages/cluster/test/Sharding.test.ts, under Sharding graceful shutdown, one test per behaviour change:

  • hands shards off to a live peer before the final releaseAll, tearing down entities first
  • waits for singleton teardown before releasing its shard
  • stops acquiring shards once shutdown starts
  • scope close finishes while lock storage is unhealthy
  • interrupts an entity whose id is also active on a shard that was interrupted first

Validation

  • Sharding.test.ts: 34/34. On unmodified v3 sources, four of the five tests above fail; the unhealthy-storage test covers the new handoff wait. Removing the shutdown acquire guard, the singleton teardown wait, or the interruptible handoff wait each fails the matching test.
  • ClusterWorkflowEngine, run alone: about 2.9s for its slowest test, the same as v3 (about 2.85s).
  • @effect/cluster tests other than SqlRunnerStorage passed 111/111 before the test reduction. SqlRunnerStorage needs Docker containers, which were unavailable locally; it doesn't use Sharding.
  • platform-node NodeClusterSocket.test.ts: pass.
  • tsc -b for @effect/cluster: pass. eslint and docgen passed before the test reduction.

Closes EFF-1884

@changeset-bot

changeset-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0f5ec6a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@effect/cluster Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-janitor effect-janitor Bot added the 3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3. label Oct 9, 2026
@effect-bot effect-bot changed the title Failing tests: graceful shutdown cannot hand shards off before the final releaseAll (v3) Add Sharding.drain and hand shards off before releaseAll on shutdown (v3) Oct 9, 2026
Tanner-Scadden and others added 8 commits October 9, 2026 10:50
…the final releaseAll (v3)

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Move the reproductions into Sharding.test.ts, instrument entity teardown
to assert teardown-before-release, and close runners while advancing the
TestClock so cleanup cannot hang.
Draining unregisters the runner, stops shard acquisition and releases each
shard lock after its entities are torn down. Scope close drains first,
bounded by entityTerminationTimeout plus the lock interval, before the
final releaseAll.
…e during drain

The empty acquisition case fails until drain re-checks completion after a
successful acquire that returns no shards.
Replace the drain-based tests with shutdown-only coverage: entity and
singleton teardown before shard release, closing and nested singleton
registrations, no acquisition once shutdown starts, and duplicate entity
ids across shards.
Backport the v4 shutdown handoff: remove Sharding.drain, stop acquiring
once shutdown starts, release every shard after its entities and
singletons stop in the Sharding finalizer, serialize registration with
unregistration, and look entities up by address when interrupting a
shard.
@effect-bot
effect-bot force-pushed the failing-tests/v3-graceful-drain branch from 2cb6a8e to fe13740 Compare October 9, 2026 11:03
@effect-bot effect-bot changed the title Add Sharding.drain and hand shards off before releaseAll on shutdown (v3) Hand cluster shards off before final cleanup on shutdown (v3) Oct 9, 2026
effect-bot and others added 2 commits October 9, 2026 11:17
…ndoff

Also clarify that each lock is released individually and that only the
handoff wait is bounded.
@tim-smart
tim-smart merged commit 2568ce7 into Effect-TS:v3 Oct 9, 2026
8 checks passed
@effect-bot effect-bot mentioned this pull request Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants