Repository navigation
fix(OMN-15868): BaseProjectionRunner retry-loop guard never enters - #2050
Conversation
self._running is initialized to the literal False in __init__, and run()'s while-guard read `self._running is not False` -- always False on a freshly constructed runner (Python singleton identity), so the loop body (the only site that constructs AIOKafkaConsumer, awaits start(), and sets self._running = True) never executed for any subclass, on any invocation. The process logged "Consumer failed after 10 retries" with zero real attempts and exited 0, which Kubernetes reports as CrashLoopBackOff (OMN-15800, deploy run 31493137810). Fix: track shutdown intent with a dedicated self._shutdown_requested sentinel, separate from the self._running readiness signal. The while-guard now reads `not self._shutdown_requested`; shutdown() sets both self._running = False (unchanged, readiness) and self._shutdown_requested = True (new). RED-then-GREEN: tests/test_projection_runner_retry_loop_omn15868.py proves (1) a failing AIOKafkaConsumer.start() is actually attempted and a real await asyncio.sleep() backoff observed -- RED against pre-fix code (0 calls, 0 sleeps, loop skipped entirely), and (2) a start() that fails twice then succeeds sets self._running = True and exits via the success path, not the retry-exhaustion path -- RED against pre-fix code (self._running never leaves False). Also updates the stale test-only `_running = True` priming comment in test_projection_kafka_auth_kwargs_omn15816.py, which documented this exact defect as "filed as its own ticket, not fixed here" -- it is fixed here now, and the priming is no longer necessary.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
Comment |
✅ Architectural Review — PASSEDErrors: 0 What this checks
No architectural violations found. Static architectural lint — no model inference (OMN-14176). |
✅ Hostile Reviewer — PASSEDBlocking findings (critical/error): 0 Gate semantics
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524) |
…6358) * evidence: OCC companion pass 1 for OmniNode-ai/omnimarket#2050 * evidence: OCC companion self-bind for #6358 --------- Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>
OMN-15868 — BaseProjectionRunner.run() retry-loop guard never enters
Fixes the root cause behind every
BaseProjectionRunnersubclass (registration,delegation, live-events, savings, session-outcome, etc.) silently exiting clean
(
exit 0) instead of connecting to Kafka when deployed as a standalone k8sprocess — observed live as
CrashLoopBackOffonomnimarket-projection-live-events-writer(OMN-15800, deploy run 31493137810).Root cause
src/omnimarket/projection/runner.py:self._runningis initialized to the literalFalse.False is not Falseis always
False(Python singleton identity), so the guard's second clauseis
Falseon the very first evaluation — before a singleAIOKafkaConsumeris ever constructed. The loop body (the only site that builds
AIOKafkaConsumer, awaitsself._consumer.start(), and setsself._running = Trueon success) never executes, for any subclass, on anyinvocation. Execution fell straight through to
logger.error("Consumer failed after %d retries", MAX_RETRY_ATTEMPTS)andrun()returned normally (exit code0) with zero real connectionattempts — Kubernetes reads a clean-but-immediate exit as
CrashLoopBackOff.Seam:
self._runningsemantics before/after__init__(runner.py:409)self._running = False__init__(new)self._shutdown_requested = Falserun()while-guard (runner.py:736)attempts < MAX_RETRY_ATTEMPTS and self._running is not False(alwaysFalse, self-defeating)attempts < MAX_RETRY_ATTEMPTS and not self._shutdown_requestedrun()on successfulstart()(runner.py:765)self._running = Truerun()per-message check (runner.py:774)if not self._running: break_start_health_server_if_configured()readiness lambda (runner.py:434)self._running is True_runningshutdown()(runner.py:802)self._running = Falseself._running = Falseandself._shutdown_requested = True(new)_runningis preservedEvery existing reader/writer of
self._runningwas enumerated (grep -n "_running\b" src/omnimarket/projection/runner.py) and is listed above —none were removed or repurposed.
self._shutdown_requestedhas exactly onewriter (
shutdown()) and one reader (therun()while-guard).RED-then-GREEN evidence
tests/test_projection_runner_retry_loop_omn15868.py, run against pre-fixcode (
git stashthe fix, same test file):Both failed with
Failed: DID NOT RAISE <class 'tests.test_projection_runner_retry_loop_omn15868._ControlFlowStop'>— the fake
AIOKafkaConsumer.start()was never even called. Captured logline (byte-exact):
ERROR omnimarket.projection.runner:runner.py:794 Consumer failed after 10 retries, printed with zero elapsed loopiterations — reproducing the ticket's live signature.
Post-fix (this PR), isolated run of this test file:
test_run_retry_loop_actually_enters_and_retries_omn15868): a fakeAIOKafkaConsumerwhosestart()always raises proves the loop body isentered at all (
start_call_count >= 1) and a realawait asyncio.sleep()backoff is awaited with the correct exponential-backoff delay
(
RETRY_BASE_DELAY * 2**1).test_run_retry_loop_recovers_and_sets_running_true_omn15868): afake consumer that fails twice then succeeds on the 3rd
start()callresults in
self._running is True, proving the loop exits via the successpath (message iteration begins) rather than the retry-exhaustion path.
tracked as a downstream step in this build lane (rebuild → repin → redeploy
→ watch rollout), not verifiable from this PR alone.
Full local focused suite (
-k projection,-m "not kafka"):1408 passed, 15 skipped, 15089 deselected, 4 warnings. Full governedpre-push suite (escalated to whole-repo because
runner.pyis a sharedmodule consumed by every projection-runner subclass):
15280 passed, 47 skipped, 3 deselected, 1 xfailed, 594.54s— zero failures.ruff format/ruff check --fix/mypy --stricton the changed file:all clean.
pre-commit run --files <changed>: all hooks passed.Also touched
tests/test_projection_kafka_auth_kwargs_omn15816.pycarried a# ... this is a test-only workaround ... filed as its own ticket, not fixed herecomment plus a
runner._running = Truepriming line, documenting this exactdefect as out of scope for OMN-15816. That ticket is this one; the comment
is updated to say so and the now-unnecessary priming is removed. No
assertion or test behavior changed — all 6 tests in that file still pass.
Scope note
Deliberately not bundled with OMN-15800's k8s-manifest PRs (#858/#859/#860/
#861, all already merged, all stay merged) — this is a shared-runner code
defect in
omnimarket, not a manifest/infra defect.dod_evidence: this PR is the dod_evidence for OMN-15868 — RED-then-GREEN test
run above, full governed pre-push suite green,
pre-commit run --filesgreen. Downstream deploy verification (AC3) is tracked as a follow-up
build-lane step (image rebuild in
omnibase_infra, digest repin inomninode_infra, redeploy watch) and will be recorded against this ticketonce complete.
Evidence-Ticket: OMN-15868
Evidence-Source: OCC#6358