Skip to content

Fix concurrency/correctness bugs and harden public API for v0.2.0 - #9

Merged
KARTIKrocks merged 4 commits into
mainfrom
release/v0.2.0
Jun 13, 2026
Merged

Fix concurrency/correctness bugs and harden public API for v0.2.0#9
KARTIKrocks merged 4 commits into
mainfrom
release/v0.2.0

Conversation

@KARTIKrocks

@KARTIKrocks KARTIKrocks commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Addresses findings from a full-repo CodeRabbit review (each verified against the code and covered by tests).

Bug fixes:

  • RetryMiddleware: a negative retry count no longer skips the handler entirely (previously the message was acked without ever being processed).
  • Publisher.NotifyReturn: registers on the active channel immediately instead of only taking effect after the next reconnection.
  • Config.ConnectionTimeout: now honored when dialing (wired via amqp.DefaultDial); it was previously ignored.
  • Message.WithHeader/WithHeaders: initialize a nil Headers map instead of panicking on a &Message{} literal.
  • Consumer.Start: track all cancel funcs so repeated calls no longer leak the previous consume goroutine.
  • Config.connectionURL: percent-encode username/password/vhost via net/url so credentials containing reserved characters produce a valid AMQP URI.
  • BatchPublisher.PublishAndClear: atomically swap out the pending messages so concurrent Add* calls are never cleared without being published; re-queue the unpublished remainder on partial failure.

API hardening (new typed errors, backward compatible):

  • Add ErrNilConnection and ErrNilMessage.
  • NewConsumer, NewPublisher, PublishToExchange, PublishDelayed return typed errors on nil arguments instead of panicking.
  • LoggingMiddleware falls back to a no-op logger when passed nil.
  • Guard against sync.WaitGroup misuse during concurrent Consume/Close.

Tooling/docs:

  • Pin golangci-lint to v2.11.4 in both Makefile and CI; drop redundant gofmt from the fmt target.
  • Document integration-test command, new sentinel errors, and fix the stale make check reference (now make ci).
  • CHANGELOG: add the 0.2.0 section.

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

    • Added sentinel errors to validate nil arguments.
  • Bug Fixes

    • Fixed AMQP URI percent-encoding and connection timeout handling.
    • Prevented goroutine leaks, nil-map header panics, and stacked return handlers.
    • Preserved FIFO semantics during concurrent batch publishing.
    • Added nil-logger fallback and made retry behavior robust for negative values.
  • Changed

    • Nil-argument cases now return typed errors; lint/format tooling updated.
  • Tests

    • Added unit and integration tests covering edge cases and concurrency.
  • Documentation

    • Updated README and contributing guidance for integration testing.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 43c5e0cb-80d8-4451-b488-432031de55da

📥 Commits

Reviewing files that changed from the base of the PR and between 388d8d7 and 381a868.

📒 Files selected for processing (1)
  • CHANGELOG.md

Walkthrough

v0.2.0 introduces sentinel nil errors, percent-encoded AMQP URL construction, honoring ConnectionTimeout for dials, publisher nil checks and return-handler refactor, atomic BatchPublisher draining with requeue on failure, consumer multi-start cancellation tracking, nil-safe message headers and middleware guards, plus tests and docs/tooling updates.

Changes

RabbitMQ Wrapper v0.2.0 Release

