Skip to content

refactor: remove anyio monkey-patch feature - #5427

Open
jonpspri wants to merge 3 commits into
mainfrom
remove-anyio-monkey-patch
Open

jonpspri wants to merge 3 commits into
mainfrom
remove-anyio-monkey-patch

Conversation

@jonpspri

@jonpspri jonpspri commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Remove the experimental ANYIO_CANCEL_DELIVERY_PATCH_ENABLED monkey-patch (former Layer 3) and retire the now-obsolete SSE containment workaround for the CPU spin loop in anyio's _deliver_cancellation.

The upstream root cause was fixed in anyio 4.15.0 ("Fixed unnecessary CPU spin when delivering cancellation from CancelScope on asyncio under certain conditions", anyio#695/#1111). This PR pins anyio>=4.15.0 in pyproject.toml so the fix is guaranteed on every install path — that pin is what makes the removal safe, since the patch actually breaks if enabled against fixed anyio versions.

Changes

Code Changes

  • mcpgateway/transports/sse_transport.py: Removed the anyio monkey-patch (apply/remove helpers, module-load application) and the EventSourceResponse.__call__ cancel-deadline override (the SSE half of the former Layer 2 containment — its only consumer was the removed override). The class keeps the compression no-op and inherits sse-starlette's stock __call__.
  • mcpgateway/config.py: Removed anyio_cancel_delivery_patch_enabled, anyio_cancel_delivery_max_iterations, and sse_task_group_cleanup_timeout.
  • mcpgateway/translate.py: Updated the stale "CPU spin protection" comment on the EventSourceResponse import.
  • tests/unit/mcpgateway/transports/test_sse_transport.py: Removed the monkey-patch and cleanup-timeout tests.

Configuration Changes

  • pyproject.toml / uv.lock: Added anyio>=4.15.0 floor; the lock carries only the anyio 4.14.2 → 4.15.0 bump.
  • docker-compose.yml: Removed the Layer 3 env vars and the Layer 2 cleanup-timeout knobs, with a note pointing at the upstream fix.
  • charts/mcp-stack/values.yaml / values.schema.json / README.md: Removed SSE_TASK_GROUP_CLEANUP_TIMEOUT, MCP_SESSION_POOL_CLEANUP_TIMEOUT (already a silent no-op since the pool-config removal in [BUG]: Problem with session management for stateful MCP Servers #4205), and the ANYIO_CANCEL_DELIVERY_* entries.
  • .env.example: Removed the EXPERIMENTAL Layer-3 block and the cleanup-timeout entries.
  • docs/docs/architecture/explorer.html: Dropped the removed config key from the SSE transport node.

Documentation Changes

  • docs/docs/operations/cpu-spin-loop-mitigation.md: Containment layer removed from the strategy, remaining layers renumbered (Prevention → Recovery), status reflects the shipped upstream fix, and a "bounded cleanup" section documents the fixed 5-second windows that remain in the session-pool and streamable-HTTP shutdown paths.
  • docs/docs/manage/configuration.md: Spin-loop section now documents only the live SSE protection settings, with a note about the removed knobs.
  • docs/docs/manage/tuning.md: Dropped the dead MCP_SESSION_POOL_CLEANUP_TIMEOUT row.

Rationale

anyio 4.15.0 fixes the _deliver_cancellation spin loop upstream, making the experimental monkey-patch (which breaks if enabled against the fix) and the SSE cancel-deadline override unnecessary. Remaining mitigations: dead-client detection (SSE_SEND_TIMEOUT, SSE_RAPID_YIELD_*) and worker recycling (GUNICORN_MAX_REQUESTS); cleanup waits stay bounded via fixed 5-second windows in the session-pool and streamable-HTTP shutdown paths.

Testing

  • make test: 23256 passed, 906 skipped, 2 xfailed
  • make ruff interrogate pylint: pass (interrogate 100%, pylint 10.00/10)
  • make coverage diff-cover: 98% total, no uncovered diff lines
  • make docker-nuke docker-prod-rust testing-up RUST_MCP_MODE=: gateway stack up
  • make test-mcp-protocol-e2e test-mcp-rbac: pass (40 RBAC/transport tests)
  • make detect-secrets-scan: clean
  • charts: make lint lint-values pass

Closes #6285 — remove anyio monkey-patch feature (chore/test-cleanup epic #5825).

@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch 2 times, most recently from 95d7157 to b83df3b Compare June 29, 2026 21:18
@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch 3 times, most recently from 80dd80c to 7912de9 Compare July 24, 2026 15:38
@jonpspri jonpspri added the COULD P3: Nice-to-have features with minimal impact if left out; included if time permits label Aug 31, 2026
@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch from 7912de9 to 9ae7097 Compare September 1, 2026 09:35
@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch from 9ae7097 to 72a04b2 Compare September 14, 2026 10:35
Remove the experimental ANYIO_CANCEL_DELIVERY_PATCH_ENABLED feature
that was used to mitigate CPU spin loops in anyio's _deliver_cancellation.

Changes:
- Remove monkey-patch implementation from sse_transport.py
- Remove config settings (anyio_cancel_delivery_patch_enabled, anyio_cancel_delivery_max_iterations)
- Remove related test functions
- Update docker-compose files to remove Layer 3 environment variables
- Update Helm chart values and schema
- Update documentation to remove Layer 3 section and renumber layers

The SSE connection protection (Layer 1) and cleanup timeouts (Layer 2)
remain in place as the primary mitigation strategy.

Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch from 72a04b2 to 33db76f Compare September 14, 2026 11:04
- Pin anyio>=4.15.0 (upstream _deliver_cancellation CPU spin fix,
  anyio#695/#1111); uv.lock now carries only the anyio 4.14.2 -> 4.15.0 bump
- Remove dangling SSE_TASK_GROUP_CLEANUP_TIMEOUT, MCP_SESSION_POOL_CLEANUP_TIMEOUT,
  and ANYIO_CANCEL_DELIVERY_* references from docker-compose.yml, charts, and
  .env.example (Settings extra=ignore made them silent no-ops)
- Rework cpu-spin-loop-mitigation.md and configuration.md: containment layer
  removed, layers renumbered, status reflects the shipped upstream fix; drop
  dead knob row from tuning.md
- Update stale EventSourceResponse comment in translate.py and explorer.html
  node metadata; drop unrelated .gitignore entry

Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri
jonpspri force-pushed the remove-anyio-monkey-patch branch from 33db76f to 0e9416d Compare September 14, 2026 11:09

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

COULD P3: Nice-to-have features with minimal impact if left out; included if time permits

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove anyio monkey-patch (ANYIO_CANCEL_DELIVERY_PATCH_ENABLED) feature

1 participant