Skip to content

Hand cluster shards off one at a time on shutdown - #8941

Merged
tim-smart merged 16 commits into
Effect-TS:mainfrom
Tanner-Scadden:failing-tests/v4-graceful-drain
Oct 9, 2026
Merged

tim-smart merged 16 commits into
Effect-TS:mainfrom
Tanner-Scadden:failing-tests/v4-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 tries to hand each shard to the remaining runners before the final releaseAll, instead of leaving every lock to releaseAll and, if that never runs, to lease expiry. There's no new public API: disposing the Sharding layer, for example on SIGTERM, starts the handoff.

  • No new shards. Once shutdown starts, the runner stops acquiring shards and doesn't register again. Registration and unregistration are serialized, so an in-flight registration can't advertise a runner that is shutting down.
  • Per-shard release. Each held shard is released through the normal per-shard path, after its entities and singletons have stopped.
  • Best effort. The wait is bounded by shardLockInterval. If it times out, releaseAll attempts to release the remaining locks. Neither step is guaranteed to finish before the process exits, so terminationGracePeriodSeconds should leave room for both.

Two related fixes:

  • Interrupting a shard no longer waits forever on an entity whose id is also active on another shard.
  • Singletons that are still stopping stay visible to shard release. Their teardown is awaited outside the singleton registry lock, so a singleton that registers other singletons can be closed without deadlocking.

Background

This started as a report with failing tests. Kubernetes rollouts send SIGTERM to every old runner at once. When a process died before releaseAll finished, new runners waited out the roughly 35-second lease, and runners that were still exiting picked up shards freed by their peers, only to hand them off again.

The first version proposed a public Sharding.drain for preStop hooks. Review replaced it with the shutdown-only change above. The v3 variant of the original tests is #8946.

Tests

Four regressions in packages/effect/test/cluster/Sharding.test.ts:

  • Scope disposal waits for entity teardown, releases the held shard before releaseAll, and does not acquire a peer's newly freed shard.
  • A singleton whose registration is already closing keeps its shard locked until teardown finishes; both held shards are released before releaseAll.
  • A singleton that registers another singleton can close without deadlocking.
  • An entity id active on two shards is interrupted on both.

Validation

After the tests-only reduction:

  • Sharding.test.ts: 88/88 with default timeouts.
  • pnpm check and pnpm lint pass.
  • All four retained tests fail on the code they guard against: three on main before this PR, and the nested-singleton test on the earlier PR implementation that held the registry lock during teardown.

Earlier validation, before this reduction: 217/217 cluster tests and 109/109 platform-node cluster tests. Those broader suites were not rerun for the reduction. SQL integration suites and Bun/Deno runner tests were not run.

Closes EFF-1876

@changeset-bot

changeset-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4a8dd22

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

This PR includes changesets to release 32 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-cloudflare Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/ai-typesafe Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator 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

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
arbitrary-combinators.ts 38.83 KB 38.83 KB 0.00 KB (0.00%)
basic.ts 7.06 KB 7.06 KB 0.00 KB (0.00%)
batching.ts 10.14 KB 10.14 KB 0.00 KB (0.00%)
brand.ts 6.73 KB 6.73 KB 0.00 KB (0.00%)
cache.ts 10.90 KB 10.90 KB 0.00 KB (0.00%)
config.ts 22.31 KB 22.31 KB 0.00 KB (0.00%)
differ.ts 21.34 KB 21.34 KB 0.00 KB (0.00%)
http-client.ts 22.69 KB 22.69 KB 0.00 KB (0.00%)
http-router.ts 34.12 KB 34.12 KB 0.00 KB (0.00%)
logger.ts 11.23 KB 11.23 KB 0.00 KB (0.00%)
metric.ts 9.03 KB 9.03 KB 0.00 KB (0.00%)
optic.ts 6.96 KB 6.96 KB 0.00 KB (0.00%)
pubsub.ts 15.21 KB 15.21 KB 0.00 KB (0.00%)
queue.ts 12.14 KB 12.14 KB 0.00 KB (0.00%)
schedule.ts 11.25 KB 11.25 KB 0.00 KB (0.00%)
schema-bigdecimal.ts 13.77 KB 13.77 KB 0.00 KB (0.00%)
schema-binary.ts 40.02 KB 40.02 KB 0.00 KB (0.00%)
schema-class.ts 21.11 KB 21.11 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 32.15 KB 32.15 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 27.32 KB 27.32 KB 0.00 KB (0.00%)
schema-string-transformation.ts 14.52 KB 14.52 KB 0.00 KB (0.00%)
schema-string.ts 12.03 KB 12.03 KB 0.00 KB (0.00%)
schema-template-literal.ts 16.09 KB 16.09 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 38.36 KB 38.36 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 25.31 KB 25.31 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 20.27 KB 20.27 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 20.48 KB 20.48 KB 0.00 KB (0.00%)
schema-toFormatter.ts 20.58 KB 20.58 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 25.05 KB 25.05 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 20.57 KB 20.57 KB 0.00 KB (0.00%)
schema.ts 20.32 KB 20.32 KB 0.00 KB (0.00%)
stm.ts 13.27 KB 13.27 KB 0.00 KB (0.00%)
stream.ts 9.99 KB 9.99 KB 0.00 KB (0.00%)

