Skip to content

Fix/per-publish confirm correlation, confirms off by default, anonymous consumer queues - #12

Merged
KARTIKrocks merged 6 commits into
mainfrom
fix/publisher-confirms-per-publish
Jul 1, 2026
Merged

Fix/per-publish confirm correlation, confirms off by default, anonymous consumer queues#12
KARTIKrocks merged 6 commits into
mainfrom
fix/publisher-confirms-per-publish

Conversation

@KARTIKrocks

@KARTIKrocks KARTIKrocks commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Motivation

Fixes #

Changes

Checklist

  • fmt, vet, lint, test, build passes (make all)
  • New code has tests where appropriate
  • Breaking changes are documented

Summary by CodeRabbit

  • New Features

    • Consumers can now use an empty queue name to get a server-assigned private queue, and the resolved name can be retrieved after setup.
    • Publisher confirmation behavior now supports safer concurrent publishing with per-message acknowledgements.
  • Bug Fixes

    • Improved confirmation handling so each published message waits for its own broker response.
    • Server-named queues are now re-created correctly after reconnects.
  • Changed

    • Publisher confirms are now disabled by default and must be enabled explicitly.

…ff; anonymous consumer queues

- Publisher confirms now use a per-publish DeferredConfirmation (delivery-tag
  correlated) instead of a shared NotifyPublish channel. Fixes miscorrelation
  under concurrent publishers, where a call could return on another message's
  ack/nack (silent loss of the confirm guarantee). Batch publishing inherits it.
- DefaultPublisherConfig().ConfirmMode now defaults to false; opt in with
  WithConfirmMode(true, timeout).
- NewConsumer accepts an empty queue name, declaring a private server-named
  (exclusive, auto-delete) queue; new Consumer.QueueName() reports the assigned
  name.

Adds integration coverage for concurrent confirmed publishing and anonymous
queues. Release v0.3.0.
An anonymous consumer (empty queue name) previously overwrote config.Queue
with the generated name in setupChannel, so on reconnect the empty-name branch
was skipped and it never re-declared. Because the queue is exclusive +
auto-delete, the broker drops it when the connection dies, and the consumer
would then Consume from a stale, non-existent queue and silently stop
delivering — contradicting its own godoc.

Track the original empty-ness (serverNamed) and the resolved name (queue)
separately from config.Queue, so setupChannel re-declares a fresh server-named
queue on every setup, including reconnects. Read the resolved name under the
same lock as the channel to keep the consume loop race-free.

Adds an integration test that forces a reconnect and asserts the anonymous
consumer re-declares a new queue and resumes delivery.
- Pin goimports via a GOIMPORTS_VERSION variable (v0.47.0) instead of @latest,
  mirroring GOLANGCI_LINT_VERSION, for reproducible tool installs.
- Bump golangci-lint v2.11.4 -> v2.12.2 (latest) in both the Makefile and
  ci.yml, keeping the two in sync. Lint is clean on the new version.
@KARTIKrocks

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Publisher confirm mode now defaults to disabled and uses per-publish deferred confirmations instead of a shared confirmation channel. Consumer supports empty queue names, resolving them to server-named auto-delete queues exposed via QueueName(), re-declared on reconnect. CI/build tool versions and docs updated accordingly.

Changes

Publisher Confirms and Anonymous Queue Support

Layer / File(s) Summary
Confirm-mode default and per-publish confirmation rewrite
publisher.go, consumer_test.go
DefaultPublisherConfig().ConfirmMode now defaults to false; shared confirmCh/NotifyPublish wiring is removed; PublishToExchange branches on ConfirmMode, using PublishWithDeferredConfirmWithContext and WaitContext with timeout handling, returning ErrNack/ErrTimeout as appropriate.
Server-named/anonymous queue support in Consumer
consumer.go
Consumer gains serverNamed/queue fields; NewConsumer accepts empty Queue; setupChannel declares a server-named queue when needed and stores the resolved name; QueueName() exposes it; the consume loop and log use the resolved queue name.
Integration tests and documentation
integration_test.go, CHANGELOG.md, README.md
New tests cover concurrent confirmed publishing, anonymous queue consumption, and reconnect re-declaration; CHANGELOG adds a 0.3.0 entry and README documents the new confirm-mode default and per-publish correlation behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

CI and Build Tooling

Layer / File(s) Summary
Lint tool version pinning
.github/workflows/ci.yml, Makefile
golangci-lint-action bumped to v2.12.2 in CI and Makefile; goimports install pinned to v0.47.0 instead of @latest.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Publisher
  participant Channel
  participant Broker

  Caller->>Publisher: PublishToExchange(msg)
  alt ConfirmMode disabled
    Publisher->>Channel: PublishWithContext
    Publisher-->>Caller: return nil
  else ConfirmMode enabled
    Publisher->>Channel: PublishWithDeferredConfirmWithContext
    Channel->>Broker: publish
    Broker-->>Channel: ack/nack
    Publisher->>Publisher: WaitContext(timeout)
    alt nack or timeout
      Publisher-->>Caller: ErrNack / ErrTimeout
    else acked
      Publisher-->>Caller: return nil
    end
  end
