fix(clients): v0.2.0 data-safety + consumer alignment#156
Closed
fix(clients): v0.2.0 data-safety + consumer alignment#156
Conversation
Switch the Consumer's default unknown-handler policy from warn+ack to nack with reason "no handler for type=X". Add an explicit opt-in unknown_handler="ack" argument to restore the previous behaviour for allow-list filter patterns. Also bump the default max_messages from 100 to 500 to match the default ticker_max_count so a single receive can drain a full batch. Replace the warn+ack regression test with two new tests covering the nack default (retry/DLQ row appears, batch advances) and the opt-in ack path (no retry row, batch advances, warning emitted).
- Track per-batch nack failures in Consumer.Start; if any nack errors occur, skip the final batch ack so PgQ redelivers the batch via the existing batch lifecycle. - Add UnknownHandlerPolicy (NackUnknown default, AckUnknown opt-in) and expose it via WithUnknownHandlerPolicy. Default is NackUnknown: when a message arrives with no registered handler, route it to retry/DLQ instead of silently dropping it. - Add WithMaxMessages(n) and bump the default from 100 to 500 so a single Receive can drain a batch produced under the default queue_ticker_max_count. - Refactor Client.Nack to accept variadic options (WithRetryAfter, WithReason). Defaults stay at 60s / NULL reason; existing callers are unaffected. - Add Client.SendBatch as a 1:1 wrapper over pgque.send_batch. - Update HandlerFunc, Handle, Start doc comments to describe the actual runtime behaviour.
…aults
- Track per-poll nack failures in Consumer.start; if any nack errors
occur, skip the final batch ack so PgQ redelivers via the existing
batch lifecycle.
- Add unknownHandlerPolicy ConsumerOption ('nack' default, 'ack' opt-in)
to control behaviour for messages with no registered handler. Default
routes unknown types to retry/DLQ rather than silently dropping them.
- Bump default maxMessages from 100 to 500 to match the default
queue_ticker_max_count so a single Receive can drain a full batch.
- Coerce top-level undefined payload to JSON 'null' in Client.send via a
shared encodeJsonbPayload helper. Matches the Python driver's handling
of None and avoids JSON.stringify(undefined) producing the SQL string
"undefined".
- Add tests covering the ack-policy opt-in and undefined coercion.
Add a top-level clients/README.md that captures the v0.2.0 capability matrix per driver, the locked-in nack-default contract for unknown handler types, and the list of items deferred to v0.2.1+.
Owner
Author
|
Closing — superseded by the per-driver split per the maintainer's "multiple agents" preference:
Each PR has its own REV in flight. The work in this branch and the per-driver branches are equivalent in scope; the per-driver PRs are easier to review independently and align with the maintainer's stated preference. No code is lost — all four targeted issues (#134, #135, #136, #149) are covered by the per-driver PRs above. Closing this branch and PR. Generated by Claude Code |
This was referenced May 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cross-driver fix to lock the v0.2.0 consumer contract on data-safety:
no driver may silently drop a message because of a missing handler or a
Nack failure, and the default
max_messagesmatches the defaultqueue_ticker_max_countso a singleReceivecan drain a batch.with an explicit opt-in to restore the previous warn+ack behaviour
for allow-list filter patterns. This intentionally reverses
PR fix(clients/python): consumer warns + acks unhandled event types #121's Python
warn+ackdefault per the maintainer audit onClients: align unknown event type policy across Python, Go, and TypeScript consumers #136 — see the rationale in Clients: align unknown event type policy across Python, Go, and TypeScript consumers #136.
the final batch ack is skipped so PgQ redelivers via the existing
batch lifecycle. Python was already correct via psycopg's
with conn.transaction()rollback.max_messagesraised to 500 in all drivers (matchesdefault
pgque.queue.queue_ticker_max_count).undefinedpayload is coerced to JSON'null'(matches Python'sNonehandling).clients/README.mdparity matrix; per-driver READMEsupdated to describe the new defaults and options.
Per-driver changes
Python (
clients/python/)consumer.py: newunknown_handler: Literal["ack", "nack"] = "nack"param. Default nacks unhandled types with reason
"no handler for type=X"."ack"restores the previous WARNING +ack behaviour.
consumer.py: defaultmax_messages100 → 500.tests/test_consumer.py: replace the warn+ack test with two newtests —
test_consumer_nacks_unhandled_event_type(default; assertsretry-or-DLQ row appears AND next
receivedoes not return the samemsg_id) and
test_consumer_acks_unhandled_event_type_when_opt_in(asserts no retry row, batch advances, warning emitted).
README.md: documents the new default + the>= ticker_max_countrecommendation.
Go (
clients/go/)consumer.go: per-batchnackFailedtracking; ack skipped if anynack errored. Doc comments on
HandlerFunc,Handle,Startupdated to match runtime behaviour (Go client: Consumer docs describe the opposite handler/error semantics #142).
options.go: newWithMaxMessages(n),WithUnknownHandlerPolicy(p)withUnknownHandlerPolicyenum(
NackUnknowndefault,AckUnknownopt-in).pgque.go: defaultmaxMessages = 500.Client.Nacknow acceptsvariadic
NackOptions (WithRetryAfter,WithReason); existingcallers compile unchanged. New
Client.SendBatch1:1 wrapper forpgque.send_batch(text, text, jsonb[]).README.md: examples for the new options andSendBatch.TypeScript (
clients/typescript/)consumer.ts: per-pollnackFailedtracking; newunknownHandlerPolicy?: 'ack' | 'nack'(default'nack').client.ts: sharedencodeJsonbPayloadcoerces top-levelundefinedto JSON'null'forsend(and any futuresendBatch).types.ts: extendsConsumerOptionswithunknownHandlerPolicy,bumps default
maxMessagesto 500.test/: new tests for the ack-policy opt-in and undefined coercion.README.md: documents the new options andundefinedsemantics.Cross-driver parity (
clients/README.md, new)Issues
Closes #134, #135, #136, #137, #142, #149.
References #138 (partial: variadic
NackOptions land here, theSubscribe/Unsubscribe/Ticker/ForceTickGo wrappers staydeferred per maintainer), #144 (docs portion: parity matrix).
Out of scope (deferred to v0.2.1+): #140, #141, #143, #145, #147,
#148, #150, #151.
Test plan
PGQUE_TEST_DSN=... python3 -m pytest clients/python/tests/→ 41 pass, 1 pre-existing baselinefailure (
test_send_batch_none_payload_produces_json_null—missing
conn.commit()betweensend_batchandforce_tick,out of scope here).
PGQUE_TEST_DSN=... go test -race -count=1 ./clients/go/...→ all pass (12.5s).PGQUE_TEST_DSN=... npm testinclients/typescript/→ 24/24 pass;npm run check(tsc--noEmit on src + tests) is clean.
go vet ./clients/go/...clean.Notes
pre-arranged by the maintainer / Clients: align unknown event type policy across Python, Go, and TypeScript consumers #136 audit. The opt-in
unknown_handler="ack"keeps that behaviour available for thefilter-style consumers that motivated fix(clients/python): consumer warns + acks unhandled event types #121.
Nack(ctx, batchID, msg)callers continue to compile(variadic options are appended, not required).
Generated by Claude Code