Skip to content

fix(clients): v0.2.0 data-safety + consumer alignment#156

Closed
NikolayS wants to merge 4 commits intomainfrom
fix/clients-v0.2.0-data-safety
Closed

fix(clients): v0.2.0 data-safety + consumer alignment#156
NikolayS wants to merge 4 commits intomainfrom
fix/clients-v0.2.0-data-safety

Conversation

@NikolayS
Copy link
Copy Markdown
Owner

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_messages matches the default
queue_ticker_max_count so a single Receive can drain a batch.

Per-driver changes

Python (clients/python/)

  • consumer.py: new unknown_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: default max_messages 100 → 500.
  • tests/test_consumer.py: replace the warn+ack test with two new
    tests — test_consumer_nacks_unhandled_event_type (default; asserts
    retry-or-DLQ row appears AND next receive does not return the same
    msg_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_count
    recommendation.

Go (clients/go/)

  • consumer.go: per-batch nackFailed tracking; ack skipped if any
    nack errored. Doc comments on HandlerFunc, Handle, Start
    updated to match runtime behaviour (Go client: Consumer docs describe the opposite handler/error semantics #142).
  • options.go: new WithMaxMessages(n),
    WithUnknownHandlerPolicy(p) with UnknownHandlerPolicy enum
    (NackUnknown default, AckUnknown opt-in).
  • pgque.go: default maxMessages = 500. Client.Nack now accepts
    variadic NackOptions (WithRetryAfter, WithReason); existing
    callers compile unchanged. New Client.SendBatch 1:1 wrapper for
    pgque.send_batch(text, text, jsonb[]).
  • README.md: examples for the new options and SendBatch.

TypeScript (clients/typescript/)

  • consumer.ts: per-poll nackFailed tracking; new
    unknownHandlerPolicy?: 'ack' | 'nack' (default 'nack').
  • client.ts: shared encodeJsonbPayload coerces top-level
    undefined to JSON 'null' for send (and any future
    sendBatch).
  • types.ts: extends ConsumerOptions with unknownHandlerPolicy,
    bumps default maxMessages to 500.
  • test/: new tests for the ack-policy opt-in and undefined coercion.
  • README.md: documents the new options and undefined semantics.

Cross-driver parity (clients/README.md, new)

  • Capability matrix per driver
  • Locked-in nack-default contract
  • List of v0.2.1+ deferred items

Issues

Closes #134, #135, #136, #137, #142, #149.
References #138 (partial: variadic NackOptions land here, the
Subscribe/Unsubscribe/Ticker/ForceTick Go wrappers stay
deferred 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

  • Python: PGQUE_TEST_DSN=... python3 -m pytest clients/python/tests/ → 41 pass, 1 pre-existing baseline
    failure (test_send_batch_none_payload_produces_json_null
    missing conn.commit() between send_batch and force_tick,
    out of scope here).
  • Go: PGQUE_TEST_DSN=... go test -race -count=1 ./clients/go/... → all pass (12.5s).
  • TypeScript: PGQUE_TEST_DSN=... npm test in
    clients/typescript/ → 24/24 pass; npm run check (tsc
    --noEmit on src + tests) is clean.
  • go vet ./clients/go/... clean.

Notes


Generated by Claude Code

claude added 4 commits April 30, 2026 17:59
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+.
Copy link
Copy Markdown
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clients: high-level consumers can skip unreturned batch rows when max_messages < batch size

2 participants