Skip to content

Separate outbox worker store boundary#40

Merged
patrickleet merged 4 commits into
feat/asyncfrom
feat/outbox-worker-store-boundary
May 25, 2026
Merged

Separate outbox worker store boundary#40
patrickleet merged 4 commits into
feat/asyncfrom
feat/outbox-worker-store-boundary

Conversation

@patrickleet
Copy link
Copy Markdown
Collaborator

@patrickleet patrickleet commented May 25, 2026

Summary

  • replace repository-based outbox worker APIs with OutboxStore/AsyncOutboxStore handles
  • add neutral table schema/write-plan wrappers and outbox table lowering
  • add SQLite/Postgres table schema artifact generation plus explicit dev/test bootstrap
  • update conformance, distributed read-model, saga, and docs usage to pass outbox stores

Verification

  • cargo check --all-targets --all-features
  • cargo test --all-features
  • cargo clippy --lib --all-features -- -D warnings
  • cargo fmt --check
  • git diff --check

Note: cargo clippy --all-targets --all-features -- -D warnings still fails on existing Bomberman/saga test lint debt unrelated to this change.

Summary by CodeRabbit

  • New Features

    • Dedicated outbox store abstraction with explicit claim-based lifecycle (claim, complete, release, fail).
    • Table/schema primitives and SQL migration/bootstrap tooling for durable outbox tables.
  • Documentation

    • Examples and docs updated to show the new store/claim workflow and schema bootstrap guidance.
  • Breaking Changes

    • Previous repository-extension outbox APIs replaced by store/claim patterns; update integrations and worker wiring.

Review Change Stack

Implements specs/outbox-worker-store-boundary
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1cfbe2a8-ca73-446d-958b-c54af3c06b2b

📥 Commits

Reviewing files that changed from the base of the PR and between 03b53d5 and 0cdab7b.

📒 Files selected for processing (2)
  • src/outbox/table.rs
  • src/table/sql.rs

📝 Walkthrough

Walkthrough

This PR refactors the outbox pattern from repository-extension traits to dedicated OutboxStore / AsyncOutboxStore implementations with explicit claim-based state transitions. It introduces a new table schema infrastructure supporting durable operational tables (outbox, read models) with SQL migration and dev-bootstrap helpers. All repository implementations (HashMap, Postgres, SQLite) and worker threads are updated to use the store-based API, including destination filtering and attempt-based retry logic.

Changes

Outbox Store API Refactoring

