Uh oh!
There was an error while loading. Please reload this page.
fix(aggregator): make Aggregator thread-safe (closes #1) - #25
Conversation
- Aggregator: introduce MAX_BUCKETS (2000) constant + max_buckets config field + would_overflow() method + max_buckets getter. Prevents silent data loss when a window crosses the ingest API's 422 threshold. Direct port of the Node SDK's overflow protection. - Public surface: re-export MAX_BUCKETS and FlushStatus from the package __init__. - Tests: cover the new aggregator behavior, the lastFlushStatus semantics on RecostHandle, and the transport-side chunking + rejection signaling paths introduced alongside this feature.
AUDIT.md collects the full audit (tests, lint, mypy, source review, runtime behavior) and maps the 25 raw findings to the 13 consolidated issues filed in this repo. ROADMAP.md sequences those 13 issues into five waves with parallelism called out, so contributors know which issues can be picked up simultaneously and which depend on earlier work landing first. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two TDD-structured plans for the parallel Wave 1 work identified in ROADMAP.md: - 2026-05-13-aggregator-thread-safety.md walks through adding a threading.RLock and swap-and-process flush, with a stress test that reliably reproduces the race before the fix lands. Closes#1. - 2026-05-13-naming-and-docs-reconciliation.md walks through renaming the Flask extension class, stripping nonexistent EcoAPI* references from CLAUDE.md, swapping the deprecated flush_interval option for flush_interval_ms in the README, documenting max_buckets and shutdown_flush_timeout_ms, and pinning the BUILTIN_PROVIDERS count. Closes#5. Both plans contain bite-sized tasks with the exact code/diffs to apply and the exact pytest commands to verify each step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Guards _buckets / _window_start / _size with a threading.RLock so the background timer thread's flush() cannot race the interceptor's user threads ingesting events. flush() now uses a swap-and-process pattern so percentile computation runs outside the lock. Adds a stress-test regression that reliably reproduced the race on the old code (RuntimeError: dictionary changed size during iteration). Refs #1
Adds a second thread-safety test that proves no counter increments are lost when N threads race on a shared bucket. Complements the no-raise stress test by exercising the counter-increment side of the race. Refs #1
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Closes#1.
Guards
Aggregatorstate with athreading.RLockso the background timerthread's
flush()cannot race user threads callingingest()via theinterceptor.
flush()uses a swap-and-process pattern so percentilecomputation runs outside the critical section.
Tests
TestThreadSafety::test_concurrent_ingest_and_flush_does_not_raise—4 ingester threads × 50000 ingests vs. 500 flushes; reliably reproduces
the
RuntimeError: dictionary changed size during iterationon theold code with
sys.setswitchinterval(0.000001).TestThreadSafety::test_concurrent_correctness_no_lost_events—proves the lock also prevents lost updates on counter increments.
Notes
sys.setswitchinterval(0.000001)(restored infinally)to make the race deterministic on modern CPython (3.14 on this dev
machine); the underlying bug is not version-specific.
🤖 Generated with Claude Code