The inbox poller's notification callback called the synchronous
enrich_peer_metadata on every push, blocking the poller for up to
2s × N uncached peers per poll batch. Push delivery latency was
gated on registry RTT — exactly what PR #2471's negative-cache patch
was trying to avoid amplifying.
Fix: cache-first nonblocking path with a tiny background worker pool.
enrich_peer_metadata_nonblocking(peer_id):
- Cache hit (fresh, within TTL): return cached record immediately
- Cache miss / stale: return None, schedule background
fetch via ThreadPoolExecutor
The first push from a new peer arrives metadata-light (bare peer_id);
the next push within the 5-min TTL hits the warm cache and gets full
name/role. Acceptable trade-off because the channel-envelope
enrichment is a UX nicety, not a correctness invariant — and the
cold-cache window per peer is bounded to one push.
Defenses:
- In-flight gate (_enrich_in_flight) — N concurrent pushes for the
same uncached peer schedule exactly ONE worker, not N. Without
this, a chatty peer's first burst of pushes would amplify into
parallel registry GETs — the exact DoS-on-self pattern the
negative cache was meant to rate-limit.
- Lazy executor init — most test fixtures + short-lived CLI
invocations never need it; only the long-running molecule-mcp
path actually fires background work.
- Daemon-style threads via thread_name_prefix; executor never
blocks process exit.
Tests:
- test_enrich_peer_metadata_nonblocking_cache_hit_returns_immediately
- test_enrich_peer_metadata_nonblocking_cache_miss_schedules_fetch
- test_enrich_peer_metadata_nonblocking_coalesces_duplicate_pushes
- test_enrich_peer_metadata_nonblocking_invalid_peer_id_returns_none
Plus updates to the existing test_envelope_enrichment_* suite that
asserted synchronous behavior — they now drain the in-flight set via
_wait_for_enrichment_inflight_for_testing before checking cache state.
Existing synchronous enrich_peer_metadata is unchanged — Phase B (#2790)
schema↔dispatcher drift gate + the negative-cache contract from PR
#2471 still apply. The nonblocking variant is purely additive.
1739 passed, 0 failed locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>