Skip to content

feat(message-delivery): Add x-outbox-id header to kafka and http transport - #115

Open
rawo wants to merge 1 commit into
mainfrom
feat/kojak-78
Open

rawo wants to merge 1 commit into
mainfrom
feat/kojak-78

Conversation

@rawo

@rawo rawo commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Adds an x-outbox-id header carrying the outbox entry's UUID to every delivery, giving consumers a stable key to deduplicate redeliveries on. Part 1 of KOJAK-78 only — the shutdown-timeout change is not in this PR.

Changes

  • OutboxHeaders.OUTBOX_ID in okapi-core, so the name lives outside any transport module and consumers can reference it without depending on okapi-kafka / okapi-http.
  • Set by both KafkaMessageDeliverer and HttpMessageDeliverer, in buildRecord/buildRequest — so single and batch paths both carry it. Value is outboxId.raw.toString(), stable across retries of an entry.
  • Written last, so okapi's value wins over a same-named DeliveryInfo header: HTTP setHeader replaces it outright; Kafka headers are multi-valued, so the caller's stays in the record and okapi's is what lastHeader(...) returns.
  • README: new "Deduplicating on x-outbox-id" section with Kafka/HTTP recipes and both caveats — the override behaviour, and that this does not deduplicate at publish() level (two publish() calls = two entries = two ids). Also rewrites the "Duplicate delivery is possible" bullet, which previously stated okapi does not send the OutboxId — no longer true.

Note: the ticket's snippet used entry.outboxId.value; the value class field is raw.

Tests (+7)

  • Kafka unit: header on deliver; on every record in deliverBatch with each carrying its own id; collision case asserting lastHeader returns okapi's and the caller's value survives in headers(...).
  • HTTP unit (WireMock): header present; caller-supplied value replaced.
  • Kafka integration: real consumer reads it back off the broker matching the entry UUID, caller headers intact; redelivering the same entry twice yields the same id.

Test plan

  • ./gradlew ktlintCheck — clean
  • ./gradlew test — 409 tests, 0 failures (Docker up; Testcontainers Kafka/Postgres/MySQL included)

#KOJAK-78

…sport for handling deduplications on client side

#KOJAK-78
@rawo
rawo requested review from endrju19 and ramafasa and a lite review from Copilot September 25, 2026 13:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Documentation should clarify built-in transport scope and safe deduplication handling.

Review effort: Lite
Findings: None

What changed in this PR

Adds stable x-outbox-id headers to Kafka and HTTP deliveries for consumer-side deduplication.

Changes:

  • Adds the shared OutboxHeaders.OUTBOX_ID constant.
  • Injects UUIDs into Kafka and HTTP deliveries, including batches.
  • Adds documentation, changelog, unit tests, and integration coverage.
File Summary
README.md Documents header usage and deduplication
okapi-kafka/​src/​test/​kotlin/​com/​softwaremill/​okapi/​kafka/​KafkaMessageDelivererTest.kt Tests Kafka header behavior
okapi-kafka/​src/​main/​kotlin/​com/​softwaremill/​okapi/​kafka/​KafkaMessageDeliverer.kt Adds headers to Kafka records
okapi-integration-tests/​src/​test/​kotlin/​com/​softwaremill/​okapi/​test/​transport/​KafkaTransportIntegrationTest.kt Verifies broker delivery and retry stability
okapi-http/​src/​test/​kotlin/​com/​softwaremill/​okapi/​http/​HttpMessageDelivererTest.kt Tests HTTP header behavior
okapi-http/​src/​main/​kotlin/​com/​softwaremill/​okapi/​http/​HttpMessageDeliverer.kt Adds headers to HTTP requests
okapi-core/​src/​main/​kotlin/​com/​softwaremill/​okapi/​core/​OutboxHeaders.kt Defines the shared header constant
CHANGELOG.md Records the new delivery header

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@endrju19 endrju19 self-assigned this Sep 26, 2026

This branch has not been deployed

No deployments
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.

4 participants