Skip to content

docs: describe the shielded, bounded child close in the integration guide - #735

Merged
lesnik512 merged 1 commit into
mainfrom
docs/integration-guide-shielded-close
Oct 19, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
docs/integration-guide-shielded-close

Conversation

@lesnik512

Copy link
Copy Markdown
Member

#728 told integration authors to catch the cancellation, close the child again with cancellation shielded, and re-raise. None of the integrations ended up doing that. modern-di-starlette, fastapi, litestar, fastmcp and faststream all close the child once, under anyio.move_on_after(10, shield=True), and log a warning when the bound expires (modern-python/modern-di-starlette#67 and its siblings).

The lifecycle rule in "Writing an integration" now describes that pattern and why it's needed: anyio cancellation is level-triggered, so an unshielded close in a cancelled scope skips every finalizer after the first await. It links modern-di-starlette's _close_child as the reference, and notes that a plain asyncio framework needs no shield, because a native task.cancel() is delivered once. The checklist gains a matching item, including a test that cancels a running handler.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Details
Benchmark suite Current: 75a923a Previous: 839b719 Ratio
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type 6902828.061046244 iter/sec (stddev: 5.408628001315632e-9) 7103903.900672631 iter/sec (stddev: 4.217723836718964e-9) 1.03
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry 6698967.74934505 iter/sec (stddev: 8.734914609226463e-9) 6741928.15908836 iter/sec (stddev: 3.6644982068702364e-9) 1.01
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve 31045.083545740166 iter/sec (stddev: 0.00003224298491216108) 32792.36182352902 iter/sec (stddev: 0.000029995502785523644) 1.06
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached 24339.158879197206 iter/sec (stddev: 0.00014592076105164736) 25473.27759769293 iter/sec (stddev: 0.0001255503891384327) 1.05
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] 860.4689038844547 iter/sec (stddev: 0.00013649357018668938) 908.5595391095443 iter/sec (stddev: 0.000025229906828901984) 1.06
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] 850.9854677332619 iter/sec (stddev: 0.00014400223088932966) 867.4750825881105 iter/sec (stddev: 0.00004039695683905601) 1.02
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] 838.0596426740266 iter/sec (stddev: 0.00003571336585952352) 855.1452145561834 iter/sec (stddev: 0.000030439249013127648) 1.02
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] 25962.101390070155 iter/sec (stddev: 0.000007228819505582675) 25286.368119196148 iter/sec (stddev: 0.0000051611185457197225) 0.97
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] 11866.824561343097 iter/sec (stddev: 0.000017781325427334936) 11933.51124869696 iter/sec (stddev: 0.000015243668151211912) 1.01
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] 6220.287436360446 iter/sec (stddev: 0.000021449978953821975) 6219.354454814388 iter/sec (stddev: 0.000021864592145821257) 1.00
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] 20280.10886372143 iter/sec (stddev: 0.000007198877548172384) 19360.871807117626 iter/sec (stddev: 0.00002596205942498727) 0.95
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] 9379.532219236266 iter/sec (stddev: 0.000011165466068730476) 8875.127182457281 iter/sec (stddev: 0.000014536555598245296) 0.95
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] 3858.110402339287 iter/sec (stddev: 0.00002499187352108574) 4146.775964622826 iter/sec (stddev: 0.00002375022817236859) 1.07
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] 57810.20716297215 iter/sec (stddev: 0.0000030438134253092825) 61210.15537701124 iter/sec (stddev: 0.000002114557974735316) 1.06
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] 25892.442362907845 iter/sec (stddev: 0.000009351982049340535) 30040.569789672594 iter/sec (stddev: 0.00000866585438400976) 1.16
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] 10917.383157860913 iter/sec (stddev: 0.000018823748172446288) 11127.514922659528 iter/sec (stddev: 0.000024827521301215907) 1.02
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container 3017364.9352008607 iter/sec (stddev: 1.612593398822089e-8) 3298339.3274008934 iter/sec (stddev: 1.5697503391843764e-8) 1.09
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope 3553890.123668379 iter/sec (stddev: 8.932574485906521e-9) 3692705.5955528105 iter/sec (stddev: 1.0892855482088905e-8) 1.04
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch 6692.458977577859 iter/sec (stddev: 0.000005150781503104354) 7028.975890991392 iter/sec (stddev: 0.000008377685168699104) 1.05
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control 115738.17888616276 iter/sec (stddev: 7.113695802021378e-7) 112693.03897417623 iter/sec (stddev: 0.0000012530717484272372) 0.97
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync 869244.8817549022 iter/sec (stddev: 5.995147891053477e-8) 830588.7221186338 iter/sec (stddev: 1.2866511067636353e-7) 0.96
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale 129664.97422124991 iter/sec (stddev: 6.980595539664326e-7) 124773.65489535635 iter/sec (stddev: 0.0000014179824907585822) 0.96
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers 1022.2565277693884 iter/sec (stddev: 0.0018655638954125325) 1149.3090224790162 iter/sec (stddev: 0.001015771648957769) 1.12
benchmarks/test_guard_resolve.py::test_g1_transient_resolve 4112684.25852696 iter/sec (stddev: 2.379731641960378e-8) 3923080.1671545426 iter/sec (stddev: 4.271555180870276e-8) 0.95
benchmarks/test_guard_resolve.py::test_g2_cached_resolve 7006683.745704858 iter/sec (stddev: 5.294932643950329e-9) 6688372.2181033725 iter/sec (stddev: 8.51133524066809e-9) 0.95
benchmarks/test_guard_resolve.py::test_g3_deep_chain 1592855.4063602774 iter/sec (stddev: 3.870416233064613e-8) 1541234.2188217025 iter/sec (stddev: 8.586162798662186e-8) 0.97
benchmarks/test_guard_resolve.py::test_g4_wide_resolve 879578.8787445802 iter/sec (stddev: 3.799155945139577e-7) 877888.0541711425 iter/sec (stddev: 4.1740039310411333e-7) 1.00
benchmarks/test_guard_resolve.py::test_g5_cross_scope 3738782.1347106085 iter/sec (stddev: 2.144849326280454e-8) 3516345.953947673 iter/sec (stddev: 4.843126891505467e-8) 0.94
benchmarks/test_guard_resolve.py::test_g9_context_resolve 2771321.368795965 iter/sec (stddev: 1.384052178605967e-7) 2800641.514949102 iter/sec (stddev: 1.1643950854293895e-7) 1.01
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve 1576295.0445956492 iter/sec (stddev: 4.3960284050894976e-8) 1546309.0569229047 iter/sec (stddev: 6.751062420007285e-8) 0.98
benchmarks/test_guard_resolve.py::test_g18_alias_hop 6847883.579406012 iter/sec (stddev: 5.5388720991959885e-9) 6896588.823060502 iter/sec (stddev: 6.903243911826836e-9) 1.01
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain 72705.37298762775 iter/sec (stddev: 0.000011191761780017393) 72981.44631598827 iter/sec (stddev: 0.000011464777285859204) 1.00
benchmarks/test_guard_validate.py::test_g11_validate_wide 42916.04021762566 iter/sec (stddev: 0.000014378936007556305) 42457.095124035404 iter/sec (stddev: 0.000014099007728673246) 0.99

This comment was automatically generated by workflow using github-action-benchmark.

@lesnik512
lesnik512 merged commit 53b159a into main Oct 19, 2026
12 checks passed
@lesnik512
lesnik512 deleted the docs/integration-guide-shielded-close branch October 19, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant