booblik

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 applied count must be at least as large as the count before the restart check.py:204-207.
  • View Consistency: The sum of all events across all users must exactly match the total number of applied events check.py:181-184.
  • User Integrity: For any specific user, the sum of counts in the actions map must equal the total events count for that user check.py:224-226.

Key files

FileLinesWhat is there
…/projection/View.kt10-22Event and UserView data classes
…/projection/View.kt39-77Projection class containing the state and apply logic
…/projection/Main.kt44-77main function setting up the Ktor server and routes
…/projection/Main.kt79-143build function managing the replay and follow loops
…/projection/Main.kt145-188Progress and ProjectionStats for monitoring

Behaviour that surprising

  • The apply function in Projection uses ConcurrentHashMap.compute to ensure thread-safe updates to user views during the follow phase View.kt:55.
  • The decode function in View.kt uses runCatching to silently skip unreadable records, incrementing a skipped counter instead of crashing the projection View.kt:48-53.
  • The top function in Projection performs a full sort of all users in memory to return the requested limit View.kt:71.

On this page