step 01 — pager-and-rows
Goal
Build the in-memory primary container. By the end of this step you should have:
- a
Row { v, tag, created_at, deleted_at }value type, - a
Connwithnext_txidstarting at 1 and an orderedprimary: i64 -> Rowmap, exec_insert(UPSERT) bumpingnext_txidevery call,select_by_kreturning live rows only.
Why "pager-and-rows" not just "rows"
Even though we are not implementing a real on-disk pager here, the discipline of treating the primary map as the single source of truth for both visible and tombstoned state mirrors what a pager gives you: a flat, ordered store you can walk in key order.
If you wanted, you could later swap the in-memory std::map for a
B-tree built on top of db-11's pager and not have to change anything
else in this lab.
Tasks
- Define
RowandConnin your chosen language. - Implement
exec_insertwith UPSERT semantics. Make sure that inserting at the same key twice replaces the row and uses the new txid increated_at. - Implement
select_by_k. It must filter out tombstoned rows. - Write a unit test that inserts two keys, selects them back, and
asserts
next_txid == 3.
Pitfalls
- If you use a hash map (Go
map, C++unordered_map), the wire test in step 03 will fail because iteration order will not be deterministic. Use an ordered map (BTreeMap, sorted-keys iteration,std::map). - Use
i64fork.i32will silently truncate when the workload in step 03 mods au64bykeysand casts.