Skip to content

PostgreSQL: bulk AppendMessages in a single round-trip#152

Merged
stidsborg merged 2 commits into
mainfrom
postgres-bulk-append-single-roundtrip
May 25, 2026
Merged

PostgreSQL: bulk AppendMessages in a single round-trip#152
stidsborg merged 2 commits into
mainfrom
postgres-bulk-append-single-roundtrip

Conversation

@stidsborg
Copy link
Copy Markdown
Owner

Summary

  • Move the per-flow AppendMessages SQL into SqlGenerator so all message-store SQL lives in one place, alongside the existing position-explicit AppendMessages(IReadOnlyList<StoredIdAndMessageWithPosition>) overload.
  • Rewrite PostgreSqlMessageStore.AppendMessages(IReadOnlyList<StoredIdAndMessage>) to group by StoredId, build one per-flow append command via SqlGenerator, and execute together with the interrupt in a single NpgsqlBatch. This drops the separate GetMaxPositions round-trip.
  • Single-flow AppendMessages(StoredId, IReadOnlyList<StoredMessage>) is now a thin wrapper around the bulk overload.

The append SQL itself is parameterized via unnest(\$3::bytea[]) WITH ORDINALITY, so the SQL string is constant regardless of batch size and PostgreSQL's prepared-statement cache is reused across calls. The per-call random base offset (COALESCE(MAX(position), -1) + 2147483647 + \$2) preserves the existing collision-avoidance scheme for concurrent appenders to the same flow.

Test plan

  • dotnet test ./Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests --filter "FullyQualifiedName~Messaging|FullyQualifiedName~WorkflowMessageTests" — 73/73 passing

stidsborg added 2 commits May 25, 2026 09:38
Move the per-flow AppendMessages SQL into SqlGenerator and have the
bulk overload group by StoredId, build one append command per flow,
and execute together with the interrupt in a single NpgsqlBatch.

This drops the separate GetMaxPositions round-trip — positions are now
computed server-side via COALESCE(MAX(position), -1) + random base +
unnest(...) WITH ORDINALITY, so the SQL string is constant regardless
of batch size and the prepared statement cache is reused across calls.
The default 10s threshold occasionally trips on the SqlServer CI runner
under 12-way parallel test execution. Bumping to 30s gives the workflow
restart + replay cycle more headroom without changing test semantics.
@stidsborg stidsborg merged commit b34b72b into main May 25, 2026
8 checks passed
@stidsborg stidsborg deleted the postgres-bulk-append-single-roundtrip branch May 25, 2026 08:25
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