Binds the reap's ids as one array on Postgres - #151
Merged
Merged
Conversation
StatifierRouter.Addresses.reap/3 named its rows in an IN list of one
bound parameter per id on every adapter, so on Postgres the statement
text varied with the batch's length and Postgres prepared one statement
per distinct length where the array form had one.
The private stamp/3 and delete/2 now branch on the repo's adapter: on
Postgres each batch is one bound array, fragment("? = ANY(?)", a.id,
^batch), the same statement for every batch length; every other
adapter, and a repo module that defines no __adapter__/0, keeps the
spliced IN list, byte-identical to before. Ids are still bound uncast;
Postgres types the array from the id column. Batches of 500 and every
count and cursor are unchanged.
Adds StatifierRouter.PostgresReapTest and a SQLiteReapTest case, each
reading the statements from the repo's query telemetry event. ADR-0002
gets a dated foot Note; a changelog fragment records the change.
Refs: sr-7zxe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
StatifierRouter.Addresses.reap/3binds each batch of ids as one array onPostgres again, and keeps the spliced
INlist on every other adapter.Ruled by the operator, 2026-10-01: the array form on Postgres, the
INlist elsewhere, counts unchanged, with Postgres and SQLite tests in the
same request.
Since the SQLite reap fix, the private
stamp/3anddelete/2named theirrows in
fragment("? IN (?)", a.id, splice(^batch))on every adapter. OnPostgres that statement's text varies with the batch's length, so Postgres
prepared a statement for each distinct length where the array form had one.
How
postgres?/1reads the repo's adapter from__adapter__/0.Only
Ecto.Adapters.Postgrestakes the array form.stamp_query/2anddelete_query/2bindfragment("? = ANY(?)", a.id, ^batch): one parameter for the whole batch,so every batch of a write is the same statement.
INlist. On SQLite the statements arebyte-identical to the ones this branch's base sends (checked by capturing
every reap statement before and after the change over a reap of 1201 rows
and comparing them).
the id column's own type, so a text id made of digits alone stays a string.
stamped,deletedandnextare unchanged.Record
A dated Note at the foot of ADR-0002, "the address sweep binds one array per
batch on Postgres again", after the outbound-send Amendment and the token
Note. It names the adapter branch, the two forms, the array's type, what
does not change and the tests.
git diff origin/main -- docs/adr/shows zeroremoved lines.
Tests
StatifierRouter.PostgresReapTest, "binds each batch of ids as one array,so every batch of a write is one statement" (new module): a reap of 1201
rows writes three batches (500, 500, 201) of stamps, then three of
deletes. It reads the statements from the repo's query telemetry event and
asserts each write sent one
= ANY(statement text for all three batches,and the counts.
StatifierRouter.SQLiteReapTest, "binds each batch of ids as a spliced INlist": the same read on SQLite, asserting every write carries
IN (and noANY(.StatifierRouter.PrimaryKeyTest, "never casts a text id made of digitsalone" (unchanged, isolated stage) now runs the array form under a text key
of digits on Postgres, and passes.
Sabotage, each run on this branch and restored byte-equal before the next:
postgres?/1answering false (theINlist on Postgres): the Postgrestest went red on its single-statement assertion (two statement texts, a
500-parameter list and a 201-parameter one).
postgres?/1answering true (the array on SQLite): the new SQLite testwent red, the first reap raising "no such function: ANY".
postgres?/1without its__adapter__/0check: the existingStatifierRouter.CreateModesTesttest "never moves a terminal_seen_atanother writer set between the read and the write", whose repo module
delegates to the test repo and defines no
__adapter__/0, raisedUndefinedFunctionError(an error, not an assertion).Changelog
changelog.d/sr-7zxe.md, under Changed. Bychangelog.d/README.md's liststhis is a change in observable behaviour rather than an internal refactor
with no visible effect: a host that reads its repo's query telemetry event
or Postgres's statement statistics sees one reap statement per write where
it saw one per batch length.
Provenance
no
__adapter__/0(one that delegates to an Ecto repo rather than beingone) takes the
INlist, as before, rather than raising. The test supportrepo above is such a module. Recorded in the Note.
Postgres reap test file, so it can attach its own query telemetry handler.
Gate
Full
mix qualitygreen locally on the committed tree (431 of 431 tests,the isolated stage, Dialyzer, Credo, Docs, Doc links). The commit was made
without the commit skill's internal re-run: the staged tree was
byte-identical to the tree the gate had just run green on, and the repo gate
lock and a machine slot were held continuously across both.