Skip to content

Releases: leonlee/outbox

v0.9.2

30 Mar 08:19

Choose a tag to compare

v0.9.1

03 Mar 05:32

Choose a tag to compare

Bug Fixes

  • Timestamp truncation in OutboxPoller: Instant.now() is now truncated to millisecond precision before passing to pollPending/claimPending, preventing mismatches with millis-precision stored timestamps
  • Longest-prefix matching in JdbcOutboxStores: detect() now picks the longest matching JDBC URL prefix instead of the first match, ensuring the most specific store wins
  • INTEGRATIONS.md schema: Fixed incorrect column list and status enum to match actual outbox_event table

Robustness Improvements

  • Deterministic ORDER BY: Added event_id tie-breaker to all 10 ORDER BY created_at SQL clauses across stores and purgers, ensuring consistent ordering for events sharing the same millisecond timestamp
  • MySqlOutboxStore claim locking: Rewrote claimPending() to use SELECT ... FOR UPDATE SKIP LOCKED for atomic row-level locking (MySQL 8.0+), replacing the non-atomic UPDATE...ORDER BY...LIMIT approach
  • MySqlOutboxStore IN clause chunking: Phase 2 UPDATE now chunks the IN (...) clause in batches of 500 to stay within MySQL parameter limits
  • Index-friendly purge SQL: Replaced COALESCE(done_at, created_at) with an OR pattern (done_at < ? OR (done_at IS NULL AND created_at < ?)) that allows the database to use indexes on done_at and created_at

Dependencies

  • central-publishing-maven-plugin 0.6.0 → 0.10.0
  • ulid-creator 5.2.3 → 5.2.4

Full Changelog: v0.9.0...v0.9.1

v0.9.0

02 Mar 05:32

Choose a tag to compare

Full Changelog: v0.8.4...v0.9.0

v0.8.4

27 Feb 10:44

Choose a tag to compare

What's Changed

Bug Fixes

  • withConnection catches RuntimeExceptionOutboxDispatcher.withConnection() now catches SQLException | RuntimeException instead of just SQLException. Previously, a RuntimeException from a store method (e.g. OutboxStoreException) could propagate into dispatchEvent's catch block, causing handleFailure to run on a successfully-processed event.
  • OutboxPoller.markDead catches RuntimeException — Same pattern fix as above, broadened catch to SQLException | RuntimeException.
  • WriterOnlyBuilder.build() passes metrics — Previously passed null for metrics to the Outbox constructor, so Outbox.close() never called metrics.close() — leaking Micrometer meters. Auto-config now also wires metrics in WRITER_ONLY mode.
  • convertToEnvelope reconstructs availableAt — Added available_at to all SELECT queries (pollPending, selectClaimed, queryDead, PostgreSQL RETURNING), added availableAt field to OutboxEvent record, and set it on the reconstructed EventEnvelope.
  • DefaultJsonCodec validates lone surrogates — Added surrogate pair validation: high surrogates must be followed by \uDC00-\uDFFF, lone low surrogates are rejected.
  • DeadEventManager propagates errors — Changed from swallowing exceptions (returning List.of() / 0 / false) to wrapping SQLException in RuntimeException. Callers can now distinguish database failures from empty results.

Improvements

  • WriterOnlyBuilder rejects irrelevant config — 8 methods (listenerRegistry, jsonCodec, interceptor, interceptors, intervalMs, batchSize, skipRecent, drainTimeoutMs) now throw UnsupportedOperationException instead of silently accepting values that are ignored at build time. metrics() is intentionally kept for lifecycle management.
  • Added spring-boot-configuration-processor — The starter module now generates spring-configuration-metadata.json for IDE auto-completion of outbox.* properties.

Documentation

  • Added CODE_REVIEW.md with full code review findings and verified-correct areas.

Full Changelog: v0.8.3...v0.8.4

v0.8.3

27 Feb 08:15

Choose a tag to compare

Full Changelog: v0.8.2...v0.8.3

v0.8.2

27 Feb 05:28

Choose a tag to compare

Handler-Controlled Retry Timing

This release adds two complementary mechanisms for handlers to control retry timing, giving listeners fine-grained control over when events are re-delivered.

DispatchResult (sealed interface)

EventListener gains a new handleEvent() default method that returns a DispatchResult:

  • DispatchResult.done() — event processed successfully (same as before)
  • DispatchResult.retryAfter(Duration) — defer re-delivery without counting against maxAttempts. The event is reset to PENDING with a future available_at. Useful for polling external systems or respecting rate-limit headers.

Existing onEvent() listeners continue to work unchanged — the default handleEvent() delegates to onEvent() and returns Done.

RetryAfterException

Throw RetryAfterException when a transient failure occurs and the handler knows the appropriate retry delay (e.g., from an HTTP Retry-After header). Unlike DispatchResult.RetryAfter, this does count against maxAttempts.

Comparison

Mechanism Counts against maxAttempts Delay source Use case
DispatchResult.RetryAfter No Handler-specified Polling, waiting for preconditions
RetryAfterException Yes Handler-specified Transient failure with known retry delay
Other exception Yes RetryPolicy Unexpected failure

Other Changes

  • OutboxStore.markDeferred() — new SPI method for deferred retry without incrementing attempts
  • MetricsExporter.incrementDispatchDeferred() — new metric for tracking deferred events
  • Documentation updated across SPEC.md, TUTORIAL.md, and README.md

Full Changelog: v0.8.1...v0.8.2

v0.8.1

21 Feb 07:59

Choose a tag to compare

What's New

  • Spring Boot Starter demo sample — new samples/outbox-spring-boot-starter-demo app demonstrating zero-config auto-configuration with @OutboxListener annotation. Compare with outbox-spring-demo which requires manual OutboxConfiguration with 7+ bean definitions.

Dependency Updates

  • build(deps): bump actions/upload-artifact from 4 to 6 by @dependabot[bot] in #35
  • build(deps): bump org.postgresql:postgresql from 42.7.3 to 42.7.10 by @dependabot[bot] in #36
  • build(deps-dev): bump org.apache.maven.plugins:maven-shade-plugin from 3.5.1 to 3.6.1 by @dependabot[bot] in #39
  • build(deps): bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.6.3 by @dependabot[bot] in #40
  • build(deps): bump com.mysql:mysql-connector-j from 8.3.0 to 9.6.0 by @dependabot[bot] in #38

Full Changelog: v0.8.0...v0.8.1

v0.8.0

21 Feb 05:20

Choose a tag to compare

Full Changelog: v0.7.2...v0.8.0

v0.7.2

21 Feb 01:24

Choose a tag to compare

Full Changelog: v0.7.1...v0.7.2

v0.7.1

19 Feb 07:11

Choose a tag to compare

Full Changelog: v0.6.0...v0.7.1