Skip to content

v1.8.1

Choose a tag to compare

@github-actions github-actions released this 11 Jun 19:22
· 110 commits to main since this release

What's changed in v1.8.1

  • perf: reduce commit/hydrate hot-path round trips and clones (#87) (by @patrickleet)

    • perf: drop event-history clones on the hydrate hot path

    hydrate() previously deep-cloned the entire event history via
    events().to_vec() on every replay even with no upcasters, purely to
    satisfy the borrow checker. Take the events out of the owned entity,
    replay from the local Vec, and restore via load_from_history. The
    no-upcaster path now replays with zero clones; the upcaster path keeps
    its single bounded clone (the durable history is restored verbatim).

    The snapshot tail path (hydrate_prepared_snapshot) did a similar
    .cloned() to collect post-snapshot events. Replay the common
    (no-upcaster) path directly from a filtered borrow so the tail is no
    longer cloned, while still restoring the full history so new_events()
    slicing on the next commit is unchanged. PR #81's snapshot+tail
    fetch behavior is preserved.

    Adds Entity::take_events / Entity::restore_history for the
    borrow-out-then-restore pattern.

    Hydrated state is identical: same replay order, same final
    version/committed_version, same upcaster semantics.

    • perf: batch commit inserts and collapse get_streams N+1

    commit_batch issued one INSERT per event and one INSERT per outbox
    message inside the open transaction, holding row/index locks across
    many round trips. Batch each into a single multi-row INSERT built with
    QueryBuilder::push_values (postgres) — and the same, chunked under
    SQLite's bound-parameter limit (sqlite). The per-stream version
    pre-check is kept: it enforces sequence contiguity, which the unique
    PK alone does not catch for expected<actual gaps.

    Conflict detection is unchanged. An event unique violation still maps
    to ConcurrentWrite (postgres re-reads the conflicting stream's version
    over the pool because a failed statement aborts the tx; sqlite re-reads
    in-tx since SQLite does not abort on a constraint error). An outbox
    unique violation still maps to DuplicateOutboxMessageInBatch.

    get_streams looped awaiting a query per identity (N+1). Replace with one
    WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY
    aggregate_id, sequence query per aggregate type (sqlite uses a bound IN
    list, having no array type), splitting the flat result into entities
    client-side. Callers of get_all already accept storage-order results.

    Ordering and hydrated state are unchanged.

    • perf: avoid payload clone on NATS publish

    publish() owns the Message and drops it immediately after, but cloned
    message.payload before handing it to JetStream. Move the buffer out with
    std::mem::take and convert via Bytes::from(Vec) (zero-copy) instead.

    Behavior is unchanged: the same bytes are published.

    Implements [[tasks/commit-hydrate-hot-path-efficiency]]

See full diff: v1.8.0...v1.8.1