datomic 2026-08-01

Feature Request for Auto-retracting Unique Composite Ref Tuples in Datomic Pro: https://github.com/theronic/eacl needs a schema-level way to define that a tuple should be retracted if it contains a :db.type/ref component to an entity that is being retracted, so that when entity A is retracted, any tuple on entity B referencing entity A is also retracted, instead of turning the tuple component nil and causing uniqueness conflicts. EACL moved away from Unique Composite Tuples in the Datomic Pro backend because calling :db.fn/retractEntity would turn tuple components of type :db.type/ref => nil, which on the second invocation, would trigger a unique tuple conflict when [_x _x nil] == [_x _x nil], blocking d/transact which was disastrous for consumers who forgot to call eacl/delete-relationships! first (also necessitates two txes in the tx-log when one could do). And they will forget to delete relationships first. Currently, calling :db.fn/retractEntity on an EACL entity , when it has Relationship defined [subject relation resource] to another entity (where the contra resource->subject tuple is stored) will leave a ghost relationship in the heterogenous tuple on the contra-entity. EACL has helpers like eacl/delete-object! that retracts both for you, but consumers still forget to call it. A Transactor Function like :eacl.fn/retractEntity (one less tx) would be more elegant than eacl/delete-object!...but consumers will still forget to call it. So the ideal solution would be a schema-level tuple definition that designates any composite tuple that contains a ref to the retracted entity should be retracted on all other referencing entities, even if only for unique composite tuples.

Your article made me aware of this footgun 🙂. Given that tuple ref components can be turned nil, perhaps Datomic Pro could support a :tuple/policy which designates "retract the containing tuple" instead of, "turn the component nil"? Both policies amend the entire tuple, but one retraction policy could retract the tuple, instead of updating one ref component to nil.

Alternatively, we need a magic value like NULL or :magic/empty-value that is not equal to anything, including other NULLs. We'd still have dangling ghost tuples, but at least they would not influence relationship queries in EACL. Rich made the decision to do nil-punning in Clj, which is unlikely to change, so I would lean toward treating this as a "rudimentary materialized view" problem in Datomic, i.e. there can exist a tuple that is derived from some facts and it should be retracted when those facts are retracted.