fix(clients/python): consumer warns + acks unhandled event types#121
fix(clients/python): consumer warns + acks unhandled event types#121
Conversation
REV review — PR #121Independent reviewer here. Verdict only — no approve/merge. Verdict: REQUEST CHANGES (minor)The two fixes are correct in shape and the headline bug is genuinely closed. Counts
ConcernsC1 — Test is mildly racy / could degrade silently (test_consumer.py) In practice the rotation requires
C2 —
Two different semantics for the same input. PR #121 explicitly closes #111
This is small but it's literally the class of bug the second half of the PR NitsN1 — README in N2 — Log message tense. N3 — ConfidenceHigh on C1/C2 reasoning. Manually traced Anti-leakDiff + body + commits scanned. No leak markers found. Out of scope (do not block this PR)
|
REV Review — PR #121 (round 2)CI: green (all R1 concerns + nits status:
Verdict: READY FOR USER REVIEW BlockingNone. Non-blockingNone new. Potential
Summary table
Anti-leakDiff and commit messages clean. One marker found in the PR body ( REV-style review (security, bugs, tests, guidelines, docs). SOC2 items skipped per project policy. |
|
Comment-scrub sweep applied per CLAUDE.md style: no scrub needed — changes are docstrings on public API and log message improvements, no bug-narrative source comments. |
|
Behavior change landed — branch updated with two new commits on top of
What changed:
Generated by Claude Code |
Round-3 review (delta on top of r2 PASS)Verdict: PASS -- no blockers. Scope: just the two new commits Behavior change checks
Non-blocking nit (optional, do not gate on this)The warning format string says SummaryBehavior change is correctly red-then-green, aligned with PgQ/skytools heritage (default ack, surface the gap in logs), retry semantics for genuine handler failures are preserved, anti-leak posture intact. Ship it. Generated by Claude Code |
Red test for #111: Consumer silently acks messages with no registered handler; verify they land in retry_queue instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consumer previously skipped messages with no registered handler and then acked the whole batch, silently dropping them. Now calls nack() with reason "unhandled event type" so they go to retry queue. Fixes #111 finding 1. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The send()/send_batch() docstrings claimed str payloads are sent "as-is", obscuring that they are always cast to jsonb. A bare Python str like "hello" fails; it must be JSON text like '"hello"'. Clarify with examples and a note about the error. Fixes #111 finding 2. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C1: bump retry_after=30 in test_consumer_nacks_unhandled_event_type so the message cannot loop back within the 3-second window. C2 (red): add test_send_batch_none_payload_produces_json_null, which currently fails because send_batch passes Python None as SQL NULL instead of JSON null. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C2: coerce Python None to "null" in send_batch comprehension so send(None) and send_batch([None]) both store JSON null, not SQL NULL. N1: README Consumer section notes that messages with no registered handler are nacked (not silently dropped); shows '*' catch-all example. N2: log message now reads "no handler (and no '*' default)" to make the precondition explicit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
231789d to
03ab8ab
Compare
|
Rebased onto Conflicts: None. The rebase applied all 9 commits cleanly. The #129 changes (thread-safe signal handling in New head: CI: Generated by Claude Code |
REV final review — post-rebase (head
|
Summary
Closes #111
Changes behavior for messages with no registered handler (and no
*catch-all):nack()→ message moved toretry_queue, then DLQ at max retrieslogging.warning(...)via the consumer's own logger, then the batchack()covers the message normallyThe
Consumernow creates a per-instance logger (pgque.consumer.<name>) so the WARNING is attributable to the specific consumer.Finding 1: Behavior change — unhandled event type now warns + acks
Root cause of original issue (#111):
Consumer._poll_once()calledcontinuewith no handler registered, thenack()for the whole batch — silently dropping the message.PR #121 original fix: nack before continue. This is now revised: nack routes unknown types to DLQ, which conflates routing gaps with processing failures. The correct behavior is to surface the gap in logs and let the operator register a handler.
New fix:
self._log.warning("no handler for event type=%s ev_id=%s; acking", ...)thencontinue— the batch-levelack()at the end of the loop covers it.Commits (TDD):
7a0dcbb—test: red -- warn+ack unhandled event typeca0e9ef—fix(clients/python): warn+ack unhandled event typesTest renamed:
test_consumer_nacks_unhandled_event_type→test_consumer_warns_and_acks_unhandled_event_typeFinding 2: Docs bug — str payload contract misdocumented (client.py)
Unchanged from original PR #121.
send()andsend_batch()docstrings now document thatstrmust be valid JSON text.Test plan
PGQUE_TEST_DSN= pytest clients/python/tests/test_consumer.py::test_consumer_warns_and_acks_unhandled_event_type— was failing on7a0dcbb(red), passes onca0e9ef(green)PGQUE_TEST_DSN= pytest clients/python/tests/— no regressions