Adds the Ecto storage adapter - #14
Merged
Merged
Conversation
Implements StatifierPersistence.Storage.Ecto over the schemas a host generates with use StatifierPersistence.Ecto: charts idempotent on content_hash (insert ... on conflict do nothing), positions upserted per session_id, and runs with the :run_exists refusal ridden on the V01 unique index through a changeset unique_constraint - one atomic insert, never a check-then-insert. update_run/2 overwrites through a single update_all whose match count doubles as the :run_not_found check. The status vocabulary maps explicitly in both directions; engine identities and blobs round-trip verbatim. isolate/1 checks out an SQL sandbox connection when the handle was built with sandbox: true, which is how the lib-shipped conformance suite now runs its 22 non-lock tests against this adapter on real Postgres (ADR-0005) - the identical suite InMemory passes. Adapter unit tests cover the host refusal, the full status vocabulary down to the stored strings, row-count idempotence, and the same CRUD against the schema-prefixed Overridden host. The test bootstrap migrates the Default and Overridden tables once per suite. lock_run/3 (the transaction-scoped per-run lock) lands separately; its conformance tests generate only once the callback exists. Includes the critic-reviewed implementation plan. Refs: sp-4an.3.1
Implements the optional per-run lock as one transaction spanning fun: pg_advisory_xact_lock(hashtextextended(run_id, 0)) first, then SELECT ... FOR UPDATE on the run row when it exists. The advisory half is load-bearing, not decoration: a row lock alone excludes nothing for a run_id whose run has not been inserted yet, and the adapter contract requires unconditional mutual exclusion. Recorded as a dated amendment to ADR-0004 decision 5, with the lock_run/3 doc in the behaviour and InMemory's pointer aligned to it. Exporting the callback makes the conformance suite generate its two lock tests against this adapter (verified present via --trace; the suite is now the full 24). New live tests outside the SQL sandbox prove the semantics on two real pooled connections - the sandbox's single shared connection serializes callers by ownership and would mask a broken lock: no overlap on an inserted run's lock, no overlap on a rowless run_id (red with the advisory lock removed), and release after a raising fun. Refs: sp-4an.3.1
README gains the Ecto adapter walkthrough - configure the persistence module, migrate through the versioned helper, build the guarded store, run host test suites with sandbox: true - and the Status section stops claiming nothing is implemented. The sp-4an.3.1 changelog fragment records the adapter, isolate/1, and lock_run/3 for the public surface. Refs: sp-4an.3.1
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.
Why
The sp-4an.3 epic's schemas and migrations landed in PR #13; this is the
adapter over them. Hosts get a Postgres storage backend that satisfies the
exact contract the behaviour and the lib-shipped conformance suite pin
(ADR-0003), with the identity guard staying in the facade above it.
What
StatifierPersistence.Storage.Ecto: theStorage.Adapterbehaviour overthe schemas a host generates with
use StatifierPersistence.Ecto(
Storage.new(Storage.Ecto, persistence: MyApp.Persistence)). Charts areidempotent on
content_hash, positions upsert persession_id, andinsert_run/2's:run_existsrides the V01 unique index through achangeset
unique_constraint- one atomic insert, never check-then-insert.update_run/2is a singleupdate_allwhose match count doubles as the:run_not_foundcheck. Engine identities and blobs round-trip verbatim.isolate/1(sandbox: true): per-testEcto.Adapters.SQL.Sandboxcheckout, which is how the conformance suite runs its full 24 tests against
this adapter - the identical suite
InMemorypasses - on real Postgres(ADR-0005 harness).
lock_run/3: one transaction spanningfunthat takespg_advisory_xact_lock(hashtextextended(run_id, 0))first, thenSELECT ... FOR UPDATEon the run row when it exists. The advisory half isload-bearing: a row lock alone excludes nothing for a
run_idwhose runhas not been inserted yet, which the adapter contract (and the conformance
lock tests) require. Recorded as a dated amendment to ADR-0004 decision 5.
connections - the sandbox's single shared connection serializes callers by
ownership and would mask a broken lock. The rowless test goes red with the
advisory lock removed; the inserted-run test pins the row-lock path.
Notes
mix qualitygreen and attested viamix gate.verifybefore everycommit and again before this push: 135/135 tests, 95.8% coverage,
dialyzer and deps audit included. Doctor/Gettext/Sobelow are the standing
not-applicable skips.
locking decision this repo owns; the behaviour doc and
InMemory'spointer were aligned in the same commit.
tables once per suite; adapter unit tests exercise the schema-prefixed
workflows.workflow_runspath, not only the zero-config host.Closes sp-4an.3.1