@effect-bot effect-bot changed the title Failing tests: graceful shutdown cannot hand shards off before the final releaseAll Hand cluster shards off one at a time on shutdown Oct 9, 2026
@tim-smart
tim-smart enabled auto-merge (squash) October 9, 2026 11:15
@tim-smart
tim-smart merged commit 66257d2 into Effect-TS:main Oct 9, 2026
8 of 10 checks passed
brandhaug added a commit to brandhaug/b2b-saas-starter that referenced this pull request Oct 15, 2026
## pnpm-workspace.yaml (default)

## Dependency Updates

| Package | From | To | Type |
| --- | --- | --- | --- |
| `@inlang/plugin-message-format` | 4.5.0 | 4.5.2 | patch |
| `@tanstack/react-query` | 5.104.1 | 5.104.2 | patch |
| `effect` | 4.0.2 | 4.0.3 | patch |

## Release Notes

<details>
<summary><b>@<!---->tanstack/react-query</b> (5.104.1 →
5.104.2)</summary>

### Patch Changes

- Updated dependencies
[[`03e8c39`](TanStack/query@03e8c39)]:
  - @<!---->tanstack/[email protected]

</details>

<details>
<summary><b>effect</b> (4.0.2 → 4.0.3)</summary>

### Patch Changes

- [#8977](Effect-TS/effect#8977)
[`b4e5af8`](Effect-TS/effect@b4e5af8)
Thanks @<!---->kitlangton! - Prevent crashes and premature interruption
when `Effect.cached`, `Effect.cachedWithTTL`, and
`Effect.cachedInvalidateWithTTL` are synchronously re-entered.

- [#8982](Effect-TS/effect#8982)
[`b8d6e7a`](Effect-TS/effect@b8d6e7a)
Thanks @<!---->Yi-111-a! - Export `CliOutput.displayWidth` to measure
terminal-cell widths using the same logic as built-in CLI help and
tables.

- [#8933](Effect-TS/effect#8933)
[`89d5c88`](Effect-TS/effect@89d5c88)
Thanks @<!---->tim-smart! - Respect `NO_COLOR` in CLI prompts and the
command wizard, suppressing color and style escapes while preserving
cursor and redraw controls.

- [#8941](Effect-TS/effect#8941)
[`66257d2`](Effect-TS/effect@66257d2)
Thanks @<!---->Tanner-Scadden! - Hand cluster shards off one at a time
when `Sharding` shuts down, after their entities and singletons have
stopped, so live runners can take them over sooner. The handoff is best
effort: its wait is bounded, and if it times out, `releaseAll` attempts
to release the remaining locks. A runner that is shutting down no longer
acquires new shards, and releasing a shard no longer waits forever on an
entity whose id is also active on another shard.

- [#8940](Effect-TS/effect#8940)
[`cd2ab19`](Effect-TS/effect@cd2ab19)
Thanks @<!---->Tanner-Scadden! - Fix cluster shard handoffs. Interrupts
and stream acknowledgements now reach requests still draining on the
previous owner, and `RpcServer` accepts acknowledgements for active
streams after a client's EOF. Volatile requests sent duri

…[full
notes](https://github.com/Effect-TS/effect/releases/tag/effect%404.0.3)

</details>

---
*This PR was auto-generated by
[catalog-update-action](https://github.com/brandhaug/catalog-update-action).*

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

This branch had an error being deployed

1 failed deployment
fork — 4a8dd22a Deployed Oct 9, 2026 by effect-bot via approval-gate #18605
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants