A Datahike backend for the modular SPI - #81
Draft
whilo wants to merge 3 commits into
Draft
Conversation
Sets up `modules/eacl-datahike` against the modular SPI: module deps, root deps wiring (paths, dev/test/nrepl extra-paths, a :build-eacl-datahike alias), the shared-contract test, and PORTING.md. The three source files (core/impl/schema) are still to be ported from `modules/eacl-datascript`. DataScript is the template rather than Datomic, and that is the substantive finding here: both DataScript and Datahike reject a PARTIAL tuple as an `:avet` seek bound, where Datomic accepts one as a lower bound. Measured on datahike main in BOTH `:attribute-refs?` modes: seek [:room :owner] → [:kb :reader :party] bound ignored seek [:room :owner nil] → [:room :owner :party] correct seek [:room :owner :party] → [:room :owner :party] correct So the DataScript adapter's nil-padding is the correct approach on datahike too — and it is O(log n), not a workaround. An earlier attempt here treated the partial bound as a datahike compatibility bug and emulated Datomic by scanning the attribute segment; that was wrong on both counts. Requires replikativ/datahike#921 (composite `:db/tupleAttrs` were silently never derived, and tuple validation silently skipped, under `:attribute-refs?`). EACL's v7 schema is built on composite tuples, so before that fix every relation and permission tuple was absent and every permission denied. Merged to datahike main, not yet tagged, hence the :local/root — swap for an :mvn/version once released. The contract test is the definition of done: it runs the same `eacl.contract-support` suite as the DataScript backend, so the two are held to one definition rather than to tests that drifted apart.
Implements the six-fn backend SPI over datahike, ported from modules/eacl-datascript. DataScript rather than Datomic is the template because DataScript and datahike order tuple index bounds the same way. eacl.datahike.contract-test runs the shared eacl.contract-support suite — the same 23 assertions the DataScript backend answers — TWICE, once per attribute representation. Datahike reports a datom's :a as the attribute keyword by default and as a numeric ref under :attribute-refs? (Datomic's representation), and the second mode is where things break quietly, in three ways that all had to be handled: - composite tuples need datahike >= 0.8.1759 (replikativ/datahike#921). Before it they were silently never derived; since the tuples ARE the v7 engine, every permission check denied. - index-range's :attrid is the one accessor that rejects the attribute keyword under :attribute-refs?. It raises, so this one was loud. - comparing a datom's :a against a keyword set matches nothing, which for the cache-eviction listener fails OPEN: can? keeps answering from pre-change permission paths and grants what the schema just revoked. Adds eacl.datahike.db as a fourth namespace, so every divergence from the DataScript source (entid, attribute representation, the index accessors) sits in one file and the rest reads like its template. Adds eacl.datahike.backend-test for the paths the shared contract cannot reach. Worth stating plainly: the contract goes through make-client, which serves relation definitions from a prebuilt catalog, so it never exercises the tuple seek — deleting the nil padding leaves the contract suite green. Both mechanisms were verified by breaking them: no padding fails 12 assertions in both modes, a raw :a comparison fails exactly 1, only under :attribute-refs?. Module is JVM-only: datahike's CLJS API is async, the SPI is synchronous. Full suite: 61 tests, 951 assertions, 0 failures (all four modules).
The module deps.edn still carried :local/root "../../../datahike" and a comment saying the fix was unreleased, while the root deps.edn had already moved to 0.8.1759. A consumer building this module alone would have resolved a sibling checkout on whatever branch it happened to sit on — exactly the failure mode the comment warned about.
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.
Adds
modules/eacl-datahike, implementing the six-fn backend SPI overdatahike. Opened for discussion rather than as a
merge request — happy to reshape it to whatever fits your plans for the
modular branch.
eacl.datahike.contract-testruns the sharedeacl.contract-supportsuite — the same 23 assertions
eacl.datascript.contract-testanswers —so the new backend is held to your definition of a backend rather than to
tests of its own. Full suite across all four modules: 61 tests, 951
assertions, 0 failures.
Why DataScript was the template, not Datomic
Datahike and DataScript order tuple index bounds the same way, and
Datomic is the outlier: a partial tuple is not a valid seek bound.
Measured on datahike 0.8.1759 —
So
db/seek-tuple-prefixkeeps your DataScript approach verbatim: pad tofull arity with
nil, then take the matching prefix. Correct andO(log n).
The suite runs twice, because datahike has two attribute representations
Datahike reports a datom's
:aas the attribute keyword by default, andas a numeric ref under
:attribute-refs? true— Datomic'srepresentation, and a creation-time choice per database. The second mode
is where things break quietly, in three ways:
(replikativ/datahike#921,
which came out of this work). Before that fix they were silently never
derived — and since the tuples are the v7 engine, every permission
check denied.
index-range's:attridis the one accessor that rejects theattribute keyword under
:attribute-refs?. It raises, so this one wasloud.
:aagainst a set of keywords matches nothing.For the cache-eviction listener that fails open:
can?keepsanswering from pre-change permission paths and grants what the schema
has just revoked.
Running the contract in both modes is what catches (3) — it is invisible
in the default mode.
One thing worth flagging about the shared contract
The contract goes through
make-client, which serves relationdefinitions from a prebuilt schema catalog (a full index scan). So it
never exercises the tuple seek at all: deleting the nil padding leaves
the contract suite green. The seek is reached only by the bare-db path
(
impl/can?with no options) and by the relation-retraction guard.eacl.datahike.backend-testcovers those directly. Both mechanisms wereverified by breaking them: no padding fails 12 assertions in both modes,
a raw
:acomparison fails exactly 1, only under:attribute-refs?.This likely applies to
eacl-datascripttoo, if you want the samecoverage there.
Shape
Four namespaces where DataScript has three.
eacl.datahike.dbholds theadapter primitives —
entid(datahike has none), attributerepresentation, and the three index accessors — so every divergence sits
in one file and the rest reads like its template.
PORTING.mddocumentsthe full API mapping.
The module is JVM-only (
.clj): datahike's ClojureScript API isasynchronous and the SPI is synchronous.