fix(datomic): install tuple attributes after the attributes they compose

A composite tuple can only be created once its member attributes exist, and the
pieces are split across the two schema files: the tuple
:journal-entry-line/running-balance-tuple is declared in schema.edn while one of
its members, :journal-entry-line/running-balance, is declared in
cloud-migration-schema.edn, which is transacted afterwards.

Against a long-lived database this never surfaced, because those attributes were
installed by separate transactions years apart. Against an empty one it fails
outright with :db.error/invalid-tuple-attrs, which meant transact-schema could
not build a fresh database at all — and since every test fixture starts from an
empty datomic:mem database, the entire test suite died in setup rather than in
any individual test.

Install every plain attribute first, then every tuple.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 23:29:00 -07:00
parent 943bc18842
commit e0b0186b62

View File

@@ -875,13 +875,22 @@
(defn all-schema []
(edn/read-string (slurp (io/resource "schema.edn"))))
(defn transact-schema [conn]
@(dc/transact conn
(edn/read-string (slurp (io/resource "schema.edn"))))
(defn transact-schema
"Installs the schema in two passes: every plain attribute first, then every composite tuple.
;; this is temporary for any new stuff that needs to be asserted for cloud migration.
@(dc/transact conn
(edn/read-string (slurp (io/resource "cloud-migration-schema.edn")))))
A tuple can only be created once the attributes it composes already exist, and the pieces are
spread across both files — `:journal-entry-line/running-balance-tuple` lives in schema.edn
while one of its members, `:journal-entry-line/running-balance`, lives in
cloud-migration-schema.edn. Transacting the files in order therefore cannot install that tuple
against an empty database. Long-lived databases never hit it because those attributes went in
years apart."
[conn]
(let [schema (concat (edn/read-string (slurp (io/resource "schema.edn")))
;; this is temporary for any new stuff that needs to be asserted for cloud migration.
(edn/read-string (slurp (io/resource "cloud-migration-schema.edn"))))
{tuples true plain false} (group-by #(contains? % :db/tupleAttrs) schema)]
(when (seq plain) @(dc/transact conn plain))
(when (seq tuples) @(dc/transact conn tuples))))
(defn backoff [n]
(let [base-timeout 500