Architecture¶
Aeon follows a 4+1 architectural model. This page covers the consumer-relevant portions.
Entity Relationships¶
flowchart TD
Schedulable["Host Model (schedulable)"] --> Allocation
Allocation --> Occurrence
Occurrence --> Override["Override (optional)"]
Allocation -. "forks forward" .-> Allocation2["Allocation (superseding)"]
Allocation2 --> Occurrence2["Occurrences (new projections)"]
A host model owns an Allocation. The Projector materializes Occurrences from that Allocation. Overrides layer on top of individual Occurrences. When a schedule changes, Aeon forks — creating a successor Allocation linked by supersedes_allocation_id.
Process Flows¶
Projection¶
- A query arrives requesting a time window
- Aeon loads allocations whose validity overlaps the window
- If
projected_untildoesn't cover the window, projection is enqueued - Known occurrences are returned immediately (may be partial)
- The Projector extends the frontier: lock allocation, expand IceCube rules, upsert occurrences, advance
projected_until
Reads are never blocked on projection. Partial results are returned while projection catches up.
Forking¶
- The host calls
fork_future(pivot:, ...)orfork_all(...) - The Forker locks the old allocation (
FOR UPDATE NOWAIT) - The old allocation is closed:
valid_to = pivot - A successor allocation is created with lineage (
supersedes_allocation_id) - Future occurrences from the old allocation are invalidated via set-based SQL
- The successor is projected inline for the configured buffer
No history is rewritten. Prior predictions become invalidated, not silently altered.
Override¶
- The host calls
override_occurrence(starts_at:, ...) - The OverrideApplier locates the target occurrence
- An Override row is created (cancel or replacement time range)
- No projections are regenerated
Overrides are surgical. Forking is structural.
Database Design¶
All Aeon tables live in a dedicated PostgreSQL schema: wt_aeon.
Tables¶
| Table | Purpose |
|---|---|
wt_aeon.allocations |
Immutable temporal laws with polymorphic attachment |
wt_aeon.occurrences |
Materialized predictions with tstzrange and GiST index |
wt_aeon.overrides |
Surgical single-instance deviations |
Key Indexes¶
| Index | Type | Purpose |
|---|---|---|
time_range on occurrences |
GiST | Efficient temporal range queries via && operator |
(allocation_id, starts_at) on occurrences |
Unique | Idempotent projection (INSERT ON CONFLICT DO NOTHING) |
| Active occurrences partial index | B-tree | Fast lookup of non-invalidated, non-purged rows |
| Active allocations partial index | Unique | One active allocation per schedulable (valid_to IS NULL) |
Design Choices¶
- UUID primary keys everywhere — all IDs, FKs, and polymorphic references are UUIDs
tstzrangeas canonical span — PostgreSQL's native range type enables GiST indexing and&&overlap queriestimestamptzfor all timestamps — UTC everywhere, no timezone ambiguity- Schema format
:sql—structure.sqlfaithfully captures PG schema, GiST indexes, partial indexes, and tstzrange columns thatschema.rbcannot represent - Dedicated PG schema —
wt_aeonisolates engine tables from the host application
Internal Services¶
| Service | Responsibility |
|---|---|
Projector |
IceCube expansion into upserted occurrence rows |
Forker |
Forward-only timeline forking (close + create + invalidate + project) |
OverrideApplier |
Surgical single-occurrence override creation |
Disposer |
Policy-driven purge of invalidated occurrences (scaffolded, not yet implemented) |