Temporal (Point-in-Time) Index Query
Reconstructs which entities held a key's value at a past TSN by replaying the index's append-only version history.
Status: π§ Partial Β· Visibility: Internal Β· Category: Indexing
π― What it solves
Current-state secondary indexes only ever answer "who matches this value now". Once a matching entity's field
changes or the entity is deleted, the old membership fact is gone from the index β there is no way to ask "which
entities had Status = Active as of last Tuesday's snapshot" without falling back to a full scan and walking every
candidate entity's revision chain by hand. Temporal index query answers that question directly off the index
itself: given a key and a historical TSN, it returns exactly the entities that were associated with that key at
that moment, in time proportional to how often the key's membership has changed rather than how many entities
exist.
βοΈ How it works (in brief)
Versioned secondary indexes already record, for every AllowMultiple key, an
append-only TAIL log of (ChainId, TSN, Active/Tombstone) transitions alongside the current-state HEAD set. A
temporal query looks up the key's TAIL buffer, scans every entry with TSN <= targetTSN, and keeps only the
most-recent entry per chain ID β chains whose latest qualifying state is Active are the answer. If the key has
never been mutated since it was created (no TAIL buffer allocated yet), the current HEAD set is returned directly,
since it has been correct for every TSN since the key's creation.
π» Usage
There is no public entry point yet β TemporalIndexQuery, the IndexedFieldInfo/TailVSBS it reads, and
VersionedIndexEntry are all internal types with no InternalsVisibleTo grant to application assemblies, and no
Transaction/query-builder method calls into them. The shape below is the actual call the engine's own test suite
exercises β shown to illustrate the contract, not as code an application can compile against today:
// Engine-internal only β illustrative, not callable from application code.
var ifi = componentTable.IndexedFieldInfos[fieldIndex]; // the AllowMultiple field's index metadata
float key = 1.0f;
List<int> chainIdsActiveAtTsn = TemporalIndexQuery.Query(
ifi, (byte*)&key, targetTSN: snapshotTsn, componentTable.TailVSBS, changeSet: null);
// Results are revision-chain handles, not EntityIds β resolving a chain ID back to an
// entity/component is also internal-only today.
β οΈ Guarantees & limits
- No public API surface yet. Nothing in
Transaction, the query builder, orPointInTimeAccessorcalls this today β it is verified, tested engine plumbing without an application-facing entry point. - Applies only to
[Index(AllowMultiple = true)]fields onVersionedcomponents β the same scope as the TAIL history it reads;SingleVersion/Transientindexes carry no TAIL to query. - Returns revision-chain IDs, not
EntityIds β turning a chain ID into the entity/component data as of the target TSN is a separate step with no public surface either. - Cost is O(H) per query, where H is the total number of TAIL transitions ever recorded for that key β not the number of entities currently or historically matching it. A key churned by frequent updates costs more to query than a stable one, independent of result size.
- A boundary-sentinel-preserving TAIL pruning algorithm exists but isn't wired to an automatic trigger yet, so TAIL history is effectively unbounded today; there is also no "target TSN older than retained history" error path β that case is only meaningful once pruning is actually scheduled.
- Chain ID reuse is handled correctly: if an old entity is deleted and a new one is later created reusing the same chain ID under the same key, the Tombstone between them separates the two in the TAIL stream, so a query never conflates them β but it does mean chain-ID-level history isn't the same thing as one entity's history.
π§ͺ Tests
- VersionedIndexTests β Phase 3
Temporal Query Testsregion:TemporalQuery_NoMutation_FallsBackToHead,TemporalQuery_AtCreate_ReturnsEntity,TemporalQuery_OldKey_AfterUpdate_SingleEntity_StillVisible/_MultiEntity_CorrectCount,TemporalQuery_AfterBackfill_ReturnsCorrectSnapshot
π Related
- Sibling feature: Versioned (HEAD/TAIL) Secondary Indexes for MVCC
- Sibling feature: Multi-value secondary index (AllowMultiple)
- Source:
src/Typhon.Engine/Indexing/internals/TemporalIndexQuery.cs,src/Typhon.Engine/Indexing/internals/TailGarbageCollector.cs