TAVE tx-window index + Logseq/shared query benches vs Datahike - #10
Merged
cursor[bot] merged 31 commits intoAug 30, 2026
Merged
Conversation
Document how to resolve wall-clock windows to tx bounds via :db/txInstant, add a tx-ordered TEAV Share_index, and rewrite unbound scans under a window so cost tracks window size instead of full-graph filter-after-scan. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Clarify that EAVT/AVET remain primary; add selection matrix, candidate discipline, API sketch, cost hints, and open decisions before implementation. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Survey logseq/logseq hot paths: entity/AVET-dominated; recency uses :block/updated-at and :block/journal-day on AVET, not tx windows. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Document that d/q and datoms stay stable; the engine auto-picks AVET time ranges for Logseq shapes and may use TAVE only for tx-scoped views. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Add tx|a|v|e TAVE Share_index (LMDB ds/tave, SQLite ds_tave), write it on every tx, prune by configurable retention (default 30 days via txInstant), and auto-use TAVE for since-bounded AEVT attr scans without API changes. Also invalidate LMDB read txns after meta_set so store/restore stays fresh. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
tMeasure SQLite per-table payload with/without rolling retention to quantify TAVE disk overhead versus the three primary indexes. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Mirror initial_data hot paths (recent pages, journals, uuid/title lookups, parent/page/tags AVET, between queries) plus since+AEVT for TAVE contrast. Compares memory, LMDB, and SQLite. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Debug showed recent-pages was ~300ms because the bench used rseek-datoms (cross-attr, DataScript semantics) instead of Logseq's (rseq (datoms :avet attr)), and LMDB rslice_seq did to_list on the whole index. Use exact-attr reverse in the bench; walk descending with datom-level bounds in rslice (LMDB + SQLite). Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Replace the Node JS upstream harness with nbb CLJS using node:sqlite (serializable round-trip). Shared OCaml suite now uses datascript_sqlite store/restore. Compare script runs forked-CLJS, origin/main, and current on the same SQLite-backed workload without creating new branches. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Patch storage_of_handle and refresh_db_indexes when copying the shared suite into the detached main worktree so the 3-way compare can run. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Avoid O(n^2) List.nth in index seq iteration, scope rseek to exact attr prefixes, keep live reverse views lazy, and reuse avet/aevt attr caches for descending walks so recent-pages matches CLJS (rseq datoms) cost. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
CLJS bench now runs under @logseq/nbb-logseq#feat-db-v34 with datascript.core (no release-js): IStorage on SQLite kvs + transit, create-conn/restore-conn, and transact! like sqlite-cli. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
CLJS nbb-logseq PSS+kvs vs ocaml-main vs ocaml-current (non-PSS). Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Hot-path slice_seq/rslice_seq now stream datoms instead of materializing full ranges. SQLite holds a dedicated cursor stmt; LMDB follows Datalevin range-seq by pulling batches through scoped Cursor.go with continuation re-seek (no Obj.magic). Reverse attr scans no longer warm avet/aevt caches eagerly. Label the 3-way bench so ocaml-main is PSS+SQLite vs current non-PSS Share. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Shared hot paths: attr fold_datoms no longer materializes via primary_attr_datoms; multi-component rseek (including pending) omits the lower clamp. SQLite sync finalizes cached stmts before WAL checkpoint. Add test_index_scan_backends for memory / memory_storage / LMDB file / SQLite file seek-rseek-datoms parity. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Stream SQLite ranges via cached fold batch pulls so WAL sync and point lookups stay fast. Point EAVT e+a for find_datom and entity attr hydrate instead of rescanning whole entities. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Current non-PSS Share now beats or matches fair CLJS PSS on query hot paths (recent-pages, uuid-lookup, entity-hydrate, etc.). Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Share AVET/TAVE keys collapse Int/Ref to Float; rehydrate from schema so scans and filters match write-side types. Emit result-edn lines from CLJS and OCaml benches and fail the 3-way compare on EDN mismatches. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Cross-runtime results are compared as canonical result-edn strings. Datahike uses PSS + SQLite JDBC; Datalevin uses LMDB with :ave indexes. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Lazy-warm avet_entities_by_attr_value after Share/LMDB point scans so repeated AVET hits stay in RAM. Entity forward attr reads now use one EAVT e-prefix scan instead of per-attr seeks. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Repeated EAVT e-prefix and entity hydrate reads hit an in-process cache keyed by db_uid/max_tx/eid, closing the gap vs in-memory Datahike PSS. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
q-journal-pages and similar attr-only AVET where clauses now use primary_attr_datoms so repeated queries hit the in-memory attr cache. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Cache AVET/EAVT hot paths for Share SQLite, fix fused execute for open-value entity groups (q-journal-pages), and compare all runtimes via canonical result-edn strings including Datahike and Datalevin. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
dl/clear expects a conn, not a directory path. Use remove-path for durable LMDB dirs; label backend as lmdb-durable. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Compare q1–q-rule on OCaml Share SQLite, Datahike PSS+SQLite JDBC, and Datalevin LMDB with matching LCG data, explicit eids, and result-edn equality. Include compare script with EDN and OCaml-vs-Datahike gates. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Bulk entity loads spent minutes in O(n^2) list append while building tx_data. Rebuild with a hash-indexed O(n) path, and write EAVT/AEVT/AVET/TAVE via multi-row REPLACE inside a bulk SQLite write transaction (still writing all indexes). Shared 20k build-ms drops to ~1.6s. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Open-value entity groups with inequality filters (qpred1 / q-pred-range) now drive fold_index_range with start/stop bounds instead of full AEVT scan + post-filter. Also densify q5 cross-entity output lookups and cover the predicate shapes in query-exec parity tests. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Remove Kernel_q2/Kernel_q5 and N-shaped unrolls — keep general AVET range pushdown and const-drive + N dense merges. Add a physical-query result cache (DATASCRIPT_QUERY_RESULT_CACHE) and DATASCRIPT_QUERY_DEBUG traces. Shared benches now report *-nocache / warm so Datalevin’s ~0.005ms is not mistaken for engine cost. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Hash join no longer drops duplicate join keys (needed for value joins). Keep single entity-group fused execute — multi-group hash-join was much slower than the specialized cross-entity path for q5. Compare script prefers *-nocache timings for engine vs Datahike. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
7 tasks
cursor
Bot
merged commit Aug 30, 2026
e3def84
into
logseq/shared-api-parity-fe5d
1 check was pending
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.
Summary
result-edn.tx_data+ batched index writes (EAVT/AEVT/AVET/TAVE all written); 20kbuild-ms≈ 1.5s.fold_index_range(qpred1/q-pred-range)q3/q-5-merge); specialized cross-entity value join forq5(multi-group hash-join was slower)DATASCRIPT_QUERY_RESULT_CACHE=0/*-nocachefor engine timing)DATASCRIPT_QUERY_DEBUG=1Shared people @ 20k (nocache engine path)
EDN matches Datahike and Datalevin. Every query faster than Datahike.
Test plan
test_shared_queries+test_query_exec_parity