feat(8.0): temporal range verbs — diff, history, since(gen|Date), asOf{exclusive}, transactionLog window
asOf() answers "state AT a point"; these answer "what happened BETWEEN two
points" and "one entity's whole history", all on the existing generation records.
- Extract resolveAsOfGeneration() as the shared gen|Date→{generation,timestamp}
chokepoint (reachability + Date semantics identical everywhere). asOf()'s
inclusive path is byte-identical — db-mvcc still 25/25.
- asOf(target, { exclusive }) — strict-before (resolved−1, clamped at gen 0,
never a RangeError).
- db.since(Db | generation | Date) — overload; number/Date resolve via the shared
resolver (new DbHost.resolveGeneration); EXCLUSIVE lower bound;
since(db) === since(db.generation). Same-store guard via Db.belongsToStore.
- brain.diff(a, b) → { added, removed, modified } split by nouns/verbs. EARNS its
name: candidate set is ONLY changedBetween(gLow,gHigh), each classified by
existence at both endpoints + a key-order-insensitive value compare
(new src/db/stableEqual.ts) — a touched-but-reverted / born-and-died id is in
no bucket.
- brain.history(id, { from, to }) → every distinct version oldest→newest, each
value === asOf(version.generation).get(id); null = removal; kind auto-detected
(throws on UUID-space collision). New store helper generationsTouching().
- brain.transactionLog({ from, to, limit }) — INCLUSIVE generation/Date window
(contrast since's exclusive lower bound); limit applied last.
- Compaction policy locked: diff/since THROW GenerationCompactedError below the
horizon; history TRUNCATES to it.
Types DiffResult/HistoryVersion/EntityHistory exported from the package root.
Tests: tests/integration/db-temporal.test.ts (8 proofs incl. diff-earns-its-name,
history↔asOf cross-check, the composition proof, granularity, compaction contrast)
+ tests/unit/db/stableEqual.test.ts (6). docs/guides/snapshots-and-time-travel.md
+ RELEASES updated. 1477 unit + db-temporal 8 + db-mvcc 25 green.
This commit is contained in:
parent
373a48122d
commit
2c84f86815
10 changed files with 1069 additions and 37 deletions
27
RELEASES.md
27
RELEASES.md
|
|
@ -51,9 +51,11 @@ What you get:
|
|||
on conflict) — the big sibling of the per-entity `ifRev` CAS from 7.31.
|
||||
`meta` is reified Datomic-style into an append-only transaction log,
|
||||
readable via `brain.transactionLog()`.
|
||||
- **`brain.asOf(generation | Date | snapshotPath)`** — time travel with the
|
||||
**full query surface**: `get()`, `find()` in every mode, semantic search,
|
||||
graph traversal, cursors, aggregation, all at the pinned past state.
|
||||
- **`brain.asOf(generation | Date | snapshotPath, { exclusive? })`** — time
|
||||
travel with the **full query surface**: `get()`, `find()` in every mode,
|
||||
semantic search, graph traversal, cursors, aggregation, all at the pinned past
|
||||
state. `exclusive: true` pins the generation immediately before the target
|
||||
(strict-before).
|
||||
- **`db.with(ops)`** — speculative writes applied in memory on top of a view.
|
||||
Nothing touches disk, the generation counter, or the indexes. What-if
|
||||
analysis, then `transact()` the same ops for real.
|
||||
|
|
@ -63,10 +65,23 @@ What you get:
|
|||
inodes). `brain.restore(path, { confirm: true })` replaces the whole store
|
||||
from one; `Brainy.load(path)` opens one read-only with the full query
|
||||
surface.
|
||||
- **`db.since(olderDb)`** — exactly the entity and relationship ids that
|
||||
committed transactions touched between two views.
|
||||
- **Range verbs over history** — answer "what happened BETWEEN two points":
|
||||
- **`db.since(Db | generation | Date)`** — the entity and relationship ids
|
||||
committed transactions touched after an **exclusive** lower bound (now
|
||||
accepts a generation or `Date`, not just a prior `Db`).
|
||||
- **`brain.diff(a, b)`** — the touched ids CLASSIFIED as `{ added, removed,
|
||||
modified }` (split by nouns/verbs) by resolving each at both endpoints; a
|
||||
touched-but-reverted id lands in none of the buckets. Endpoints are a
|
||||
generation, `Date`, or `Db`, in either order.
|
||||
- **`brain.history(id, { from?, to? })`** — every distinct version of ONE
|
||||
entity/relationship over a range, oldest first (`value: null` marks a
|
||||
removal); each version equals `asOf(version.generation).get(id)`.
|
||||
- **`brain.transactionLog({ from?, to?, limit? })`** — the commit log over an
|
||||
**inclusive** generation/`Date` window (contrast `since`'s exclusive lower
|
||||
bound); `limit` applies last.
|
||||
- **`brain.compactHistory({ retainGenerations, retainMs })`** — explicit
|
||||
retention. Compaction never breaks a pinned read.
|
||||
retention. Compaction never breaks a pinned read. `diff`/`since` throw
|
||||
`GenerationCompactedError` below the horizon; `history` truncates to it.
|
||||
|
||||
The precise guarantees (and the honest limits — history granularity is
|
||||
`transact()` commits; single-operation writes advance the clock but produce no
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue