Documentation
¶
Overview ¶
Package format is the .meguri single-file container: the 64-byte header, the five regions (URL table, host table, schedule index, seen-set filter, string and blob), and the footer written last, bracketed by the magic MEG1 at both ends.
A .meguri file is one partition's entire frontier state. It is at once the durable checkpoint, the redistribution unit when a partition moves or rebalances, and the cold archive (D1, D12). The footer comes last so one read of the file tail learns the whole structure, which is what makes object-store redistribution cheap.
This package owns the bytes. The logical schema it serializes (URLRecord, HostRecord, and the rest) lives in the top-level meguri package, and a checkpoint writes those records straight into columns with no remapping. The writer is deterministic: given the same records and the same creation timestamp it produces byte-identical files, the property every golden-file test and content-addressed redistribution leans on.
M0 lands the container with RAW column encoding plus an optional zstd block codec. The richer tatami encoding cascade (dictionary, delta, frame of reference, FSST) slots in behind the same page and directory structure in later milestones without changing the container.
Index ¶
- Constants
- Variables
- func Encode(p *Partition) ([]byte, error)
- func EncodeManifest(m *Manifest) []byte
- func EncodeToFile(path string, p *Partition) (err error)
- func PackRobots(blob []byte, codec uint8) []byte
- func StreamEncodeToFile(path string, src URLRecordSource, maxRows int, p *Partition, tmpDir string) (err error)
- func StreamURLRegion(w io.Writer, src URLRecordSource, regionStart uint64, maxRows int, codec uint8, ...) ([]columnDir, uint64, uint32, error)
- func UnpackRobots(packed []byte, codec uint8, sizeHint int) ([]byte, bool)
- type ArenaRandReader
- type ArenaSeqReader
- type DueCursor
- type FSSTArena
- type Header
- type Inspect
- type Manifest
- type ManifestEntry
- type Partition
- func Compact(p *Partition) *Partition
- func Decode(b []byte) (*Partition, error)
- func Extract(p *Partition, moving map[uint64]bool) (out, rest *Partition)
- func Merge(a, b *Partition) (*Partition, error)
- func Pack(id uint32, hostKeyLo, hostKeyHi uint64, createdHours uint32, codec uint8, ...) *Partition
- func Split(p *Partition, atHostKey uint64) (lo, hi *Partition)
- type Reader
- func (r *Reader) ArenaRandReader() (*ArenaRandReader, error)
- func (r *Reader) ArenaSeqReader() *ArenaSeqReader
- func (r *Reader) ContainsURL(key m.URLKey) (bool, error)
- func (r *Reader) DueByWheel(now uint32) ([]m.URLKey, error)
- func (r *Reader) DueCursor(now uint32) (*DueCursor, error)
- func (r *Reader) DueKeys(now uint32) ([]m.URLKey, error)
- func (r *Reader) DueRange() (min, max uint32)
- func (r *Reader) EnableKeyCache(capPages int)
- func (r *Reader) HasSchedule() bool
- func (r *Reader) Header() Header
- func (r *Reader) HostCount() int
- func (r *Reader) HostKeyRange() (lo, hi uint64)
- func (r *Reader) HostRangePageScan(lo, hi uint64) (scanned, total int)
- func (r *Reader) HostRangeURLKeys(lo, hi uint64) ([]m.URLKey, error)
- func (r *Reader) Hosts() ([]m.HostRecord, error)
- func (r *Reader) LookupURL(key m.URLKey) (m.URLRecord, bool, error)
- func (r *Reader) MaybeDueAt(now uint32) bool
- func (r *Reader) MaybeOwnsHost(hostKey uint64) bool
- func (r *Reader) NextDue() ([]uint32, error)
- func (r *Reader) Schedule() (*ScheduleIndex, error)
- func (r *Reader) SeenFilter() ([]byte, error)
- func (r *Reader) URLCount() int
- func (r *Reader) URLKeys() ([]m.URLKey, error)
- func (r *Reader) URLRows() (*URLRowCursor, error)
- func (r *Reader) URLZone(col int) (min, max uint64, ok bool)
- type RegionInfo
- type ScheduleIndex
- type Stats
- type URLRecordSource
- type URLRowCursor
Constants ¶
const ( VersionMajor uint16 = 1 VersionMinor uint16 = 0 )
Format version. Major changes break the layout; minor changes are additive.
const ( ChecksumNone uint8 = 0 ChecksumCRC32C uint8 = 1 ChecksumXXH64 uint8 = 2 )
Checksum algorithm selectors, stored in the header's checksum_algo byte.
const ( CodecNone uint8 = 0 CodecLZ4 uint8 = 1 // reserved, not wired in M0 CodecZstd uint8 = 2 CodecZstdDict uint8 = 3 // reserved, not wired in M0 )
Block codec ids, stored in the header's default_codec byte and per page. The values match tatami's codec enum so a shared decoder can serve both formats.
const ( EncRaw uint8 = 0 EncDict uint8 = 1 EncDelta uint8 = 2 EncFOR uint8 = 3 EncRLE uint8 = 4 EncFSST uint8 = 5 EncDeltaFOR uint8 = 6 // EncFrontCode marks a blob page whose payload is front-coded: each string is // stored as the shared-prefix length with the previous string in the page plus // the literal suffix, the first string of every page a restart (shared 0). It is // a meguri extension past tatami's enum (Spec 2074 M1), reversed back to the raw // arena bytes on decode so the *Ref offsets are unchanged. EncFrontCode uint8 = 7 )
Column encoding ids, stored per page and as the dominant encoding per column. The values match tatami's encoding enum. M0 writes RAW only; the others are decoded-ready placeholders for later milestones.
const ( FlagSorted uint16 = 1 << 0 FlagHasSchedule uint16 = 1 << 1 FlagHasSeenset uint16 = 1 << 2 FlagHasBlob uint16 = 1 << 3 FlagSeensetIsRibbon uint16 = 1 << 4 FlagHasMPHF uint16 = 1 << 5 // FlagBlobFrontCoded records that the string blob region's pages are front-coded // (EncFrontCode) rather than raw. A reader that does not know the layout rejects // the file rather than misresolving a ref against bytes it cannot reverse. FlagBlobFrontCoded uint16 = 1 << 7 )
Header flag bits.
const ( RegionURLTable uint8 = 0 RegionHostTable uint8 = 1 RegionSchedule uint8 = 2 RegionSeenset uint8 = 3 RegionStringBlob uint8 = 4 )
Region ids, the fixed order regions appear in a .meguri file.
const ( PageData uint8 = 0 PageDict uint8 = 1 PageIndex uint8 = 2 PageBlob uint8 = 3 PageFilter uint8 = 4 PageSchedule uint8 = 5 )
Page kinds.
const ( ColURLHostKey = colURLHostKey ColURLPathKey = colURLPathKey ColURLStatus = colURLStatus ColURLNextDue = colURLNextDue ColURLHTTPStatus = colURLHTTPStatus ColURLCrawlCount = colURLCrawlCount ColURLLastCrawled = colURLLastCrawled )
Exported URL column ids, the stable schema positions a projection names. They match the on-disk column directory and never change once shipped (doc 10 section 4, the URL table columns).
const HeaderSize = 64
HeaderSize is the fixed size of the .meguri header at offset 0.
const PageHeaderSize = 32
PageHeaderSize is the fixed, uncompressed page header size.
const RobotsSizeHint = 1 << 20
RobotsSizeHint bounds an unpacked robots blob. A robots.txt that matters for crawl scheduling is small; a blob claiming to inflate past this is treated as corrupt rather than allocated.
Variables ¶
var ( ErrBadMagic = errors.New("meguri/format: bad magic") ErrShortFile = errors.New("meguri/format: file too short") ErrCorrupt = errors.New("meguri/format: corrupt file") ErrChecksum = errors.New("meguri/format: checksum mismatch") ErrUnsupported = errors.New("meguri/format: unsupported version") ErrNotSorted = errors.New("meguri/format: url records not sorted by urlkey") )
Sentinel errors a caller can match with errors.Is.
var ErrArenaBackward = errors.New("format: arena ref moved backward")
ErrArenaBackward is returned by ArenaSeqReader.At when a ref moves backward, which breaks the ascending-access contract the sequential reader relies on.
var Magic = [4]byte{'M', 'E', 'G', '1'}
Magic brackets a .meguri file at both ends.
var ManifestMagic = [4]byte{'M', 'G', 'M', '1'}
ManifestMagic brackets a serialized manifest. It is distinct from the file magic so a manifest is never mistaken for a partition file.
Functions ¶
func Encode ¶
Encode serializes a Partition into a complete .meguri file. The output is deterministic: the same Partition value always produces the same bytes, which is what makes a checkpoint diffable and the round-trip gate meaningful. Encode does not sort; it returns ErrNotSorted if the caller's rows are out of order.
func EncodeManifest ¶
EncodeManifest serializes a manifest deterministically: the magic, the entry count, then each entry, then the trailing CRC and magic, mirroring the file's commit discipline so a torn manifest is detectable.
func EncodeToFile ¶ added in v0.2.0
EncodeToFile writes a Partition's complete .meguri file to path, producing the exact bytes Encode produces but streaming one region at a time so the whole image is never held in memory at once. Encode accumulates every region and then a second full-image copy in its output slice; at 100M URLs that doubled image is several GB of avoidable checkpoint transient on top of the record materialization the snapshot already pays (scale doc 12, F4). This builds each region, writes it through a buffered writer, and drops it before building the next, so the resident cost is the largest single region (the URL table or the string blob), not their sum plus a copy. The header's FooterOffset is known only after the body is sized, so a zero placeholder goes down first and the real header is written last with WriteAt at offset 0; everything between is written front to back.
The output is byte-for-byte identical to Encode(p): the same regions in the same order, the same footer, trailer, and header. TestEncodeToFileMatchesEncode pins the equality on the real corpus, so the streaming path can never drift from the in-memory one without the gate catching it.
func PackRobots ¶
PackRobots wraps a robots blob in the smallest of the three modes: the allow-all sentinel for an empty blob, then whichever of raw or codec-compressed is shorter. The first byte is always the mode, so UnpackRobots needs nothing else to reverse it. An empty blob packs to a single sentinel byte.
func StreamEncodeToFile ¶ added in v0.2.0
func StreamEncodeToFile(path string, src URLRecordSource, maxRows int, p *Partition, tmpDir string) (err error)
StreamEncodeToFile writes a complete .meguri file to path, streaming the URL table from src one record at a time instead of taking a materialized p.URLs. It is the bounded-memory checkpoint encoder (spec 2072 D9, 2071 implementation doc 51): the largest region, the URL table, never exists as a record slice or a column-major copy in memory; it is built page by page by StreamURLRegion and spilled to temp files. The host table, string blob, and optional seen-set are small and stay materialized in p, as in EncodeToFile. The output is a valid, byte-stable .meguri file identical to EncodeToFile fed the same records with the same MaxPageRows; the schedule region is not supported here because a checkpoint snapshot never builds one (p.BuildSchedule must be false).
src must yield records in ascending URLKey order (the store's k-way shard merge does); the meter verifies it and the call fails with ErrNotSorted otherwise. maxRows should be > 0 so the per-column page buffers stay bounded; a zero keeps the single-page layout and reintroduces the O(rows) buffer the streaming exists to avoid.
func StreamURLRegion ¶ added in v0.2.0
func StreamURLRegion(w io.Writer, src URLRecordSource, regionStart uint64, maxRows int, codec uint8, tmpDir string) ([]columnDir, uint64, uint32, error)
StreamURLRegion encodes the URL column region by pulling records from src one at a time, spilling each column's pages to a temp file under tmpDir, then concatenating the temp files in column order to w. It produces byte-identical output to encodeColumnRegion(urlColumns(allRecords, codec), regionStart, maxRows): same page boundaries, same per-page buildColumnPage decisions, same directory. The win is residency. It holds at most one page per column (about maxRows*width bytes each) plus one record, never the O(rows) partition slice or the column-major copy that the materializing checkpoint builds (spec 2072 D9, 2071 implementation doc 51). regionStart is the region's absolute file offset so the directory's firstPageOffset is absolute, matching encodeColumnRegion.
maxRows must be > 0 for the residency win: with maxRows <= 0 the format keeps a single page per column and a single page is the whole column, so the last page buffer holds every row. The function stays byte-identical in that case but is no longer bounded; the checkpoint caller passes a bounded maxRows. The fourth return is the CRC32C over the entire region's bytes, which the footer's region descriptor records exactly as Encode/EncodeToFile do for the materialized region.
func UnpackRobots ¶
UnpackRobots reverses PackRobots, returning the original blob and whether the bytes were a well-formed packed blob. A compressed body is bounded by sizeHint so a corrupt length cannot drive an unbounded allocation; pass the codec the blob was packed with. Empty input or an unknown mode reports not-ok.
Types ¶
type ArenaRandReader ¶ added in v0.2.0
type ArenaRandReader struct {
// contains filtered or unexported fields
}
ArenaRandReader resolves arena refs in any order. It decodes the whole string arena once, so its resident cost is the arena size, not one page, and it is the tool for a file whose arena is not key-ordered: an engine checkpoint interns strings in the insertion order URLs were discovered, so a walk in URLKey order hits refs that jump backward, which ArenaSeqReader rejects by contract. A key-ordered, front-coded file (BulkLoad or a compaction wrote it) should use ArenaSeqReader instead, whose transient is one page and so scales to a 100M arena; this reader is for the general unordered file where correctness beats the page bound.
func (*ArenaRandReader) At ¶ added in v0.2.0
func (a *ArenaRandReader) At(ref uint64) ([]byte, error)
At returns the string interned at ref, or nil for the zero sentinel or an out-of-range ref. The slice aliases the decoded arena and stays valid for the reader's life, unlike ArenaSeqReader.At whose result the next call overwrites. The error is always nil; it matches ArenaSeqReader.At's signature so an export can hold either behind one interface.
type ArenaSeqReader ¶ added in v0.2.0
type ArenaSeqReader struct {
// contains filtered or unexported fields
}
ArenaSeqReader resolves string-arena refs in ascending order without holding the whole (possibly multi-gigabyte) arena resident. It is the read side of Stage 2 compaction (spec 2073 doc 08): the compactor walks the base URL table in URLKey order to source each unchanged record's URL string, and BulkLoad interns URL strings in that same key order, so a record's URLRef only ever increases as the cursor advances. This reader decodes the blob region's pages in order and keeps a sliding window of decoded bytes starting at the last resolved ref, so its transient is about one blob page plus the current span, not the arena. The whole-arena decodeBlobRegion is the wrong tool at 100M scale; this is the bounded one.
Host strings live at the low end of the arena (BulkLoad interns hosts before URLs), so a compactor resolves the host table's refs through one reader first, then opens a second reader for the ascending URL walk. Two forward passes, never a random seek.
func (*ArenaSeqReader) At ¶ added in v0.2.0
func (a *ArenaSeqReader) At(ref uint64) ([]byte, error)
At returns the string interned at ref, valid until the next At call. ref must be at or after the previous call's ref (ascending access); a zero ref is the absent sentinel and returns nil. A backward ref returns ErrArenaBackward; a ref or span past the arena returns ErrCorrupt.
type DueCursor ¶ added in v0.2.0
type DueCursor struct {
// contains filtered or unexported fields
}
DueCursor streams the URL rows due at or before a wall-clock hour off the file in bounded batches, so a scheduler pulls the next work without decoding the whole next_due column or materializing every due key at once. It walks the next_due, hostkey, and pathkey columns page by page, uses each next_due page's zone map to skip a page whose soonest due time is still in the future, and emits due keys up to a caller batch cap, remembering its position across calls. The transient is a few decoded pages plus the batch, not the column: at 100M, where DueKeys' whole-column projection plus its every-due-key slice would hold gigabytes, this is the scale-appropriate scheduler read.
func (*DueCursor) NextBatch ¶ added in v0.2.0
NextBatch returns up to limit URLKeys due at or before the cursor's now, in stored (host-key ascending) order, decoding only as many pages as it takes to fill the batch. It returns nil when the scan is exhausted, so a scheduler loops on NextBatch until it gets nil or has dispatched enough. A due row has a nonzero next_due at or before now; a zero next_due is unscheduled and never returned.
type FSSTArena ¶
type FSSTArena struct {
// contains filtered or unexported fields
}
FSSTArena is a per-ref string arena: a shared symbol table and a span region where each string is stored as its own FSST code stream behind a uvarint length. A ref is a byte offset into the spans, and Read decodes just that string from the shared table, the random access the whole-arena zstd blob cannot give. It is the doc 10 section 7 layout: the columns keep byte-offset refs, but a resolve touches one span, not the whole region.
func BuildFSSTArena ¶
BuildFSSTArena trains a table over the strings and lays each one out as an independently-decodable span, returning the arena and the per-string offsets in input order. Offset 0 is the none sentinel (a single zero byte), so a zero ref reads back empty, matching the verbatim arena's convention.
func LoadFSSTArena ¶
LoadFSSTArena reconstructs an arena from its serialized table and span region, the read side a file uses after decoding the two regions off disk.
type Header ¶
type Header struct {
VersionMajor uint16
VersionMinor uint16
PartitionID uint32
Flags uint16
ChecksumAlgo uint8
DefaultCodec uint8
HostKeyLo uint64
HostKeyHi uint64
URLCount uint64
HostCount uint64
CreatedHours uint32
}
Header is the fixed 64-byte file header. It lets a reader sanity-check the file and learn the global facts (the partition's HostKey range, the region offsets, the row counts) without parsing the footer, and it carries the two things a frontier partition file needs that a document shard does not: the partition id and the HostKey range the partition owns.
func DecodeHeader ¶
DecodeHeader parses and verifies a 64-byte header.
type Inspect ¶
type Inspect struct {
PartitionID uint32
VersionMajor uint16
VersionMinor uint16
Flags uint16
ChecksumAlgo uint8
DefaultCodec uint8
CreatedHours uint32
HostKeyLo uint64
HostKeyHi uint64
URLCount uint64
HostCount uint64
FileSize uint64
Regions []RegionInfo
URLColumns int
HostColumns int
Encodings map[string]int // encoding name -> column count, the cascade made visible
Stats Stats
Meta map[string]string
}
Inspect is the cheap structural summary of a .meguri file: everything the header and footer carry, with no column data decoded. It is what the inspect subcommand prints and what a tool reaches for to decide whether a file is worth loading. It is read from the tail, so it costs a header read plus a footer read regardless of file size.
func InspectBytes ¶
InspectBytes builds an Inspect from a whole file in memory. It verifies the header and footer checksums but does not touch the column pages.
type Manifest ¶
type Manifest struct {
Entries []ManifestEntry
}
Manifest is the parsed catalog: the partition entries sorted by HostKeyLo so a route is a binary search.
func BuildManifest ¶
func BuildManifest(entries []ManifestEntry) *Manifest
BuildManifest assembles a manifest from entries, sorting by HostKeyLo so the route lookup is a binary search and the ranges read in order.
func DecodeManifest ¶
DecodeManifest parses what EncodeManifest wrote, verifying the magic at both ends and the body CRC. The entries are returned in their written (sorted) order, so Route works without a re-sort.
func (*Manifest) CoverageGap ¶
CoverageGap returns the first HostKey range gap or overlap in the manifest within one epoch, or ok=false when the entries tile [0, 2^64) cleanly. The ranges must partition the space with no gap and no overlap (doc 10 section 11, D14), and this is the check a control plane runs before trusting the map.
func (*Manifest) DueParts ¶
func (m *Manifest) DueParts(now uint32) []ManifestEntry
DueParts returns the partitions with due work at or before now: those whose DueMin is nonzero and not in the future. This is the fleet-level pushdown of doc 10 section 9 lifted to partitions, reading only the manifest.
type ManifestEntry ¶
type ManifestEntry struct {
PartitionID uint32
FileRef string // path or object-store key of the .meguri file
HostKeyLo uint64
HostKeyHi uint64
URLCount uint64
HostCount uint64
DueMin uint32
BytesPerURL float32
FileCRC32C uint32 // a CRC over the file's footer, a cheap identity check
CreatedHours uint32
Epoch uint32
}
ManifestEntry is one partition's row in the catalog, the durable record of its range, size, and soonest due time pulled from the file's STATS.
func ManifestEntryFor ¶
func ManifestEntryFor(fileBytes []byte, fileRef string, epoch uint32) (ManifestEntry, error)
ManifestEntryFor builds a manifest entry from a freshly written file's bytes and its storage ref, reading the header and footer once. The epoch tags which partition-map generation the entry belongs to (doc 10 section 11, D14).
type Partition ¶
type Partition struct {
ID uint32
HostKeyLo uint64
HostKeyHi uint64
CreatedHours uint32
DefaultCodec uint8 // CodecNone or CodecZstd; M0 writes RAW pages either way
URLs []m.URLRecord
Hosts []m.HostRecord
Strings []byte // arena the URLRef/ETagRef/HostRef/RegistrableRef offsets index
// StringsAt and StringsSize are the streaming alternative to Strings for the
// bounded-memory checkpoint (StreamEncodeToFile). When StringsAt is non-nil the
// encoder reads the [0, StringsSize) string arena from it through a bounded
// chunk buffer and writes the blob region page by page, so a 100M checkpoint
// never materializes the whole multi-gigabyte arena in RAM. The streamed pages
// decode to the same bytes Strings would have framed as one page, so the *Ref
// offsets are identical. A non-nil StringsAt takes precedence over Strings.
StringsAt io.ReaderAt
StringsSize int64
// SeenFilter is the optional serialized resident seen-set filter (doc 10
// section 6, the seen-set filter region). It is the approximate dedup tier
// (dedup.SeenSet.MarshalFilter) carried across a checkpoint so a reload does
// not re-add every key; empty means the region is omitted and the filter is
// rebuilt from the urlkey column on load. The bytes are opaque to the format:
// the dedup package owns their layout, the format frames them with a CRC.
SeenFilter []byte
// BuildSchedule asks Encode to derive and write the schedule index region (doc
// 10 section 7, the bucketed timing wheel) from the URL rows' next_due. It is
// off by default so a partition that does not want the wheel stays byte-for-byte
// the same; a checkpoint that wants a scheduler to find due work without
// scanning the next_due column sets it. The region is omitted anyway when no row
// is scheduled.
BuildSchedule bool
// MaxPageRows caps how many rows one column page holds. Zero (the default) keeps
// the M0 behavior of one page per column, so a partition that does not opt in
// stays byte-for-byte the same and the pinned size baselines do not move. A
// positive value spills a column past that many rows into successive pages and
// builds a per-page skip list (doc 10 section 4, the page_index_offset and inline
// page min/max): each page carries its own zone min/max so a reader prunes at the
// page level, decompressing only the pages whose range overlaps a predicate
// rather than the whole column. doc 14 sets the production page size; this is the
// mechanism it turns on.
MaxPageRows int
// BlobFrontCode asks the encoder to front-code the string blob region (Spec 2074
// M1): each string stored as the shared-prefix length with the previous string
// in its page plus the literal suffix, ahead of the block codec. Off by default
// so a partition that does not opt in stays byte-for-byte the same and the pinned
// size baselines do not move; the live engine's writers set it. The transform is
// reversed to the exact raw arena on decode, so the *Ref offsets are unchanged.
BlobFrontCode bool
Meta map[string]string // optional string metadata, keys sorted on write
}
Partition is the in-memory image of one .meguri file: the URL and host tables plus the shared string arena their *Ref fields point into. It is the unit a checkpoint writes and a load reads. The engine builds one from its live state and hands it to Encode; Decode hands one back on recovery or redistribution.
The caller owns ordering: URLs must be sorted by URLKey big-endian and hosts by HostKey ascending. Encode verifies this rather than sorting, so a buggy caller is caught instead of silently reordering rows the column zone maps and the seen-set assume are sorted.
func Compact ¶
Compact rewrites a partition dropping the URL rows whose status is Gone (the crawl has confirmed they no longer exist, doc 03) and reclaiming the string arena to only the spans the surviving rows still reference. A partition that has accumulated Gone rows and orphaned strings shrinks to exactly its live footprint, which is the garbage collection D11's checkpoint rotation leans on. The result is a fresh Partition; the input is untouched.
func Decode ¶
Decode parses a complete .meguri file back into a Partition, verifying the header, footer, region, page, and column checksums along the way.
func Extract ¶
Extract splits a partition by host membership: out holds exactly the rows of the hosts whose HostKey is in moving, rest holds every other host. Both keep the input's sorted order and each gets its own re-based string arena holding only the spans its rows reference.
This is the rebalance primitive the distribution layer calls with a jump-hash moving set (doc 12, section 3). Unlike Split, which cuts at one HostKey boundary, the moving set is a set of non-contiguous HostKeys, because jump hashing spreads the hosts that remap onto a new partition across the whole key space. Each host's rows are still a contiguous run in the source (the table is sorted by URLKey, so by HostKey first), so the selection is a single pass that copies whole hosts to one side or the other, never splitting a host.
The two results carry the input's id, creation time, and codec; the caller assigns each its destination id and HostKey range as the rebalance needs.
func Merge ¶
Merge combines two partitions with disjoint HostKey ranges into one, keeping both tables sorted and the string arena shared. It is the inverse of Split, the rebalance step that folds two small partitions back together. Merge returns ErrNotSorted if the merged URL rows would not be sorted, which catches overlapping or mis-ordered inputs rather than producing a file a reader would reject. The lower-keyed partition's identity and creation time win.
func Pack ¶
func Pack(id uint32, hostKeyLo, hostKeyHi uint64, createdHours uint32, codec uint8, urls []m.URLRecord, hosts []m.HostRecord, strings []byte) *Partition
Pack builds a Partition from already-sorted records and the shared string arena. It is the in-memory step before Encode: a caller that has live URL and host records hands them here, sets the partition identity, and gets a value ready to serialize. Pack does not sort; it trusts the caller, the same contract Encode keeps, so an unsorted input is caught at Encode.
func Split ¶
Split divides a partition into a low half owning HostKeys < atHostKey and a high half owning HostKeys >= atHostKey. Because the URL rows are sorted by URLKey (HostKey high half first) and a host's rows are contiguous, the cut is one boundary in each table, so neither half ever holds part of a host. Each half gets its own re-based string arena holding only the spans its rows reference. The two halves carry the same partition id and creation time as the input; a caller assigns new identities and ranges as the rebalance needs.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader is a projection-and-pushdown view over a .meguri file. Where Decode materializes every column of every row into full records, a Reader parses the header and footer once and then decodes only the columns a caller asks for, and lets a caller skip the file entirely when the footer's zone maps prove no row can match a predicate (doc 10 section 9, predicate pushdown and projection discipline). This is what keeps a scheduler scan, a dedup rebuild, and a host range read from paying for the 21 columns they do not read.
A Reader holds the file bytes; it does not copy them. The decoded columns it returns are fresh slices.
func NewReader ¶
NewReader parses a .meguri file's header and footer, verifying their checksums, without decoding any column body. It is the cheap open a pushdown query starts from: the zone maps and stats it needs to decide whether to read further all live in the footer.
func (*Reader) ArenaRandReader ¶ added in v0.2.0
func (r *Reader) ArenaRandReader() (*ArenaRandReader, error)
ArenaRandReader opens a random-access reader over the file's string blob, decoding the whole arena up front. A file with no blob region yields a reader that resolves every ref to nil.
func (*Reader) ArenaSeqReader ¶ added in v0.2.0
func (r *Reader) ArenaSeqReader() *ArenaSeqReader
ArenaSeqReader opens a sequential reader over the file's string-blob region. A file with no blob region yields a reader that resolves every ref to nil, matching an empty arena.
func (*Reader) ContainsURL ¶ added in v0.2.0
ContainsURL reports whether key is stored in the partition, the dedup-confirm primitive the file-backed engine runs when its resident filter says "maybe" (spec 2073 doc 08). It is LookupURL stripped to a presence test: it decodes only the two key columns, not the full thirteen-column record, so a confirmation costs a key-column page decode rather than a whole-row projection. The header host range rejects an out-of-partition key in O(1); a multi-page table prunes to the pages whose hostkey zone covers the key and binary-searches each by URLKey; a single-page table decodes its key columns once and binary-searches the set.
func (*Reader) DueByWheel ¶
DueByWheel returns the URLKeys of every row due at or before now, using the schedule wheel to prune the scan: it reads the wheel's due buckets, then projects the urlkey and next_due columns only for those candidate rows and confirms each against next_due. It falls back to the column-scan DueKeys when the file carries no wheel. This is the schedule-index read path of doc 10 section 7.
func (*Reader) DueCursor ¶ added in v0.2.0
DueCursor opens a bounded due-dispatch scan for the rows due at or before now. When the file's footer proves nothing is due it returns a cursor that yields no batches, the same file-level pushdown DueKeys uses, so a partition with no due work costs no column decode. The next_due, hostkey, and pathkey columns must split on the same page boundaries (they always do, since the encoder pages the URL table uniformly), so a page index lines up across the three.
func (*Reader) DueKeys ¶
DueKeys returns the URLKeys of every row due at or before now, combining the file-level pushdown with a two-column projection: when the footer proves nothing is due it returns nil without decoding a body, otherwise it reads only the urlkey and next_due columns. A due row has a nonzero next_due at or before now (a zero next_due is unscheduled). This is the scheduler's read path made cheap, the worked example of doc 10 section 9.
func (*Reader) DueRange ¶
DueRange returns the smallest and largest nonzero next_due across the URL rows, as the footer stats recorded them. A dueMin of 0 means no row is scheduled.
func (*Reader) EnableKeyCache ¶ added in v0.2.0
EnableKeyCache installs a decoded key-page cache of capPages pages on the reader, so repeated presence checks into the same pages amortize the decode. capPages <= 0 disables it. It is opt-in because a one-shot scan gains nothing from it and a cache holds decoded pages resident; the live engine turns it on for the probe-stream read path.
func (*Reader) HasSchedule ¶
HasSchedule reports whether the file carries a schedule index region, the durable timing wheel a scheduler can read instead of scanning the next_due column.
func (*Reader) Header ¶ added in v0.2.0
Header returns the file's decoded header: partition id, build time, codec, key range, and row counts. It is the cheap metadata a recovering store reads to rebuild its shell without decoding a single body column.
func (*Reader) HostKeyRange ¶
HostKeyRange returns the partition's HostKey bounds from the header, the range a router uses to decide whether this file could own a host at all.
func (*Reader) HostRangePageScan ¶
HostRangePageScan reports how many of the hostkey column's pages a host-range read for [lo, hi] would decode versus the column's total page count, the observable proof that the per-page skip list pruned pages. total is 0 for a single-page column, which carries no skip list to prune.
func (*Reader) HostRangeURLKeys ¶
HostRangeURLKeys returns the URLKeys of every row whose host key falls in [lo, hi], the host range read of doc 10 section 9 sharpened to the page level. When the hostkey column is split across pages (Partition.MaxPageRows opted in), it consults the per-page skip list and decodes only the pages whose zone overlaps [lo, hi], pruning the rest without decompressing them; the pathkey column splits on the same boundaries, so the same page indices line up. A single-page column falls back to a whole-column projection and filter, and a range disjoint from the partition's header bounds returns nil without touching the body. The rows stay in stored order, which is host-key ascending.
func (*Reader) Hosts ¶ added in v0.2.0
func (r *Reader) Hosts() ([]m.HostRecord, error)
Hosts decodes the whole host table into records. The host table is the small region of the file, one row per host rather than per URL, so it is materialized whole where the URL table streams. A recovering engine reads it to rebuild the resident host map from the mapped file without decoding the URL body.
func (*Reader) LookupURL ¶ added in v0.2.0
LookupURL returns the URL record stored under key, or ok=false when the partition does not hold it. It is the point read the live store needs once the .meguri file is the body store rather than the append log (doc 03 change 3): a GetURL that misses the in-memory tail resolves the base record here by key, with no log byte offset to chase. The lookup is a zone-pruned page search. The header HostKey range rejects an out-of-partition key in O(1). For a multi-page table the hostkey column's per-page skip list narrows the scan to the pages whose zone covers the key's host, and a binary search inside each candidate page finds the row by full URLKey, the rows being stored in URLKey order (the streamed repository's order). A single-page table decodes its columns once and binary-searches the whole set.
func (*Reader) MaybeDueAt ¶
MaybeDueAt reports whether any URL could be due at or before now, the file-level pushdown a scheduler uses to skip a partition with no due work without decoding a single column: false means the soonest due time is in the future (or nothing is scheduled), true means the next_due column must be read.
func (*Reader) MaybeOwnsHost ¶
MaybeOwnsHost reports whether the host could live in this partition, a O(1) pushdown from the header's HostKey range: a false is authoritative (the host is outside the partition's range), a true means the file must be read to confirm. This is the host range read of doc 10 section 9.
func (*Reader) NextDue ¶
NextDue decodes only the next_due column, the next_due-only scan of doc 10 section 9: a scheduler reads the due times without materializing records.
func (*Reader) Schedule ¶
func (r *Reader) Schedule() (*ScheduleIndex, error)
Schedule decodes the schedule index region into a wheel a scheduler queries with DueBuckets, or returns nil when the file carries no schedule region. It verifies the page CRC. This is the pushdown read for due work: the wheel narrows the scan to the buckets whose window has opened, the next_due column confirms the survivors.
func (*Reader) SeenFilter ¶ added in v0.2.0
SeenFilter returns the serialized resident seen-set filter from the file's seen-set region, or nil if the file has none. It is the bytes dedup.LoadFilter restores, so a recovering engine reloads the approximate dedup tier from the mapped file without re-adding every key (spec 2073 doc 08, the recovery path).
func (*Reader) URLKeys ¶
URLKeys decodes only the two key columns and reconstructs the partition's URLKeys, the urlkey-only dedup projection of doc 10 section 9: a seen-set rebuild reads the keys without paying for status, timestamps, or fingerprints.
func (*Reader) URLRows ¶ added in v0.2.0
func (r *Reader) URLRows() (*URLRowCursor, error)
URLRows opens a sequential row cursor over the file's URL table. A single-page table decodes its columns once on the first Next; a multi-page table decodes a page at a time, so the resident cost is one page regardless of the table size.
type RegionInfo ¶
RegionInfo is one row of the inspect region table.
type ScheduleIndex ¶
type ScheduleIndex struct {
// contains filtered or unexported fields
}
ScheduleIndex is the read view over a schedule region: the wheel base and the row indices grouped by bucket. A scheduler asks it which rows could be due at a given time and confirms the candidates against the next_due column.
func (*ScheduleIndex) Base ¶
func (s *ScheduleIndex) Base() uint32
Base returns the wheel's base time, the file's earliest nonzero next_due that all bucket windows are measured from.
func (*ScheduleIndex) Covered ¶
func (s *ScheduleIndex) Covered() uint32
Covered returns the total URL row count the wheel was built over, the denominator for the fraction of rows a due query selects.
func (*ScheduleIndex) DueBuckets ¶
func (s *ScheduleIndex) DueBuckets(now uint32) []uint32
DueBuckets returns the row indices of every bucket whose window starts at or before now, the wheel's pushdown answer to "what could be due at now". It is a superset of the truly-due rows: a row in the boundary bucket (the one now falls inside) may have a next_due just past now, so a caller confirms each candidate against the next_due column. A now before the base yields nothing, the cheap skip for a file whose soonest work is still in the future.
type Stats ¶
type Stats struct {
ScheduledCount uint64
DueMin uint32
DueMax uint32
TotalCompressed uint64
TotalUncompressed uint64
BytesPerURL float32
}
Stats is the inspect-visible copy of the footer stats block.
type URLRecordSource ¶ added in v0.2.0
URLRecordSource yields URL records in ascending URLKey order for streaming region encoding. Next returns false once the records are exhausted. The source is pulled one record at a time, so a caller can k-way-merge the store's sorted shards (spec 2072 D9) without ever materializing the whole partition.
type URLRowCursor ¶ added in v0.2.0
type URLRowCursor struct {
// contains filtered or unexported fields
}
URLRowCursor streams the URL table of a .meguri file row by row in stored order (URLKey ascending), decoding one page of all columns at a time and yielding its rows before moving to the next page. It is the sequential read the checkpoint needs to source base-record bodies from the prior snapshot: a checkpoint that re-folds the live frontier reads every unchanged record straight off the file in key order, merge-joined against the log tail, instead of a per-key page decode or a random-read gather over the body log (doc 14, the body-ordered store). The transient is one page of records, not the table.