Skip to content
2 changes: 1 addition & 1 deletion docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The engine has no dependencies outside `java.base`. All three modules build into
| `engine.txn` | `TransactionManager` (commit lock, append, publication, pins, history), `Transaction` (op log, read sets, rebase), `Snapshot`, `View` (read API shared by both), `Workspace` (mutable root vector plus derivations), `GroupCommitter`, `Op`/`EdgeAction`, `Derivation`/`EngineDerivations`, `CrashPoint`, `Durability`, `WalMode`, `Isolation` and `TxnOptions`. |
| `engine.wal` | `WriteAheadLog` (segmented, checksummed records) and `WalRecord` (`Begin`, `Page`, `PageRef`, `Root`, `BranchMeta`, `Feed`, `Commit`, `Abort`, `Checkpoint`). |
| `engine.feed` | `ChangeFeed` (segmented append-only `feed/*.feed` files, sparse generation index, retention, tailing subscriptions), `CommitEvent` and `FeedCodec`; see [change-feed.md](../storage/change-feed.md). |
| `engine.maintenance` | `Checkpointer`, `Compactor` (liveness, relocation, reclamation) and `Recovery` (catalog discovery, WAL redo, durable-prefix rule). |
| `engine.maintenance` | `Checkpointer`, `Compactor` (liveness, moving live pages out of victim segments, reclamation) and `Recovery` (catalog discovery, WAL redo, durable-prefix rule). |

### `io.hstore.db`

Expand Down
2 changes: 1 addition & 1 deletion docs/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ not what drives HStore's footprint:
* **Disk at scale 4 (0.54×).** Live data is comparable: about 1 GB of live node images in HStore against
1.27 GB in JE. The difference is garbage that has not been reclaimed yet. Both engines are log-structured
with a 50% minimum-utilisation cleaner: HStore's `compaction_live_ratio = 0.5`, JE's
`je.cleaner.minUtilization = 50`. A sealed HStore segment is only relocated once fewer than half of its
`je.cleaner.minUtilization = 50`. A sealed HStore segment is only compacted once fewer than half of its
images are live, and the background pass runs after every `checkpoint_wal_mb` of writes
([maintenance.md](storage/maintenance.md)). Raising `compaction_live_ratio` trades write amplification for
space.
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ max_connections = 200
| `cache_nodes` | `HSTORE_CACHE_NODES` | `65536` | Decoded tree nodes kept in the node cache. |
| `checkpoint_wal_mb` | `HSTORE_CHECKPOINT_WAL_MB` | `256` | WAL volume since the last checkpoint that triggers a background checkpoint. |
| `history_limit` | `HSTORE_HISTORY_LIMIT` | `64` | Committed generations kept addressable for `AT GENERATION`, `AS OF`, `HISTORY` and the Studio time slider. |
| `compaction_live_ratio` | `HSTORE_COMPACTION_LIVE_RATIO` | `0.5` | Sealed segments whose live bytes fall below this fraction of their allocated bytes are relocated by compaction: one segment (the emptiest) per background pass after every `checkpoint_wal_mb` of written node images, or all of them with `COMPACT`. Time-travel history is preserved; retired segments are deleted once `history_limit` has rolled past them. Must satisfy 0 < r < 1; other values fail at startup. |
| `compaction_live_ratio` | `HSTORE_COMPACTION_LIVE_RATIO` | `0.5` | Sealed segments whose live bytes fall below this fraction of their allocated bytes are emptied by compaction, which moves their live pages to the active segment: one segment (the emptiest) per background pass after every `checkpoint_wal_mb` of written node images, or all of them with `COMPACT`. Time-travel history is preserved. Emptied segments are deleted after the next checkpoint, once no reader that started before the move is still open. Must satisfy 0 < r < 1; other values fail at startup. |
| `feed_retention_generations` | `HSTORE_FEED_RETENTION_GENERATIONS` | `100000` | Committed generations kept in the [change feed](../storage/change-feed.md) for `HISTORY`, `DIFF` and subscribers. After every checkpoint, whole feed segments that lie entirely below `current − N` are deleted. *Holds* lower that floor: the semantic index's last persisted generation and the generation of each continuous materialised view. Feed segments roll at the WAL segment size (`EngineOptions.walSegmentBytes`, 64 MiB). Must be ≥ 1. |

