Skip to content

perf: resolve FromDI without a worker thread - #85

Merged
lesnik512 merged 1 commit into
mainfrom
perf/contextvar-request-container
Oct 17, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
perf/contextvar-request-container

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Every FromDI went through a worker thread on every message. Dependency.__call__ took context: faststream.ContextRepo to read the request container, and fast_depends resolves that parameter as a FastStream Context custom field. For each one it opens an anyio task group and runs the field's sync use() through anyio.to_thread.run_sync (fast_depends/core/model.py, the custom_fields block of asolve).

The request container now lives in a module-level ContextVar. The middleware sets it around call_next and resets it with the token, and Dependency.__call__ takes no parameters. FastStream's own context locals are ContextVars as well, so each message, and each task it spawns, still sees its own container. The variable also cannot collide with a user's context names, which is the problem #74 fixed by renaming the key.

The same change makes setup_di's two startup hooks async. FastStream ran them, as sync callables, in worker threads at every start, so install_middleware was changing broker config from a thread.

Measured through TestNatsBroker, median of 7 runs x 3000 messages, main and branch back to back:

handler main branch
no DI 72 us 75 us
3 plain Depends 101 us 108 us
1 FromDI 198 us 88 us
3 FromDI 428 us 104 us

The machine was not quiet (load average about 3.6), so single cells are noisy. The 2-4x gap on the FromDI rows is well outside that noise, and three FromDI now cost about the same as three plain Depends.

Two tests wrap anyio.to_thread.run_sync in a mock and assert it is never called. One covers handling a message with FromDI, and it fails on main with assert 1 == 0. The other covers app startup and shutdown, and it fails on main with assert 2 == 0.

Compatibility: the request container is no longer stored in the FastStream context under modern_di_request_container. That key was a private constant, and nothing in the org reads it. The modern-di FastStream docs page mentions it in the 4.0.1 rename note, so after this is released that note will need a line saying which version dropped the key.

@lesnik512
lesnik512 merged commit d1e6b9e into main Oct 17, 2026
12 checks passed
@lesnik512
lesnik512 deleted the perf/contextvar-request-container branch October 17, 2026 16:52
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