A persistent index has to reopen from its file. Today you write Open - it reads the meta page, checks the magic, and reconstructs the tree pointing at the stored root - and Close, which writes the current meta back.
Open an existing tree file by reading its meta page and pointing the tree at the stored root, and Close by writing the meta.
Open is the inverse of everything the file pager does: point at a file, read page
0, confirm the magic, and lift out the root, freeHead, and pageCount to
reconstruct a tree that resumes exactly where the last one left off. The tree needs
nothing else - the root id is the single thread you pull to reach every node, all of
which are already on disk in their pages.
Close is the other half: it writes the current meta back so the next Open finds
the right root. Whenever the tree’s root changes - a root split grows one, a
collapse replaces one - that new root id must reach the meta, and for a clean
shutdown, writing it at Close is enough. What Close does not yet guarantee is
survival of a crash mid-write, where the process never reaches Close at all -
that is the entire next chapter. First, prove the clean round-trip.
func Open(path string) (*Tree, error) {// open (create if missing) the file// brand-new/empty file: bootstrap it - NewTree over the pager makes// an empty leaf root (page 1), then writeMeta the initial header// existing file: read meta (page 0); check magic -> error if wrong// root, freeHead, pageCount := readMeta(...)// return &Tree{pager: filePager{f, pageCount, freeHead}, root: root}}func (t *Tree) Close() error {// writeMeta(current root, freeHead, pageCount); close the file}