Expand description
The index image: what a checkpoint points at so an index does not have to be rebuilt from the records it was built from.
CheckpointEntry::index_image_addr
has been in the superblock since the format was first written down and
nothing has pointed at anything yet. This is the first thing it points at,
and it is the vector index, because the vector index is the one that costs
real money to rebuild: a million vectors is a million quantisations and a few
thousand partitions that arrived at their shape through a long sequence of
splits, merges and sweeps. Replaying the records gives back the vectors. It
does not give back the shape, and rebuilding the shape on open is the outage
the whole update protocol exists to avoid.
§It is chunks, not a record kind
06 fixes the record kinds and there is no kind for an index, which is
deliberate: an index is derived, so a reader that has never heard of it must
be able to walk straight past it. So an image is written as
CollectionChunk records through the
chain in yo-kv, exactly the way a demoted collection is, and the only thing
that knows the chunks mean an index is the checkpoint entry that points at
them. A reader that ignores the checkpoint sees a run of chunks nobody claims
and compaction drops them.
10 section 2 is where the shape comes from: a partition is a natural chunk.
At the default posting size and 768 dimensions a partition is about 32 KiB,
which is half of one chunk, so a partition is one chunk and one read almost
always, and a partition that has grown past a chunk is a chain of its own
rather than a special case.
root one per partition
+------------------+ +---------------------------+
| header, 100 bytes | | count | code_bytes | stuck |
+------------------+ +---------------------------+
| centroid chain | | ids count * 8 |
| key chain | | tags count * 8 |
+------------------+ | codes count * width |
| partition 0 | -----> | meta count * 16 |
| partition 1 | +---------------------------+
| ... |
+------------------+The four arrays inside a partition are separate runs rather than one run of structures, and that is the same reason the posting itself is laid out that way in memory: a scan that only wants the tags reads only the tags. A cold partition can be brought in one array at a time for the same reason.
§What is not in here
The vectors. They are records of kind 3 already (crate::vector), at
addresses the log resolves, and G8’s budget is 96 bytes of index for a 768
dimensional vector with the raw copy in the log. An image that carried them
as well would write every vector twice and spend the whole gate to save a
walk. So loading an image is two halves: the image gives the shape and the
codes, and the log gives the vectors back under the keys the image names.
No checksum either, and that is not an omission. Every chunk of an image is a record, every record carries a CRC32C over its own bytes, and the chain’s directory is a record too, so a second checksum inside the image would cover bytes that are already covered. What a checksum cannot catch is an image that is intact and stale, and that is what the checkpoint’s log addresses are for.
§The freeze
This layout is frozen with the rest of the format at the end of M6. After
that the only lever is min_reader_version (07 section 9), so the fields
that exist to be changed later exist now: ImageHeader::kind so that a
document or graph index can have an image beside this one, flags in both
headers so that a section can be added to an image a version one reader then
refuses one image at a time, and bits and metric as their own bytes
rather than as something a reader has to infer.
An image is a cache in the end, which is the safety net under all of it: a reader that does not like an image can throw it away and rebuild from the records, slowly and correctly.
Modules§
- image_
kind - What kind of index an image holds.
- metric
- How a vector is compared, as the byte the image stores.
Structs§
- Chain
- Where a section went and how long it is.
- Image
Header - The root of an image: everything the index is, apart from the members.
- Keys
- The key table, one entry at a time.
- Posting
Header - The fixed part at the front of one partition’s image.
Constants§
- IMAGE_
HEADER_ LEN - The fixed part at the front of an image root.
- IMAGE_
TAG - The four bytes an image starts with, so that a stray chunk is not read as one.
- META_
LEN - What a code needs beside it:
norm,scale,loanddelta, fourf32. - PARTITION_
ENTRY_ LEN - One line of the partition table that follows the root header.
- POSTING_
HEADER_ LEN - The fixed part at the front of one partition’s image.
Functions§
- get_
floats - Reads floats back out of
bytesintoout, which says how many. - get_
partition - Reads partition
i’s chain back out of an encoded root. - image_
len - How long an image root with this many partitions is.
- key_
entry_ len - How many bytes a key of
klentakes in the key table. - posting_
len - How long one partition’s image is.
- put_
floats - Writes floats into
intoend to end and says how many bytes that took. - put_key
- Writes one key table entry and says how long it was.
- put_
partition - Writes partition
i’s chain into an encoded root.