### Resource budgets
Expand Down
4 changes: 2 additions & 2 deletions docs/operations/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ This is every message the server emits. Placeholders are in `{braces}`.

| Severity | Component | Message |
|---|---|---|
| LOG | engine | `background compaction relocated segments {list}` (write-volume-triggered pass) |
| LOG | engine | `compaction relocated segments {list} and reclaimed {list}` |
| LOG | engine | `background compaction moved the live pages out of segments {list}` (write-volume-triggered pass) |
| LOG | engine | `compaction moved the live pages out of segments {list} and reclaimed {list}` |
| LOG | engine | `reclaimed retired segments {list}` |
| WARNING | hstore | `background maintenance failed` (with DETAIL) |
| LOG | txn | `created branch {name} (#{id}) from generation {g}` |
Expand Down
4 changes: 2 additions & 2 deletions docs/storage/catalog-and-generations.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Published generations are kept in `TransactionManager.history` (a `ConcurrentSki
* `snapshotAt(generation, branch)` pins the generation and opens a snapshot over its root vector. A generation that has been trimmed fails with `generation N is no longer retained`.
* `generationAsOf(wallTime)` returns the newest retained generation with `wallTime <= t`.

Time travel needs no undo log. Copy-on-write never overwrites a page, so an old generation's root vector still points at valid pages, and reading the past is exactly as fast as reading the present. A historical page is only reclaimed once compaction has relocated the live data *and* both history trimming (`history_limit`) and every pinned reader have moved past the compaction generation ([maintenance.md](maintenance.md#reclaim)). Compaction therefore never shortens the time-travel window.
Time travel needs no undo log. Copy-on-write never overwrites a page, so an old generation's root vector still points at valid pages, and reading the past is exactly as fast as reading the present. Compaction moves the pages a retained generation still needs and keeps their page numbers, so old root vectors stay valid after their segments are deleted ([maintenance.md](maintenance.md#reclaim)). Compaction therefore never shortens the time-travel window.

The retained history is also persisted. `Checkpointer` writes up to `historyLimit` past generations into the catalog image, and `Recovery` appends every replayed generation, so time travel survives restarts.

Expand All @@ -124,7 +124,7 @@ Branches are named, independently writable lines of generations that share pages
| merge | `markMerged(id)` → `closeBranch(id, MERGED)` | Sets the state to `MERGED` **and empties its roots**, so its pages are no longer reachable from the current generation. The actual three-way merge of contents happens in `db.temporal.BranchMerge` before this call. |
| drop | `dropBranch(id)` → `closeBranch(id, DROPPED)` | The same, with state `DROPPED`. `Generation.branch` rejects a dropped id. |

`main` (id 0) cannot be closed. `StorageEngine.branches()` lists only `ACTIVE` branches. Because closed branches carry empty root vectors, compaction only has to relocate the active ones.
`main` (id 0) cannot be closed. `StorageEngine.branches()` lists only `ACTIVE` branches. Because closed branches carry empty root vectors, they keep no pages alive.

```sql
CREATE BRANCH what_if;
Expand Down
2 changes: 1 addition & 1 deletion docs/storage/change-feed.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ for each flagged column, in that order:

A column is flagged only when it differs from the default (`roleSet != NO_ROLES`, `weight != Weight.ONE`, `validFrom != Long.MIN_VALUE`, `validTo != Long.MAX_VALUE`, `dataRef != 0`, `qualifier != 0`), so a plain membership costs 1 byte of flags plus the member id.

Slot values that contain tree references (codecs with `holdsRefs()`, for example an `EdgeRecord` with member and order roots) are **detached** before encoding: every embedded `Ref` is replaced with `Ref.EMPTY`. Feed events therefore never pin pages, and compaction may relocate or reclaim any page without invalidating the feed. Consumers that need the topology of an edge read it from a snapshot, or use the event's `members` list.
Slot values that contain tree references (codecs with `holdsRefs()`, for example an `EdgeRecord` with member and order roots) are **detached** before encoding: every embedded `Ref` is replaced with `Ref.EMPTY`. Feed events therefore never pin pages, and compaction may move or reclaim any page without invalidating the feed. Consumers that need the topology of an edge read it from a snapshot, or use the event's `members` list.

### Example

Expand Down
Loading
Loading