Layer / File(s) Summary
Sentinel errors and connection URL/dial behavior
rabbitmq.go, config_test.go
Adds ErrNilConnection and ErrNilMessage; rebuilds connectionURL() using net/url + net.JoinHostPort; applies ConnectionTimeout through amqp.DefaultDial; adds URI round-trip tests covering percent-encoding and scheme behavior.
Publisher validation, return wiring, and atomic batch
publisher.go
NewPublisher returns ErrNilConnection for nil conn. PublishToExchange/PublishDelayed return ErrNilMessage for nil messages. setupChannel starts a single per-channel return listener that forwards to the latest handler; NotifyReturn updates the handler without stacking. BatchPublisher.PublishAndClear atomically drains, publishes in order, and re-queues remaining unpublished messages ahead of concurrent adds on failure.
Constructor nil-guard tests
consumer_test.go
Adds tests asserting NewConsumer(nil, ...) and NewPublisher(nil, ...) return ErrNilConnection (checked with errors.Is).
Consumer lifecycle cancellation and handler coordination
consumer.go
Consumer now stores multiple cancel funcs (cancelFns) for repeated Start calls; Stop and CloseWithContext cancel and clear all stored funcs. Consume registers handler goroutines with handlerWg under mu to avoid goroutine leaks. Minor QueueInfo comment formatting.
Message and middleware nil/edge-case handling
message.go, message_test.go, middleware.go, middleware_test.go
WithHeader/WithHeaders initialize Headers when nil. LoggingMiddleware uses a nopLogger fallback for nil logger. RetryMiddleware clamps negative retries to zero. Unit tests added for nil header map, nil logger, and negative retries.
Integration tests for batch and returns
integration_test.go
Adds TestIntegration_BatchPublishAndClearConcurrent stress test and TestIntegration_PublisherNotifyReturn to validate batch concurrency behavior and NotifyReturn handler replacement with actual broker returns.
Release documentation and development tooling updates
CHANGELOG.md, README.md, CONTRIBUTING.md, Makefile, .github/workflows/ci.yml
Adds 0.2.0 changelog section, updates README examples and sentinel-error docs, documents integration-test tag/runtime requirements, bumps golangci-lint to v2.11.4 in Makefile/CI, and updates fmt target to run goimports only.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Config
  participant Connection
  participant Publisher
  participant BatchPublisher
  participant Broker
  participant Consumer

  Client->>Config: build Config
  Config->>Connection: connectionURL()
  Connection->>Broker: amqp.Dial (uses amqp.DefaultDial(ConnectionTimeout))
  Client->>Publisher: NewPublisher(conn)
  alt conn is nil
    Publisher-->>Client: ErrNilConnection
  else
    Publisher-->>Client: publisher
  end

  Client->>Publisher: Publish(msg)
  Publisher->>Publisher: validate msg != nil
  alt msg is nil
    Publisher-->>Client: ErrNilMessage
  else
    Publisher->>Broker: publish
    Broker-->>Publisher: ack or return
    Publisher->>Publisher: return listener forwards to latest onReturn handler
  end

  Client->>BatchPublisher: Add(msg)
  Client->>BatchPublisher: PublishAndClear
  BatchPublisher->>BatchPublisher: drain queue under lock
  BatchPublisher->>Broker: publish drained messages
  alt publish error
    BatchPublisher->>BatchPublisher: requeue remaining ahead of new adds
  end

  Client->>Consumer: Start
  Consumer->>Consumer: append cancel func to cancelFns
  Client->>Consumer: Stop
  Consumer->>Consumer: iterate cancelFns and cancel
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

Nil checks guard the rabbit's lair,
URLs now safely percent-encoded there.
Batches drain, then queue again,
Consumers stop without loose pain.
Tests and docs hum — steady, fair.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary changes: bug fixes for concurrency/correctness and API hardening for the v0.2.0 release, which aligns with the raw_summary and pr_objectives.
Description check ✅ Passed The description is comprehensive and addresses the template structure. It lists bug fixes, API hardening, and tooling/docs changes with clear organization, though the template sections (Summary, Motivation, Changes) are not explicitly filled as separate headers.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.2.0

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

@codecov-commenter

codecov-commenter commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 29.50820% with 43 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
publisher.go 6.66% 28 Missing ⚠️
consumer.go 13.33% 13 Missing ⚠️
rabbitmq.go 75.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Addresses findings from a full-repo review (each verified against the code and
covered by tests).

Bug fixes:
- RetryMiddleware: a negative retry count no longer skips the handler entirely
  (previously the message was acked without ever being processed).
- Publisher.NotifyReturn: registers on the active channel immediately instead of
  only taking effect after the next reconnection.
- Config.ConnectionTimeout: now honored when dialing (wired via amqp.DefaultDial);
  it was previously ignored.
- Message.WithHeader/WithHeaders: initialize a nil Headers map instead of
  panicking on a &Message{} literal.
- Consumer.Start: track all cancel funcs so repeated calls no longer leak the
  previous consume goroutine.
- Config.connectionURL: percent-encode username/password/vhost via net/url so
  credentials containing reserved characters produce a valid AMQP URI.
- BatchPublisher.PublishAndClear: atomically swap out the pending messages so
  concurrent Add* calls are never cleared without being published; re-queue the
  unpublished remainder on partial failure.

