Skip to content

feat: gather async deps concurrently with per-key deduplication#704

Closed
FRidh wants to merge 1 commit intoreagento:developfrom
FRidh:feat/async-concurrent-resolution-b
Closed

feat: gather async deps concurrently with per-key deduplication#704
FRidh wants to merge 1 commit intoreagento:developfrom
FRidh:feat/async-concurrent-resolution-b

Conversation

@FRidh
Copy link
Copy Markdown

@FRidh FRidh commented Apr 3, 2026

When resolving an async factory with 2+ async dependencies, the compiled
getter now awaits them concurrently via asyncio.gather instead of
sequentially. For N independent deps each taking T seconds, resolution
takes max(T) instead of sum(T). No user-side changes are required.

Concurrency safety for cached dependencies is handled via a _Pending
Future sentinel in the cache dict: the first coroutine to resolve a dep
places a sentinel; concurrent coroutines find it and await the embedded
Future instead of duplicating work and registering duplicate exits.
On failure the sentinel is removed from the cache and the exception is
propagated to all waiters, so a subsequent retry resolves cleanly.

This is a pure compile-time code generation change: the FactoryBuilder
emits asyncio.gather calls and pending bookkeeping into the generated
factory functions. The cache dict semantics, CompiledFactory protocol,
Registry, and sync container are all unchanged.

#45

When resolving an async factory with 2+ async dependencies, the compiled
getter now awaits them concurrently via asyncio.gather instead of
sequentially. For N independent deps each taking T seconds, resolution
takes max(T) instead of sum(T). No user-side changes are required.

Concurrency safety for cached dependencies is handled via a _Pending
Future sentinel in the cache dict: the first coroutine to resolve a dep
places a sentinel; concurrent coroutines find it and await the embedded
Future instead of duplicating work and registering duplicate exits.
On failure the sentinel is removed from the cache and the exception is
propagated to all waiters, so a subsequent retry resolves cleanly.

This is a pure compile-time code generation change: the FactoryBuilder
emits asyncio.gather calls and pending bookkeeping into the generated
factory functions. The cache dict semantics, CompiledFactory protocol,
Registry, and sync container are all unchanged.

reagento#45
@FRidh FRidh force-pushed the feat/async-concurrent-resolution-b branch from 36ad7c0 to 4cc567a Compare April 3, 2026 08:15
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
12.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

container = make_async_container(IndependentDepsProvider())
start = asyncio.get_running_loop().time()
result = await container.get(str)
elapsed = asyncio.get_running_loop().time() - start
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do not use time measurements in test assertions. Use events or barriers if you want to check concurrent execution

@FRidh
Copy link
Copy Markdown
Author

FRidh commented Apr 5, 2026

Closing this POC, discussion in #45.

@FRidh FRidh closed this Apr 5, 2026
@github-project-automation github-project-automation bot moved this to To be released in Dishka kanban Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To be released

Development

Successfully merging this pull request may close these issues.

2 participants