From a9c6a3c99d6d1b4862c0328153c051b0faca50a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Mon, 8 Jun 2026 09:21:03 +0200 Subject: [PATCH 1/3] docs: cut 0.3.0 changelog section Convert [Unreleased] to [0.3.0] (2026-06-08), condense entries to their essence while keeping BREAKING flags, PR refs, and the 0.2.x migration note. Add a fresh empty [Unreleased] and update compare links. VERSION_NAME is already 0.3.0, so the file now matches the artifact version. --- CHANGELOG.md | 119 +++++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5469230..7add003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,98 +8,56 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * ## [Unreleased] +## [0.3.0] — 2026-06-08 + ### Changed (BREAKING) -- **`PostgresOutboxStore` / `MysqlOutboxStore` no longer take a `clock` constructor - parameter.** It became unused after the lag-gauge fix ([#58](https://github.com/softwaremill/okapi/pull/58)) — - the stores derive no timestamps from a clock. Code that passed an explicit clock - (`PostgresOutboxStore(connectionProvider, clock)`) must drop the second argument; - the usual `PostgresOutboxStore(connectionProvider)` form is unchanged. Spring Boot - users are unaffected. -- **Outbox domain table renamed `outbox` → `okapi_outbox`.** Indexes follow the rename - (`idx_outbox_*` → `idx_okapi_outbox_*`). Host applications with a pre-existing `outbox` - table are no longer affected — okapi creates its own table under the `okapi_` prefix. - The new name is fixed; it is not configurable. ([#37](https://github.com/softwaremill/okapi/issues/37)) +- **Domain table renamed `outbox` → `okapi_outbox`** (indexes `idx_outbox_*` → + `idx_okapi_outbox_*`). okapi now owns a prefixed table, so a pre-existing `outbox` + no longer collides. Not configurable. ([#37](https://github.com/softwaremill/okapi/issues/37)) - **Liquibase tracking tables default to `okapi_databasechangelog` / - `okapi_databasechangeloglock`.** Previously okapi shared the application's - default `databasechangelog` / `databasechangeloglock`. Override the new defaults - via configuration to keep the shared-table layout (see Added below). - ([#37](https://github.com/softwaremill/okapi/issues/37)) + `okapi_databasechangeloglock`** instead of sharing the app's defaults; override via + the new properties to keep the old layout. ([#37](https://github.com/softwaremill/okapi/issues/37)) - **okapi's Liquibase migrations consolidated into a single - `001__create_okapi_outbox_table.sql` per database.** New installations create the - full schema (table + indexes) from one changeset instead of replaying the change - history; the resulting schema is unchanged. Existing installations from an earlier - release: the `outbox:001` changeset checksum changed — they must start on a fresh - okapi schema, or clear okapi's rows from `okapi_databasechangelog`, before upgrading. -- **`OutboxScheduler` / `OutboxPurger` in `okapi-core` now require a non-null - `TransactionRunner`** constructor parameter. The previous nullable default - caused `tick()` to silently fall back to a non-transactional path; under JDBC - auto-commit, `FOR UPDATE SKIP LOCKED` releases its row lock at the end of the - claim `SELECT` and concurrent processor instances deliver the same entry - multiple times. Spring Boot users are unaffected. Direct constructor users - (Ktor, manual Spring wiring, plain Java/Kotlin) must supply a - `TransactionRunner`; adapters with their own transaction primitive wrap it - in a thin implementation, e.g. - - ```kotlin - object : TransactionRunner { - override fun runInTransaction(block: () -> T): T = transaction { block() } - } - ``` - - ([#51](https://github.com/softwaremill/okapi/issues/51)) -- **`OutboxProcessorScheduler` / `OutboxPurgerScheduler` constructors now require a - non-null `TransactionRunner`** (previously a nullable `TransactionTemplate?`, with - `OutboxPurgerScheduler`'s parameter defaulted to `null`). Spring Boot autoconfig users - are unaffected — the autoconfig derives a `TransactionRunner` from any - `PlatformTransactionManager` on the classpath. Users constructing the schedulers - directly must supply a `TransactionRunner` (e.g. `SpringTransactionRunner(template)` or - a thin lambda wrapping their framework's native transaction primitive). - ([#49](https://github.com/softwaremill/okapi/pull/49)) -- **`okapi-spring-boot` autoconfig refuses to start when it cannot verify the - PlatformTransactionManager↔outbox-DataSource binding** in a multi-DataSource context. - Specifically: if `extractDataSource` cannot determine the PTM's DataSource (e.g. JTA, - Exposed's `SpringTransactionManager`, or any PTM that exposes neither a `DataSource` - resourceFactory nor a public `getDataSource()`), AND the context has ≥2 `DataSource` - beans, AND `okapi.transaction-manager-qualifier` is not set, the context refresh now - fails with an actionable message. `okapi.datasource-qualifier` alone is not - sufficient — it picks the outbox DataSource but does not constrain which PTM - brackets it. Single-DataSource contexts and setups that explicitly name the PTM - via `okapi.transaction-manager-qualifier` are unaffected. Escape hatch: supply an - explicit `@Bean TransactionRunner` to bypass validation. - ([#49](https://github.com/softwaremill/okapi/pull/49)) + `001__create_okapi_outbox_table.sql` per database.** Resulting schema is unchanged, + but the `outbox:001` checksum changed — upgraders must start on a fresh okapi schema + or clear okapi's rows from `okapi_databasechangelog`. +- **`OutboxScheduler` / `OutboxPurger` (okapi-core) now require a non-null + `TransactionRunner`.** The old nullable default silently ran non-transactionally, + letting `FOR UPDATE SKIP LOCKED` drop its lock under JDBC auto-commit and deliver + entries more than once. Spring Boot users unaffected; direct users (Ktor, manual + wiring, Java/Kotlin) must supply one. ([#51](https://github.com/softwaremill/okapi/issues/51)) +- **`OutboxProcessorScheduler` / `OutboxPurgerScheduler` now require a non-null + `TransactionRunner`** (was a nullable `TransactionTemplate?`). Spring autoconfig + derives it from any `PlatformTransactionManager`; direct constructor users must pass + `SpringTransactionRunner(template)` or a thin wrapper. ([#49](https://github.com/softwaremill/okapi/pull/49)) +- **`PostgresOutboxStore` / `MysqlOutboxStore` no longer take a `clock` parameter** — + unused after the lag-gauge fix. Drop the second constructor argument; Spring Boot + users unaffected. ([#58](https://github.com/softwaremill/okapi/pull/58)) +- **`okapi-spring-boot` autoconfig fails fast when it cannot verify the + PlatformTransactionManager↔outbox-DataSource binding** in a multi-DataSource context + with no `okapi.transaction-manager-qualifier` set. Name the PTM via that qualifier, or + supply an explicit `@Bean TransactionRunner` to bypass. ([#49](https://github.com/softwaremill/okapi/pull/49)) ### Added -- `okapi.liquibase.changelog-table` — Spring Boot property that configures the - `databaseChangeLogTable` of okapi's autoconfigured `SpringLiquibase` beans - (`okapiPostgresLiquibase` / `okapiMysqlLiquibase`). Default: `okapi_databasechangelog`. -- `okapi.liquibase.changelog-lock-table` — likewise for `databaseChangeLogLockTable`. - Default: `okapi_databasechangeloglock`. +- `okapi.liquibase.changelog-table` / `okapi.liquibase.changelog-lock-table` — Spring Boot + properties to override okapi's Liquibase tracking-table names (defaults + `okapi_databasechangelog` / `okapi_databasechangeloglock`). ### Fixed -- **`okapi.transaction-manager-qualifier` is now honoured in Spring Boot apps that - load `TransactionAutoConfiguration`.** Previously the qualifier was silently - ignored whenever a unique `TransactionTemplate` was present in the context — - which Spring Boot's `TransactionAutoConfiguration` registers out of the box around - the @Primary `PlatformTransactionManager`. The factory short-circuited on the - auto-TT's bound PTM and never consulted the qualifier. In a multi-PTM setup this - meant the @Primary PTM was used even when the user explicitly named a different - one. Resolution rule is now: explicit qualifier > auto-wired TT; when the - qualified PTM matches the unique TT's PTM the TT is reused verbatim (preserving - its `timeout`/`isolation`/`propagation`), otherwise a fresh `TransactionTemplate` - is built around the qualified PTM. ([#49](https://github.com/softwaremill/okapi/pull/49)) +- **`okapi.transaction-manager-qualifier` is now honoured even when + `TransactionAutoConfiguration` registers a unique `TransactionTemplate`.** Previously the + qualifier was silently ignored in multi-PTM setups, defaulting to the @Primary PTM. Rule + is now: explicit qualifier > auto-wired TT. ([#49](https://github.com/softwaremill/okapi/pull/49)) ### Migration from 0.2.x -These are breaking changes; existing deployments must take action before the first -`0.3.0` startup. The README has the full SQL: see -[Database migrations § Upgrading from 0.2.x](README.md#upgrading-from-02x). -Two paths are documented: rename in place (recommended) or stay on the legacy -changelog table names by overriding `okapi.liquibase.changelog-table` / -`changelog-lock-table`. The domain-table rename has no opt-out — run the -provided `ALTER TABLE ... RENAME TO okapi_outbox` script. +Breaking — existing deployments must act before the first `0.3.0` startup. Full SQL is in +the README: [Database migrations § Upgrading from 0.2.x](README.md#upgrading-from-02x). +Rename the domain table in place (no opt-out), and either adopt the new Liquibase +tracking-table names or override them back to the legacy ones. ## [0.2.0] — 2026-04-29 @@ -143,6 +101,7 @@ Initial public release. - `okapi-exposed` integration (transaction runner, connection provider, validator). - Concurrent processing via `FOR UPDATE SKIP LOCKED`. -[Unreleased]: https://github.com/softwaremill/okapi/compare/v0.2.0...HEAD +[Unreleased]: https://github.com/softwaremill/okapi/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/softwaremill/okapi/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/softwaremill/okapi/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/softwaremill/okapi/releases/tag/v0.1.0 From db56112d49b59894de09c10817de3e561fb5d338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Mon, 8 Jun 2026 09:25:09 +0200 Subject: [PATCH 2/3] docs: add missing 0.3.0 entries surfaced in review Code review flagged user-facing changes merged since v0.2.0 that were never recorded in [Unreleased]: - Added: MessageDeliverer.deliverBatch (#35) - Fixed: HTTP exception classification (#44), kafka api deps (#47), Liquibase NCDF on SB 3.5.x (#42/#38), micrometer autoconfig ordering (#41), OutboxPurger partial-batch progress log (#55) Internal-only changes (benchmarks #34/#48, docs #56, chore #59, tests #61) remain intentionally excluded. --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7add003..d71450e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,16 +41,44 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * ### Added +- **`MessageDeliverer.deliverBatch(entries)`** — batch-aware delivery method with a + sequential default impl (loops `deliver()`, preserving order and per-entry result + classification). Existing deliverers need no change; transports can override it for + concurrent I/O, and `CompositeMessageDeliverer` routes batches by delivery type. + ([#35](https://github.com/softwaremill/okapi/pull/35)) - `okapi.liquibase.changelog-table` / `okapi.liquibase.changelog-lock-table` — Spring Boot properties to override okapi's Liquibase tracking-table names (defaults `okapi_databasechangelog` / `okapi_databasechangeloglock`). ### Fixed +- **HTTP delivery exception classification.** `HttpMessageDeliverer` previously caught + every exception as `RetriableFailure`, so corrupt delivery metadata or an unknown + service triggered an infinite retry loop. `JsonProcessingException` and other non-IO + errors (malformed URI, unknown service) are now `PermanentFailure`; `IOException` / + `InterruptedException` stay retriable. ([#44](https://github.com/softwaremill/okapi/pull/44)) - **`okapi.transaction-manager-qualifier` is now honoured even when `TransactionAutoConfiguration` registers a unique `TransactionTemplate`.** Previously the qualifier was silently ignored in multi-PTM setups, defaulting to the @Primary PTM. Rule is now: explicit qualifier > auto-wired TT. ([#49](https://github.com/softwaremill/okapi/pull/49)) +- **`okapi-kafka` now exposes `kafka-clients` and `okapi-core` as `api` dependencies.** + `KafkaMessageDeliverer`'s public constructor takes `Producer`, so those + types belong on the consumer's compile classpath transitively — no more adding + `kafka-clients` by hand or hitting surprising `okapi-core` classpath failures. + ([#47](https://github.com/softwaremill/okapi/pull/47)) +- **Startup `NoClassDefFoundError` on Spring Boot 3.5.x without `liquibase-core`** (e.g. + Flyway-only apps) — okapi's Liquibase beans are now guarded by class-level + `@ConditionalOnClass(SpringLiquibase)`. Also stops okapi's `SpringLiquibase` bean from + shadowing the host application's own changelog (via `@AutoConfigureAfter` Boot's + `LiquibaseAutoConfiguration`). ([#42](https://github.com/softwaremill/okapi/pull/42), + [#38](https://github.com/softwaremill/okapi/issues/38)) +- **`okapi-micrometer` auto-config ordering on Spring Boot 3.5.x.** `@AutoConfigureAfter` + now lists both the 3.5.x and 4.0.x metrics-package locations, so the listener / metrics / + refresher are no longer silently skipped when `MeterRegistry` registers later. + ([#41](https://github.com/softwaremill/okapi/pull/41)) +- **`OutboxPurger` error log preserves partial-batch progress.** A mid-loop failure now + reports how many entries / batches were already purged this tick, so operators can tell + an early outage from a late transient hiccup. ([#55](https://github.com/softwaremill/okapi/pull/55)) ### Migration from 0.2.x From 79f0ed6feba529bcb73936ba65816289ed97d62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Mon, 8 Jun 2026 09:40:04 +0200 Subject: [PATCH 3/3] docs: tighten 0.3.0 entries per second-round review Three-reviewer team (completeness / technical truth / format) found no correctness errors, only precision fixes: - clock removal now cites #59 (actual ctor-param removal), #58 as the cause - add missing #50 ref to the Liquibase-consolidation bullet - drop 'infinite retry loop' (RetryPolicy maxRetries is finite) -> 'wasted the whole retry budget before being marked FAILED' - #42: don't name @AutoConfigureAfter (code uses @AutoConfiguration afterName) Cosmetic-only notes (Added bold-lead, trailing-URL line length) left as-is: they match the existing 0.2.0/0.1.0 style. --- CHANGELOG.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d71450e..f119b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * `001__create_okapi_outbox_table.sql` per database.** Resulting schema is unchanged, but the `outbox:001` checksum changed — upgraders must start on a fresh okapi schema or clear okapi's rows from `okapi_databasechangelog`. + ([#50](https://github.com/softwaremill/okapi/pull/50)) - **`OutboxScheduler` / `OutboxPurger` (okapi-core) now require a non-null `TransactionRunner`.** The old nullable default silently ran non-transactionally, letting `FOR UPDATE SKIP LOCKED` drop its lock under JDBC auto-commit and deliver @@ -32,8 +33,9 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * derives it from any `PlatformTransactionManager`; direct constructor users must pass `SpringTransactionRunner(template)` or a thin wrapper. ([#49](https://github.com/softwaremill/okapi/pull/49)) - **`PostgresOutboxStore` / `MysqlOutboxStore` no longer take a `clock` parameter** — - unused after the lag-gauge fix. Drop the second constructor argument; Spring Boot - users unaffected. ([#58](https://github.com/softwaremill/okapi/pull/58)) + it became unused after the lag-gauge fix ([#58](https://github.com/softwaremill/okapi/pull/58)). + Drop the second constructor argument; Spring Boot users unaffected. + ([#59](https://github.com/softwaremill/okapi/pull/59)) - **`okapi-spring-boot` autoconfig fails fast when it cannot verify the PlatformTransactionManager↔outbox-DataSource binding** in a multi-DataSource context with no `okapi.transaction-manager-qualifier` set. Name the PTM via that qualifier, or @@ -54,9 +56,10 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * - **HTTP delivery exception classification.** `HttpMessageDeliverer` previously caught every exception as `RetriableFailure`, so corrupt delivery metadata or an unknown - service triggered an infinite retry loop. `JsonProcessingException` and other non-IO - errors (malformed URI, unknown service) are now `PermanentFailure`; `IOException` / - `InterruptedException` stay retriable. ([#44](https://github.com/softwaremill/okapi/pull/44)) + service wasted the whole retry budget before being marked `FAILED` instead of failing + fast. `JsonProcessingException` and other non-IO errors (malformed URI, unknown + service) are now `PermanentFailure`; `IOException` / `InterruptedException` stay + retriable. ([#44](https://github.com/softwaremill/okapi/pull/44)) - **`okapi.transaction-manager-qualifier` is now honoured even when `TransactionAutoConfiguration` registers a unique `TransactionTemplate`.** Previously the qualifier was silently ignored in multi-PTM setups, defaulting to the @Primary PTM. Rule @@ -69,8 +72,9 @@ Until `1.0.0`, breaking changes may appear in any release and are flagged with * - **Startup `NoClassDefFoundError` on Spring Boot 3.5.x without `liquibase-core`** (e.g. Flyway-only apps) — okapi's Liquibase beans are now guarded by class-level `@ConditionalOnClass(SpringLiquibase)`. Also stops okapi's `SpringLiquibase` bean from - shadowing the host application's own changelog (via `@AutoConfigureAfter` Boot's - `LiquibaseAutoConfiguration`). ([#42](https://github.com/softwaremill/okapi/pull/42), + shadowing the host application's own changelog — okapi's auto-config is now ordered + after Spring Boot's `LiquibaseAutoConfiguration`. + ([#42](https://github.com/softwaremill/okapi/pull/42), [#38](https://github.com/softwaremill/okapi/issues/38)) - **`okapi-micrometer` auto-config ordering on Spring Boot 3.5.x.** `@AutoConfigureAfter` now lists both the 3.5.x and 4.0.x metrics-package locations, so the listener / metrics /