API hardening (new typed errors, backward compatible):
- Add ErrNilConnection and ErrNilMessage.
- NewConsumer, NewPublisher, PublishToExchange, PublishDelayed return typed
  errors on nil arguments instead of panicking.
- LoggingMiddleware falls back to a no-op logger when passed nil.
- Guard against sync.WaitGroup misuse during concurrent Consume/Close.

Tooling/docs:
- Pin golangci-lint to v2.11.4 in both Makefile and CI; drop redundant gofmt
  from the fmt target.
- Document integration-test command, new sentinel errors, and fix the stale
  `make check` reference (now `make ci`).
- CHANGELOG: add the 0.2.0 section.
@KARTIKrocks

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 13, 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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Makefile (1)

42-45: ⚠️ Potential issue | 🟡 Minor

Make fmt depend on setup.

goimports is installed by setup, but fmt runs goimports -w . and all can invoke fmt before lint (which is the only target that depends on setup), so make fmt / make all can fail on a fresh clone. Wire fmt: setup (or fall back to gofmt if goimports isn’t available).

♻️ Proposed fix
-fmt:
+fmt: setup
 	goimports -w .
🤖 Prompt for 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.

In `@Makefile` around lines 42 - 45, The Makefile's fmt target can run before
setup installs goimports; modify the fmt target (named "fmt") to depend on the
"setup" target (i.e., change its rule header to "fmt: setup") so goimports is
available, and as an alternative add a fallback in the fmt recipe that uses
"command -v goimports >/dev/null 2>&1 && goimports -w . || gofmt -w ." so
formatting still works if goimports is missing.
🤖 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 421-427: The loop using time.Sleep in the test makes timing flaky;
replace it with synchronization so the producer and the flusher overlap
deterministically: run the producer that calls batch.Add(NewTextMessage(...)) in
a goroutine and coordinate with the code path that triggers PublishAndClear by
using channels/WaitGroup signals (e.g., send a "started flush" signal from the
flusher or wait for a "ready" signal before continuing adds) so you guarantee
interleaving without sleeps; update the test to wait for completion of both
producer and flusher goroutines and remove the time.Sleep(200 *
time.Microsecond) calls so the test is deterministic and race-free.

In `@message_test.go`:
- Around line 182-192: The test TestWithHeaderNilMap currently seeds the Headers
map via WithHeader before exercising WithHeaders, so update or add a test case
that constructs m := &Message{} with Headers nil and then calls
m.WithHeaders(map[string]any{"k2":"v2"}) (or call WithHeaders on a fresh Message
without first calling WithHeader) and assert that m.Headers["k2"] == "v2"; keep
the existing assertions for WithHeader behavior in a separate test or add a
second sub-case that first calls WithHeader then WithHeaders to cover both
paths, verifying that WithHeaders correctly initializes a nil Message.Headers
map and merges entries when map already exists (referencing
TestWithHeaderNilMap, WithHeaders, WithHeader, Message, and Headers).

In `@README.md`:
- Around line 182-187: The snippet creates a publisher with
rabbitmq.NewPublisher (assigned to publisher) but never closes it; after
checking err from NewPublisher, ensure you close the publisher to avoid leaking
the confirm channel by calling publisher.Close() (or publisher.Close(ctx) if the
API requires a context) — place this as a deferred call immediately after
successful creation (e.g., defer publisher.Close() / defer publisher.Close(ctx))
so the publisher is always closed even if publisher.Publish(ctx, msg) returns an
error.

---

Outside diff comments:
In `@Makefile`:
- Around line 42-45: The Makefile's fmt target can run before setup installs
goimports; modify the fmt target (named "fmt") to depend on the "setup" target
(i.e., change its rule header to "fmt: setup") so goimports is available, and as
an alternative add a fallback in the fmt recipe that uses "command -v goimports
>/dev/null 2>&1 && goimports -w . || gofmt -w ." so formatting still works if
goimports is missing.
🪄 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: 0171a5d6-510d-443d-b40b-a25871689d4b

📥 Commits

Reviewing files that changed from the base of the PR and between 53977bf and 4159d17.

📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Makefile
  • README.md
  • config_test.go
  • consumer.go
  • consumer_test.go
  • integration_test.go
  • message.go
  • message_test.go
  • middleware.go
  • middleware_test.go
  • publisher.go
  • rabbitmq.go