Layer / File(s) Summary
Claim & Store API Contract
src/outbox_worker/store.rs
ClaimOutboxMessages, OutboxClaimRef claim types; OutboxStore and AsyncOutboxStore traits; OutboxPublishFailureAction and ensure_active_claim added.
Outbox Table Schema & Row Model
src/outbox/table.rs, src/outbox/mod.rs
Defines OUTBOX_MESSAGES_TABLE, schema, key/row conversion, insert-plan, validate_outbox_message_table_write, and TableModel impl for OutboxMessage.
Table Model & SQL Migration Infrastructure
src/table/mod.rs, src/table/sql.rs
Adds TableModel and TableSchemaRegistryExt, TableSqlSchemaAdapter with dialect-aware DDL generation and migration artifact/bootstrapping helpers.
HashMapOutboxStore Implementation
src/hashmap_repo/repository.rs, src/hashmap_repo/mod.rs
Adds HashMapOutboxStore wrapper and outbox_store() accessor; per-message validation delegated to validate_outbox_message_table_write.
PostgresOutboxStore Implementation
src/postgres_repo/mod.rs
Adds PostgresOutboxStore, moves async outbox claim/complete/release/fail/record_failure to AsyncOutboxStore impl; adds table-schema adapter/migration/bootstrap helpers and table_schema_storage_error.
SqliteOutboxStore Implementation
src/sqlite_repo/mod.rs
Adds SqliteOutboxStore, moves async outbox APIs to it, updates claim SQL to support destination filtering and attempt guards, and adds table-schema bootstrap helpers and table_schema_storage_error.
Worker Thread Store-Based Claiming & Completion
src/outbox_worker/thread.rs, src/outbox_worker/mod.rs
Updates OutboxWorkerThread::spawn* to accept OutboxStore; worker loop claims via store.claim(ClaimOutboxMessages::new(...)), converts messages to OutboxClaimRef, and calls store.complete or store.record_failure. Documentation and re-exports updated.
Repository Module & AsyncOutboxRepositoryExt Removal
src/repository/async_repository.rs, src/repository/mod.rs
Removes AsyncOutboxRepositoryExt trait and its re-export from the crate root; narrows imports to outbox message types only.
Library Root Exports & Module Structure
src/lib.rs
Adds table module and re-exports table primitives; updates crate-root re-exports to expose outbox store types (HashMapOutboxStore, PostgresOutboxStore, SqliteOutboxStore), expands outbox re-exports to include table/schema helpers, and removes AsyncOutboxRepositoryExt from top-level repository exports.
Documentation & Examples
README.md, docs/async-repositories.md, docs/read-models.md, src/bus/mod.rs
Updates README and module docs to demonstrate OutboxStore claim/complete APIs; replaces AsyncOutboxRepositoryExt docs with AsyncOutboxStore; adds table-schema registry/bootstrap example for dev/test.
Tests Updated to Store-Based API
tests/*, src/outbox/commit.rs, tests/persistent_repository_conformance/outbox.rs
Tests updated to import/use OutboxStore/AsyncOutboxStore, construct ClaimOutboxMessages, convert claimed messages to OutboxClaimRef, pass repo.outbox_store() into worker threads and conformance helpers, and use table-schema bootstrap test for SQLite.
Supporting Changes
src/outbox_worker/worker.rs, src/sqlx_repo/mod.rs, src/read_model/metadata.rs
Doc comment updates, per-message table-write validation via validate_outbox_message_table_write, and addition of ColumnType::Timestamp and its SQL mapping.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🐰 I nibble code and hum a tune,

Claims and stores beneath the moon.
Tables seed and workers roam,
Messages find a proper home.
Hop, commit, and publish — done!

✨ 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 feat/outbox-worker-store-boundary

@patrickleet
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
src/outbox_worker/store.rs (1)

242-274: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align claim ordering with SQL stores (created_at, then message_id).

Line 253/357 currently sorts by message_id only, while SQL store implementations claim by created_at ASC, message_id ASC (src/postgres_repo/mod.rs line range 348-352 and src/sqlite_repo/mod.rs line range 557-561 in provided snippets). This backend divergence can reorder delivery and produce inconsistent behavior across repositories.

Suggested fix
-        let mut ids = storage.keys().cloned().collect::<Vec<_>>();
-        ids.sort();
+        let mut ids = storage
+            .values()
+            .map(|m| (m.created_at, m.id().to_string()))
+            .collect::<Vec<_>>();
+        ids.sort_by(|(l_ts, l_id), (r_ts, r_id)| l_ts.cmp(r_ts).then_with(|| l_id.cmp(r_id)));
         let mut claimed = Vec::new();
-        for id in ids {
+        for (_, id) in ids {
             let Some(message) = storage.get_mut(&id) else {
                 continue;
             };

Also applies to: 342-381

🤖 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 `@src/outbox_worker/store.rs` around lines 242 - 274, The claim ordering
currently sorts only by message id in the claim function (fn claim) causing
divergence from SQL backends; change the iteration to collect entries from
self.storage (iterate storage.iter() or storage.keys().cloned() together with
their messages) and sort by (message.created_at, message_id) — i.e., order by
created_at ASC then message_id ASC — before claiming; apply the same fix to the
other similar loop noted around lines 342-381 so both places use created_at then
id for consistent ordering across repositories (use the OutboxMessage.created_at
field and the message id key from the map when building the sort key).
🧹 Nitpick comments (3)
docs/read-models.md (1)

264-264: ⚡ Quick win

Consider showing the import or full path for outbox_message_schema().

The function outbox_message_schema() is called without context. Readers may benefit from seeing where this function is imported from or its full path.

📘 Suggested clarification
 ```rust
+use sourced_rust::outbox::outbox_message_schema;
+
 let mut registry = TableSchemaRegistry::new();
 registry.register_schema(outbox_message_schema())?;

Or use the full path inline:

-registry.register_schema(outbox_message_schema())?;
+registry.register_schema(sourced_rust::outbox::outbox_message_schema())?;
🤖 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 `@docs/read-models.md` at line 264, Show where outbox_message_schema() comes
from by adding its import or using its full path when registering the schema:
update the example that creates let mut registry = TableSchemaRegistry::new();
to either include a use line that imports outbox_message_schema (e.g., use
sourced_rust::outbox::outbox_message_schema;) or call
registry.register_schema(sourced_rust::outbox::outbox_message_schema()) to make
the function origin explicit.
src/hashmap_repo/repository.rs (1)

411-416: ⚡ Quick win

Extract this outbox write validation into one shared helper.

This is duplicated from src/sqlx_repo/mod.rs, so HashMap and SQL-backed repositories can quietly diverge on validation behavior. Pulling it into a shared outbox/table utility keeps store conformance aligned.

🤖 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 `@src/hashmap_repo/repository.rs` around lines 411 - 416, The
validate_outbox_table_write function duplicates logic present in
src/sqlx_repo/mod.rs; extract its body into a shared helper (e.g.,
outbox::validate_outbox_message_write or outbox::outbox_write_validator) and
replace both validate_outbox_table_write (in hashmap repo) and the equivalent
code in sqlx_repo with a call to that shared helper; keep the same behavior
(calling crate::outbox::outbox_message_insert_plan(...).and_then(|plan|
plan.validate().map(|()| plan)).map(|_| ()).map_err(|err|
RepositoryError::Model(err.to_string()))) and ensure the helper returns
Result<(), RepositoryError> so callers (including validate_outbox_table_write)
simply delegate to it.
src/outbox_worker/store.rs (1)

323-338: ⚡ Quick win

Make async status listing ordering deterministic with sync path.

Line 337 sorts only by created_at; sync messages_by_status also tie-breaks by message_id (Line 234-238). Adding the same tie-breaker avoids unstable ordering when timestamps collide.

🤖 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 `@src/outbox_worker/store.rs` around lines 323 - 338, The async listing in
messages_by_status_async currently sorts only by created_at, causing
nondeterministic ordering when timestamps tie; mirror the sync
messages_by_status tie-breaker by sorting by (created_at, message_id) instead of
solely created_at. Update messages.sort_by_key(...) to use a composite key or
use sort_by comparing created_at then message_id so ordering matches the sync
path (referencing messages_by_status, OutboxMessage.created_at and
OutboxMessage.message_id).
🤖 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 `@src/outbox_worker/thread.rs`:
- Around line 111-118: Remove the unnecessary Clone bound from the
OutboxWorkerThread spawn APIs: update the generic constraints on spawn and
spawn_with_id so S: OutboxStore + Send + 'static (drop S: Clone) and similarly
remove Clone where spawn delegates to spawn_with_id; confirm there are no
store.clone() calls in methods like record_publish_claim,
record_publish_complete, or record_publish_failure and adjust any signatures
that currently require Clone so valid OutboxStore implementations that are not
Clone compile.

In `@src/outbox/table.rs`:
- Around line 26-33: The temporal columns (created_at, next_available_at,
claimed_until, published_at, failed_at) are defined as
ColumnType::UnsignedInteger in the table schema (see the table_column(...) calls
in src/outbox/table.rs) but runtime SQL in postgres_repo and sqlite_repo uses
timestamp semantics (to_timestamp(...), CURRENT_TIMESTAMP, timestamp
comparisons), causing a type mismatch; change those table_column(...)
definitions to a proper timestamp type (or the project’s canonical DB timestamp
ColumnType) so the schema matches the SQL paths, and update any related
migration/bootstrap artifacts and tests to use timestamps consistently (also
review usages in src/postgres_repo/mod.rs and src/sqlite_repo/mod.rs to ensure
they read/write the same timestamp representation).

In `@src/sqlite_repo/mod.rs`:
- Around line 127-136: The bootstrap_table_schema_for_dev loop (and the sibling
method around lines 171-180) incorrectly maps SQL execution failures using
read_model_storage_error; update those sqlx::query(...).execute(...).await
.map_err(...) calls to use the table-schema/table-store specific error mapper
used by the Postgres implementation (replace read_model_storage_error(...) with
the table-specific mapper function used elsewhere in the codebase, e.g. the
TableStoreError mapper from the Postgres module), so failures are converted into
TableStoreError instead of read-model errors.

---

Outside diff comments:
In `@src/outbox_worker/store.rs`:
- Around line 242-274: The claim ordering currently sorts only by message id in
the claim function (fn claim) causing divergence from SQL backends; change the
iteration to collect entries from self.storage (iterate storage.iter() or
storage.keys().cloned() together with their messages) and sort by
(message.created_at, message_id) — i.e., order by created_at ASC then message_id
ASC — before claiming; apply the same fix to the other similar loop noted around
lines 342-381 so both places use created_at then id for consistent ordering
across repositories (use the OutboxMessage.created_at field and the message id
key from the map when building the sort key).

---

Nitpick comments:
In `@docs/read-models.md`:
- Line 264: Show where outbox_message_schema() comes from by adding its import
or using its full path when registering the schema: update the example that
creates let mut registry = TableSchemaRegistry::new(); to either include a use
line that imports outbox_message_schema (e.g., use
sourced_rust::outbox::outbox_message_schema;) or call
registry.register_schema(sourced_rust::outbox::outbox_message_schema()) to make
the function origin explicit.

In `@src/hashmap_repo/repository.rs`:
- Around line 411-416: The validate_outbox_table_write function duplicates logic
present in src/sqlx_repo/mod.rs; extract its body into a shared helper (e.g.,
outbox::validate_outbox_message_write or outbox::outbox_write_validator) and
replace both validate_outbox_table_write (in hashmap repo) and the equivalent
code in sqlx_repo with a call to that shared helper; keep the same behavior
(calling crate::outbox::outbox_message_insert_plan(...).and_then(|plan|
plan.validate().map(|()| plan)).map(|_| ()).map_err(|err|
RepositoryError::Model(err.to_string()))) and ensure the helper returns
Result<(), RepositoryError> so callers (including validate_outbox_table_write)
simply delegate to it.

In `@src/outbox_worker/store.rs`:
- Around line 323-338: The async listing in messages_by_status_async currently
sorts only by created_at, causing nondeterministic ordering when timestamps tie;
mirror the sync messages_by_status tie-breaker by sorting by (created_at,
message_id) instead of solely created_at. Update messages.sort_by_key(...) to
use a composite key or use sort_by comparing created_at then message_id so
ordering matches the sync path (referencing messages_by_status,
OutboxMessage.created_at and OutboxMessage.message_id).
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bef64870-9c08-4b6d-93be-eec3c19794ea

📥 Commits

Reviewing files that changed from the base of the PR and between f789034 and 2a8dea2.

📒 Files selected for processing (35)
  • README.md
  • docs/async-repositories.md
  • docs/read-models.md
  • src/bus/mod.rs
  • src/hashmap_repo/mod.rs
  • src/hashmap_repo/repository.rs
  • src/lib.rs
  • src/outbox/commit.rs
  • src/outbox/mod.rs
  • src/outbox/table.rs
  • src/outbox_worker/mod.rs
  • src/outbox_worker/store.rs
  • src/outbox_worker/thread.rs
  • src/outbox_worker/worker.rs
  • src/postgres_repo/mod.rs
  • src/repository/async_repository.rs
  • src/repository/mod.rs
  • src/sqlite_repo/mod.rs
  • src/sqlx_repo/mod.rs
  • src/table/mod.rs
  • src/table/sql.rs
  • tests/async_repository/main.rs
  • tests/bomberman/main.rs
  • tests/distributed_read_model/main.rs
  • tests/distributed_read_model_board/main.rs
  • tests/hashmap_repository_conformance/main.rs
  • tests/microsvc/convention.rs
  • tests/persistent_repository_conformance/outbox.rs
  • tests/postgres_repository_conformance/main.rs
  • tests/sagas/distributed.rs
  • tests/sagas/microsvc_saga.rs
  • tests/sourced_snapshot/main.rs
  • tests/sqlite_repository/main.rs
  • tests/sqlite_repository_conformance/main.rs
  • tests/todos/main.rs

Comment thread src/outbox_worker/thread.rs Outdated
Comment thread src/outbox/table.rs Outdated
Comment thread src/sqlite_repo/mod.rs Outdated
@patrickleet
Copy link
Copy Markdown
Collaborator Author

@coderabbitai I also addressed the non-inline review items in f7a6c7a:

  • src/outbox_worker/store.rs: HashMap claim ordering now matches SQL stores (created_at ASC, message_id ASC) for both sync and async claim paths via claim_order_ids(...); async status listing now uses the same sort_by_claim_order(...) tie-breaker as sync. Added regression tests for created-at ordering and id tie-breaking.
  • docs/read-models.md: the example now calls sourced_rust::outbox_message_schema() so the API origin is explicit.
  • src/outbox/table.rs, src/hashmap_repo/repository.rs, src/sqlx_repo/mod.rs: outbox table write validation is centralized in validate_outbox_message_table_write(...) and both HashMap and SQL-backed repository validation paths call it.

Follow-up cleanup in 03b53d5 clears the unrelated all-target clippy debt in the test fixtures (tests/bomberman and tests/sagas) so cargo clippy --all-targets --all-features -- -D warnings now passes.

Validation run after these commits:

  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-features
  • cargo fmt --check
  • git diff --check

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

@patrickleet Thanks for the thorough follow-up! All addressed items look solid:

  • Deterministic ordering — unifying HashMap and SQL claim ordering (created_at ASC, message_id ASC) via claim_order_ids / sort_by_claim_order eliminates a subtle non-determinism that could surface in tests and real workloads. Good to have regression tests backing this.
  • Explicit API origin — qualifying the docs/read-models.md example with sourced_rust::outbox_message_schema() makes the module path clear for readers.
  • Centralized validation — consolidating into validate_outbox_message_table_write(...) removes the risk of the HashMap and SQL paths drifting in their validation rules.
  • Clippy debt cleared — great to have cargo clippy --all-targets --all-features -- -D warnings fully green now.

Triggering a review of the new commits.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/outbox/table.rs (1)

17-39: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add updated_at to the generated outbox schema.

The runtime SQL outbox paths mutate updated_at on claim/complete/release/fail, but outbox_message_schema() never declares that column. Any outbox_messages table bootstrapped from this schema will therefore fail on the first worker update with a missing-column error. Add updated_at here and make sure the insert/default path is consistent with it.

🛠 Minimal schema fix
         columns: vec![
             table_column("message_id", ColumnType::Text, false),
             table_column("event_type", ColumnType::Text, false),
             table_column("payload", ColumnType::Bytes, false),
             table_column("payload_codec", ColumnType::Text, false),
             table_column("payload_codec_version", ColumnType::UnsignedInteger, false),
             table_column("destination", ColumnType::Text, true),
             table_column("metadata", ColumnType::Json, false),
             table_column("status", ColumnType::Text, false),
             table_column("created_at", ColumnType::Timestamp, false),
             table_column("next_available_at", ColumnType::Timestamp, false),
             table_column("claimed_by", ColumnType::Text, true),
             table_column("claimed_until", ColumnType::Timestamp, true),
             table_column("attempts", ColumnType::UnsignedInteger, false),
             table_column("last_error", ColumnType::Text, true),
             table_column("published_at", ColumnType::Timestamp, true),
             table_column("failed_at", ColumnType::Timestamp, true),
+            table_column("updated_at", ColumnType::Timestamp, true),
             table_column("source_aggregate_type", ColumnType::Text, true),
             table_column("source_aggregate_id", ColumnType::Text, true),
             table_column("source_sequence", ColumnType::UnsignedInteger, true),
             table_column("correlation_id", ColumnType::Text, true),
             table_column("causation_id", ColumnType::Text, true),
         ],
🤖 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 `@src/outbox/table.rs` around lines 17 - 39, outbox_message_schema() currently
omits the updated_at column while runtime SQL outbox mutates updated_at on
claim/complete/release/fail; add table_column("updated_at",
ColumnType::Timestamp, false) to the columns vector in outbox_message_schema()
(near the existing created_at/next_available_at columns) and ensure any
insert/default logic that sets created_at also sets updated_at (or gives it the
same default) so bootstrap schema and runtime updates use the same column and
won’t cause a missing-column error.
🤖 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.

Outside diff comments:
In `@src/outbox/table.rs`:
- Around line 17-39: outbox_message_schema() currently omits the updated_at
column while runtime SQL outbox mutates updated_at on
claim/complete/release/fail; add table_column("updated_at",
ColumnType::Timestamp, false) to the columns vector in outbox_message_schema()
(near the existing created_at/next_available_at columns) and ensure any
insert/default logic that sets created_at also sets updated_at (or gives it the
same default) so bootstrap schema and runtime updates use the same column and
won’t cause a missing-column error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8095e36a-11cf-4e67-b13b-bd79ba8411d7

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8dea2 and 03b53d5.

📒 Files selected for processing (17)
  • docs/read-models.md
  • src/hashmap_repo/repository.rs
  • src/outbox/mod.rs
  • src/outbox/table.rs
  • src/outbox_worker/store.rs
  • src/outbox_worker/thread.rs
  • src/read_model/metadata.rs
  • src/sqlite_repo/mod.rs
  • src/sqlx_repo/mod.rs
  • src/table/sql.rs
  • tests/bomberman/commands.rs
  • tests/bomberman/domain/game_map.rs
  • tests/read_model_schema_bootstrap/main.rs
  • tests/sagas/order/mod.rs
  • tests/sagas/order/order_aggregate.rs
  • tests/sagas/order/payment.rs
  • tests/sagas/order/saga.rs
✅ Files skipped from review due to trivial changes (2)
  • tests/sagas/order/payment.rs
  • docs/read-models.md

@patrickleet
Copy link
Copy Markdown
Collaborator Author

@coderabbitai Addressed the latest updated_at schema finding in 0cdab7b.

  • src/outbox/table.rs: outbox_message_schema() now includes updated_at as a non-null ColumnType::Timestamp column with DEFAULT CURRENT_TIMESTAMP, matching the runtime SQL updates in the Postgres and SQLite outbox stores while preserving insert paths that omit updated_at.
  • src/table/sql.rs: added renderer assertions that generated SQLite and Postgres outbox table DDL includes the updated_at timestamp/default column.

Validation:

  • cargo check --all-targets --all-features
  • cargo test --all-features
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo fmt --check
  • git diff --check

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

@patrickleet Great fix! The updated_at column addition is exactly right:

  • Declaring it as ColumnType::Timestamp with DEFAULT CURRENT_TIMESTAMP and non-null ensures the schema mirrors what the SQL update paths already write at runtime, while insert paths that omit updated_at get the default automatically.
  • The DDL renderer assertions in src/table/sql.rs give confidence that both SQLite and Postgres artifacts stay in sync with the schema definition going forward.

Triggering a review of commit 0cdab7b.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@patrickleet patrickleet merged commit a310a30 into feat/async May 25, 2026
1 check passed
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.

1 participant