Loading
sequenceDiagram
  participant Caller
  participant Consumer
  participant Channel
  participant Broker

  Caller->>Consumer: NewConsumer(empty Queue)
  Consumer->>Channel: setupChannel()
  Channel->>Broker: declare server-named queue
  Broker-->>Channel: assigned name
  Channel->>Consumer: store c.queue
  Caller->>Consumer: QueueName()
  Consumer-->>Caller: c.queue
  Consumer->>Channel: Consume(c.queue)
Loading

Related Issues: None referenced in the provided diff.

Related PRs: None referenced in the provided diff.

Suggested labels: breaking-change, enhancement, documentation

Suggested reviewers: Maintainers familiar with publisher.go confirm-mode logic and consumer.go reconnect handling.

Poem

A rabbit hopped with confirms in tow,
Now silent by default, until told "go!"
Queues with no names get one of their own,
Reconnect, re-declare, never alone.
🐇📬 Delivery tags all lined in a row.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is only the template skeleton and lacks the required summary, motivation, changes, and checklist details. Replace the placeholders with a real summary, motivation, key changes, and completed checklist items, including issue links and breaking-change notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main functional changes: per-publish confirms, confirm mode off by default, and anonymous consumer queues.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/publisher-confirms-per-publish

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 2.70270% with 36 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
publisher.go 5.00% 19 Missing ⚠️
consumer.go 0.00% 17 Missing ⚠️

📢 Thoughts on this report? Let us know!

NewConsumer no longer errors on an empty queue name — it declares a private
server-named queue instead — so this test asserting the old "queue is required"
behavior was failing in CI. The empty-queue path is covered by
TestIntegration_AnonymousServerNamedQueue and TestIntegration_AnonymousQueueReconnect.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@integration_test.go`:
- Around line 1814-1820: The reconnect test around consumer.QueueName polling
and the retry gate using time.Sleep/time.After is timing-dependent and flaky.
Replace the sleep-based loop with an explicit synchronization point in the test
flow, using the existing consumer/reconnect path to wait for a deterministic
signal that the consumer has reconnected and started consuming. Update the
affected test logic in integration_test.go (the reconnect/queue-name check and
the retry section) so it no longer relies on broker timing assumptions.
- Around line 1699-1703: The loop that waits on deliveryCh can falsely pass if
the channel closes early because the receive result is ignored. Update the
select branch in this delivery-counting test to check the boolean receive value
from deliveryCh and fail the test immediately if the channel is closed before
got reaches total. Use the surrounding delivery tracking logic in
integration_test.go to locate the select over deliveryCh and ctx.Done(), and
keep the ctx cancellation handling unchanged.

In `@Makefile`:
- Around line 1-2: The setup step is currently allowing preinstalled goimports
and golangci-lint binaries to bypass the pinned versions, so the version
constants may not be enforced. Update the setup logic in Makefile so it always
installs the pinned GOIMPORTS_VERSION and GOLANGCI_LINT_VERSION, or explicitly
verifies the installed binary version before skipping the go install, using the
existing setup targets and those version variables as the entry points.

In `@publisher.go`:
- Around line 255-261: The deferred-confirm wait path in dc.WaitContext
currently returns the raw error, losing operation context for caller-side
cancellation or channel-close failures. Update the error handling around
WaitContext to keep the existing ErrTimeout mapping and any ErrNack behavior,
but wrap all remaining errors with a clear publish/deferred-confirm operation
message before returning; use the nearby WaitContext and acked/err handling in
publisher.go to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 46eb744e-57a2-4e7d-8f87-f46af2de69ae

📥 Commits

Reviewing files that changed from the base of the PR and between 6292b5c and af4cff4.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • Makefile
  • README.md
  • consumer.go
  • consumer_test.go
  • integration_test.go
  • publisher.go

Comment thread integration_test.go
Comment thread integration_test.go
Comment thread Makefile
Comment thread publisher.go Outdated
RabbitMQ 4 deprecated and rejects transient non-exclusive queues
(transient_nonexcl_queues), so integration tests that declared queues with
durable=false and exclusive=false failed with Exception (541) INTERNAL_ERROR.

- Declare the test queues as durable (autoDelete still cleans them up); valid
  on both old and new brokers.
- Bump the broker to rabbitmq:4-management-alpine in both docker-compose.yml
  and the ci.yml services block (they provision RabbitMQ independently) so CI
  exercises the current major version.
- Harden the concurrent-confirm test: check the delivery-channel receive so an
  early channel close fails the test instead of falsely counting toward total.

Verified: full integration suite passes against RabbitMQ 4.3.2.
WaitContext failures other than our own confirm-timeout were returned raw,
losing operation context. Wrap them ("await publish confirmation: %w"), keeping
the ErrTimeout mapping and ErrNack path; %w preserves errors.Is checks against
context.Canceled and context.DeadlineExceeded.
@KARTIKrocks

Copy link
Copy Markdown
Owner Author

@coderabbitai resolve

@KARTIKrocks KARTIKrocks changed the title Fix/publisher confirms per publish Fix/per-publish confirm correlation, confirms off by default, anonymous consumer queues Jul 1, 2026
@KARTIKrocks
KARTIKrocks merged commit 9b00fa6 into main Jul 1, 2026
10 of 11 checks passed
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

@KARTIKrocks
KARTIKrocks deleted the fix/publisher-confirms-per-publish branch July 1, 2026 14:01
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.

2 participants