Comment thread integration_test.go
Comment on lines +421 to +427
// Pace the adds so they interleave with the publish window of
// PublishAndClear; without pacing the producer finishes before the
// first flush and the race is never exercised.
for i := 0; i < total; i++ {
batch.Add(NewTextMessage(fmt.Sprintf("msg-%d", i)))
time.Sleep(200 * time.Microsecond)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Replace sleep-based pacing with synchronization-driven overlap

Using time.Sleep(200 * time.Microsecond) to force interleaving makes this integration test timing-sensitive and flaky across environments. Prefer synchronization primitives (e.g., producer/flusher goroutines coordinated via channels + completion signals) to create overlap without fixed sleeps.

As per coding guidelines, **/*_test.go: “Verify tests are deterministic and free of races, sleeps, and flaky timing assumptions.”

🤖 Prompt for 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.

In `@integration_test.go` around lines 421 - 427, The loop using time.Sleep in the
test makes timing flaky; replace it with synchronization so the producer and the
flusher overlap deterministically: run the producer that calls
batch.Add(NewTextMessage(...)) in a goroutine and coordinate with the code path
that triggers PublishAndClear by using channels/WaitGroup signals (e.g., send a
"started flush" signal from the flusher or wait for a "ready" signal before
continuing adds) so you guarantee interleaving without sleeps; update the test
to wait for completion of both producer and flusher goroutines and remove the
time.Sleep(200 * time.Microsecond) calls so the test is deterministic and
race-free.

Source: Coding guidelines

Comment thread message_test.go
Comment thread README.md

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rabbitmq.go (1)

19-32: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document ErrNilConnection and ErrNilMessage individually.

These are new exported API symbols, but the var-block comment does not explain either identifier in godoc. Add per-symbol comments so downstream users see their exact meaning in package docs.

As per coding guidelines, **/*.go: “Require godoc comments on all exported identifiers.”

🤖 Prompt for 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.

In `@rabbitmq.go` around lines 19 - 32, Add godoc comments for the two new
exported sentinels so their meanings appear in package docs: place a short
comment immediately above ErrNilConnection (for example: "ErrNilConnection is
returned when a function expecting an active *amqp.Connection was given a nil
connection") and another immediately above ErrNilMessage (for example:
"ErrNilMessage is returned when a publish/consume function was passed a nil
message or delivery"), keeping them as part of the existing var block and using
the exact exported names ErrNilConnection and ErrNilMessage.

Source: Coding guidelines

🤖 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 `@config_test.go`:
- Around line 154-200: Add a test case exercising reserved characters in VHost
so TestConnectionURLEncoding also validates vhost path-escaping: add a table
entry (same slice used by TestConnectionURLEncoding) named like "reserved
characters in vhost" that uses Config{Host:"myhost", Port:5672, Username:"user",
Password:"pass", VHost:"/p@th:with?=chars"} and set the expected fields
accordingly (wantUser "user", wantPass "pass", wantHost "myhost", wantPort 5672,
wantVhost "p@th:with?=chars") so the test asserts that the code handling Config
-> connection URL properly escapes/decodes VHost; reference the Config struct
and the TestConnectionURLEncoding table to locate where to insert this case.

In `@integration_test.go`:
- Around line 455-471: Replace the current "received" counter loop (variables
received, deliveryCh, idle, ctx, total) with logic that reads each delivery from
deliveryCh, extracts the message ID from the message body (the "msg-%d" payload
referenced in the test), and records IDs in a map/set (e.g., map[int]bool); on
each delivery check if the ID is a duplicate and fail the test immediately if
so, otherwise mark it seen; continue until the set size == total, then verify
that all IDs 1..total are present and fail if any are missing; preserve the idle
timer/context timeout behavior but update failure messages to report seen IDs
and duplicates/missing IDs for clearer assertions.

In `@publisher.go`:
- Around line 155-186: The current approach creates a new amqp Return listener
goroutine every time Publisher.NotifyReturn is called, causing stacked
listeners; fix by making the channel-level listener single and have it dispatch
to the current Publisher handler. Concretely: change registerReturnHandler so it
no longer creates a listener per call but instead ensures one long-lived
listener is started once for a given Channel (e.g., add a boolean/flag or
listener field on type Channel and guard it with Channel's mutex), have that
single goroutine read from the amqp returnCh and, for each received amqp.Return,
acquire Publisher.onReturnMu and call the latest p.onReturn (or drop if nil);
update Publisher.NotifyReturn to only set p.onReturn under onReturnMu and start
the channel-level listener only if it hasn’t already been started (do not spawn
a new goroutine on each NotifyReturn), ensuring old goroutines are not created
and returned messages are dispatched to the latest handler.

---

Outside diff comments:
In `@rabbitmq.go`:
- Around line 19-32: Add godoc comments for the two new exported sentinels so
their meanings appear in package docs: place a short comment immediately above
ErrNilConnection (for example: "ErrNilConnection is returned when a function
expecting an active *amqp.Connection was given a nil connection") and another
immediately above ErrNilMessage (for example: "ErrNilMessage is returned when a
publish/consume function was passed a nil message or delivery"), keeping them as
part of the existing var block and using the exact exported names
ErrNilConnection and ErrNilMessage.
🪄 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: c45c88de-2975-4ef1-bd94-16d52e4178c8

📥 Commits

Reviewing files that changed from the base of the PR and between 4159d17 and 03498f2.

📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Makefile
  • README.md
  • config_test.go
  • consumer.go
  • consumer_test.go
  • integration_test.go
  • message.go
  • message_test.go
  • middleware.go
  • middleware_test.go
  • publisher.go
  • rabbitmq.go

Comment thread config_test.go
Comment thread integration_test.go Outdated
Comment thread publisher.go Outdated
- message_test: split TestWithHeaderNilMap into two sub-cases, each starting
  from a fresh &Message{}, so WithHeaders exercises its own nil-map
  initialization path (previously WithHeader seeded the map first).
- README: defer publisher.Close() in the Publisher Confirms example so it does
  not leak the confirm channel.
- Makefile: make the fmt target depend on setup so goimports is installed before
  it runs (consistent with lint: setup).

Skipped: the time.Sleep pacing in TestIntegration_BatchPublishAndClearConcurrent
is intentional. The race window is inside PublishAndClear's broker round-trip,
which boundary-level channel/WaitGroup handshakes cannot deterministically
reach; removing the pacing makes the producer finish before flushing starts and
stops exercising the race. The pass/fail assertion is deterministic for the
fixed code (no message can be lost), so the test is not flaky.
@KARTIKrocks

Copy link
Copy Markdown
Owner Author

@coderabbitai can you check the state of the comments with updated codebase, and also check the last commit.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Open/Unresolved Review Threads on PR `#9` ==="
gh pr view 9 --repo KARTIKrocks/rabbitwrap --json reviews,reviewDecision,state

echo ""
echo "=== Review Comments (inline) ==="
gh api repos/KARTIKrocks/rabbitwrap/pulls/9/comments \
  --jq '.[] | {id: .id, path: .path, line: .line, body: .body[:200], created_at: .created_at, updated_at: .updated_at}'

echo ""
echo "=== Issue Comments on PR `#9` ==="
gh api repos/KARTIKrocks/rabbitwrap/issues/9/comments \
  --jq '.[] | {id: .id, user: .user.login, body: .body[:300], created_at: .created_at}'

echo ""
echo "=== Last 3 Commits on PR Branch ==="
gh pr view 9 --repo KARTIKrocks/rabbitwrap --json commits \
  --jq '.commits[-3:] | .[] | {sha: .oid, message: .messageHeadline, authoredDate: .authoredDate, author: .authors[0].name}'

Length of output: 17852


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Diff of last commit abaf63065a2911b7b8824277a6a40b2ba059953e ==="
gh api repos/KARTIKrocks/rabbitwrap/commits/abaf63065a2911b7b8824277a6a40b2ba059953e \
  --jq '.files[] | "FILE: \(.filename)\nSTATUS: \(.status)\nADDITIONS: \(.additions)\nDELETIONS: \(.deletions)\nPATCH:\n\(.patch // "(binary or no patch)")\n---"'

Length of output: 2404



@KARTIKrocks: Here's the status after the last commit (abaf630 — "Address PR review comments"):


✅ Addressed in last commit

File Comment
Makefile fmt: setup dependency wired — goimports now installed before formatting
README.md defer publisher.Close() added to the Publisher Confirms snippet
message_test.go Split into two independent sub-cases; WithHeaders now exercises nil-map init on a fresh &Message{}

🔴 Still open

File Line Severity Issue
publisher.go 186 Major registerReturnHandler spawns a new goroutine every time NotifyReturn is called — stacked listeners accumulate. Needs a single long-lived dispatcher per channel that forwards to the current p.onReturn handler under a mutex.
rabbitmq.go 19–32 Major ErrNilConnection and ErrNilMessage have no godoc comments — exported symbols need per-symbol doc per your coding guidelines.
config_test.go 200 Minor TestConnectionURLEncoding has no test case with reserved chars in VHost — only username/password are stress-tested.
integration_test.go 471 Minor Delivery loop only counts; a duplicate can mask a dropped message. Tracking seen IDs in a map[int]bool would prove exactly-once.
integration_test.go 427 Minor time.Sleep pacing — you noted in the PR this is intentional to keep the race window open across broker round-trips, which is a valid call. I'll leave this to your judgment.

The publisher.go stacked-listener issue is the most impactful one to address before merging.

- publisher: NotifyReturn no longer spawns a listener per call, which stacked
  goroutines and left replaced handlers firing. setupChannel now starts a single
  per-channel return listener that dispatches to the current handler; NotifyReturn
  just swaps the handler. Behavior (immediate effect, persists across reconnects)
  is preserved. Covered by a new integration test that also asserts no stacking.
- rabbitmq: add godoc for the new ErrNilConnection and ErrNilMessage sentinels.
- config_test: add a TestConnectionURLEncoding case with reserved characters in
  the vhost to cover vhost path-escaping.
- integration_test: track message IDs in the batch concurrency test to detect
  duplicates and missing IDs, not just a raw count.
@KARTIKrocks

Copy link
Copy Markdown
Owner Author

@coderabbitai, check again.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Last 5 Commits on PR `#9` ==="
gh pr view 9 --repo KARTIKrocks/rabbitwrap --json commits \
  --jq '.commits[-5:] | .[] | {sha: .oid, message: .messageHeadline, authoredDate: .authoredDate}'

echo ""
echo "=== Latest commit diff ==="
LAST_SHA=$(gh pr view 9 --repo KARTIKrocks/rabbitwrap --json commits --jq '.commits[-1].oid')
echo "Last SHA: $LAST_SHA"
gh api repos/KARTIKrocks/rabbitwrap/commits/$LAST_SHA \
  --jq '.files[] | "FILE: \(.filename)\nSTATUS: \(.status)\nPATCH:\n\(.patch // "(none)")\n---"'

Length of output: 8325


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== publisher.go: NotifyReturn and registerReturnHandler ==="
cat publisher.go | grep -n -A 30 "registerReturnHandler\|NotifyReturn"

echo ""
echo "=== rabbitmq.go: ErrNil exports ==="
grep -n -B2 -A2 "ErrNil" rabbitmq.go

echo ""
echo "=== config_test.go: VHost test cases ==="
grep -n "VHost\|vhost\|Vhost" config_test.go

echo ""
echo "=== integration_test.go: delivery loop ==="
grep -n -A 5 "seen\|map\[int\]\|Duplicate\|duplicate\|Body\|msgID" integration_test.go | tail -60

Length of output: 5778



@KARTIKrocks: Everything is addressed in commit 388d8d7. Here's the final status:

# File Previous concern Status
1 publisher.go registerReturnHandler stacked a new goroutine on every NotifyReturn call ✅ Fixed — single startReturnListener per channel; handler read at delivery time under RLock; NotifyReturn only swaps the pointer. New TestIntegration_PublisherNotifyReturn integration test validates no stacking and that a replaced handler never fires.
2 rabbitmq.go ErrNilConnection/ErrNilMessage missing godoc ✅ Fixed — both have // Err... is returned by... comments.
3 config_test.go No reserved-char test for VHost ✅ Fixed — "reserved characters in vhost" case with /p@th:with?=chars.
4 integration_test.go Plain counter couldn't detect a drop+duplicate cancellation ✅ Fixed — seen map[int]bool with duplicate detection and post-loop per-ID completeness check.

No open issues remain. The PR is good to merge. 🐇

@KARTIKrocks
KARTIKrocks merged commit 852a201 into main Jun 13, 2026
10 of 11 checks passed
@KARTIKrocks
KARTIKrocks deleted the release/v0.2.0 branch June 13, 2026 14:51
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