Projection
Generated page
Model gemma-mtp, commit f508a4b65b3f, 2026-08-15, sources: 6. Edit the code or the hand-written documentation instead.
Documentation for the Projection module, focusing on the read model implementation and its verification.
Diagram
The Projection lifecycle
The lifecycle of a projection is split into two distinct phases to ensure the view is both complete and current. It begins with a replay() phase that reads from the Earliest position to build the initial state, and transitions to a follow() phase once the history has been exhausted Main.kt:98-110.
The Projection state machine
The state is managed within the Projection class, where the apply function performs a functional fold over the incoming stream View.kt:44-67. The UserView is updated by merging new actions into the existing map and incrementing event counts. Crucially, the correctness of lastAction depends on the fact that all events for a single user are routed to the same partition, ensuring they arrive in order View.kt:34-37.
Recovery after a crash
The design follows a "persist neither" philosophy to avoid silent corruption. Because the Projection does not persist its position to a volume, a restart forces a complete rebuild from the beginning of the log View.kt:27-32. This ensures that the view is always a complete reflection of the log, rather than a potentially truncated view that resumes from a stale offset README.md:140-144.
Verification of the read model
The verification suite, primarily implemented in dev/check-projection.sh, asserts several invariants:
- Rebuild Completeness: After a restart, the
appliedcount must be at least as large as the count before the restartcheck.py:204-207. - View Consistency: The sum of all
eventsacross all users must exactly match the total number ofappliedeventscheck.py:181-184. - User Integrity: For any specific user, the sum of counts in the
actionsmap must equal the totaleventscount for that usercheck.py:224-226.
Key files
| File | Lines | What is there |
|---|---|---|
…/projection/View.kt | 10-22 | Event and UserView data classes |
…/projection/View.kt | 39-77 | Projection class containing the state and apply logic |
…/projection/Main.kt | 44-77 | main function setting up the Ktor server and routes |
…/projection/Main.kt | 79-143 | build function managing the replay and follow loops |
…/projection/Main.kt | 145-188 | Progress and ProjectionStats for monitoring |
Behaviour that surprising
- The
applyfunction inProjectionusesConcurrentHashMap.computeto ensure thread-safe updates to user views during thefollowphaseView.kt:55. - The
decodefunction inView.ktusesrunCatchingto silently skip unreadable records, incrementing askippedcounter instead of crashing the projectionView.kt:48-53. - The
topfunction inProjectionperforms a full sort of all users in memory to return the requested limitView.kt:71.