tiny.sql.history.segment.manifest
Defined in history.segment.
API (28)
Actions
Public operations.
Entry.fromVerifiedEntry.localReleaseAllowedPage.entryPage.latestReader.acceptReader.demandReader.doneReader.initRoot.demandappendpromoteTailColdverifyPageverifyRoot
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
Source
Source: lib/sql/src/history/segment/manifest.zig
zig
const std = @import("std");const sql = @import("../../root.zig");const segment = @import("root.zig");const Hash = sql.Hash;const Sha256 = std.crypto.hash.sha2.Sha256;pub const root_bytes: usize = 128;pub const page_bytes: usize = 4096;pub const page_header_bytes: usize = 96;pub const entry_bytes: usize = 208;pub const page_trailer_bytes: usize = Sha256.digest_length;pub const entries_per_page: usize = (page_bytes - page_header_bytes - page_trailer_bytes) / entry_bytes;pub const PageRange = struct { pub const Count = u64; pub const unit_precision_segments: Count = 1; pub const maximum_segments_per_second: Count = segment.SegmentRange.maximum_units_per_second; pub const maximum_seconds_per_year: Count = segment.SegmentRange.maximum_seconds_per_year; pub const service_lifetime_years: Count = segment.SegmentRange.service_lifetime_years; pub const service_lifetime_seconds: Count = segment.SegmentRange.service_lifetime_seconds; pub const segments_per_page: Count = entries_per_page; pub const maximum_increment_pages: Count = 1; pub const budget_pages: Count = total( maximum_segments_per_second, service_lifetime_seconds, ).?; pub fn total(segments_per_second: Count, lifetime_seconds: Count) ?Count { const segment_count = @as(u128, unit_precision_segments) * @as(u128, segments_per_second) * @as(u128, lifetime_seconds); const page_count = if (segment_count == 0) 0 else (segment_count - 1) / @as(u128, segments_per_page) + 1; return std.math.cast(Count, page_count); } pub fn advance(current: Count, increment: Count) ?Count { if (!containsCount(current) or increment > maximum_increment_pages) { return null; } const next = std.math.add(Count, current, increment) catch return null; return if (next <= budget_pages) next else null; } pub fn containsCount(count: Count) bool { return count > 0 and count <= budget_pages; }};comptime { std.debug.assert(PageRange.budget_pages > 0); std.debug.assert( PageRange.budget_pages == (segment.SegmentRange.budget_segments - 1) / PageRange.segments_per_page + 1, ); std.debug.assert( @as(u128, PageRange.budget_pages) + @as(u128, PageRange.maximum_increment_pages) <= std.math.maxInt(PageRange.Count), );}const root_magic = "TINYMNRT".*;const page_magic = "TINYMPAG".*;const format_version: u32 = 1;pub const Error = segment.Error || error{ InvalidManifest, InvalidLifecycleTransition, ManifestChanged,};pub const Tier = enum(u8) { sealed = 1, cold = 2,};pub const Entry = struct { segment: Hash, previous_segment: Hash, stream_identity: Hash, checkpoint_root: Hash, cold_receipt: Hash = @splat(0), epoch: u64, ordinal: segment.SegmentRange.Count, first_event: segment.EventRange.Count, event_count: segment.EventRange.Count, encoded_bytes: u64, tier: Tier = .sealed, pub fn fromVerified(value: segment.View) Entry { return .{ .segment = value.digest, .previous_segment = value.spec.previous_segment, .stream_identity = value.spec.stream_identity, .checkpoint_root = value.spec.checkpoint_root, .epoch = value.spec.epoch, .ordinal = value.spec.ordinal, .first_event = value.spec.first_event, .event_count = value.spec.event_count, .encoded_bytes = value.bytes.len, }; } pub fn localReleaseAllowed(self: Entry) bool { return self.tier == .cold and !isZero(self.cold_receipt); }};pub const Root = struct { digest: Hash, tail_page: Hash, last_segment: Hash, segment_count: segment.SegmentRange.Count, page_count: PageRange.Count, tail_entries: u32, pub fn demand(self: Root) Error!ReplayDemand { return .{ .manifest_pages = self.page_count, .segments = self.segment_count, .io_operations = std.math.add( u64, self.page_count, self.segment_count, ) catch return error.InvalidManifest, }; }};pub const ReplayDemand = struct { manifest_pages: u64, segments: u64, sealed_segments: u64 = 0, cold_segments: u64 = 0, event_count: u64 = 0, segment_bytes: u64 = 0, max_segment_bytes: u64 = 0, cold_fetch_count: u64 = 0, cold_bytes: u64 = 0, io_operations: u64,};pub const Page = struct { bytes: []const u8, digest: Hash, previous_page: Hash, first_ordinal: segment.SegmentRange.Count, count: u32, pub fn entry(self: Page, index: usize) Error!Entry { if (index >= self.count) return error.InvalidManifest; const start = page_header_bytes + index * entry_bytes; return try readEntry(self.bytes[start..][0..entry_bytes]); } pub fn latest(self: Page) Error!Entry { if (self.count == 0) return error.InvalidManifest; return try self.entry(self.count - 1); }};pub const Publication = struct { root: Root, page: Page,};pub const Reader = struct { expected_page: Hash, expected_last_segment: Hash, pages_remaining: PageRange.Count, segments_remaining: segment.SegmentRange.Count, first: bool = true, tail_entries: u32, replay_demand: ReplayDemand, pub fn init(root: Root) Reader { std.debug.assert(root.page_count != 0); std.debug.assert(root.segment_count != 0); return .{ .expected_page = root.tail_page, .expected_last_segment = root.last_segment, .pages_remaining = root.page_count, .segments_remaining = root.segment_count, .tail_entries = root.tail_entries, .replay_demand = .{ .manifest_pages = root.page_count, .segments = 0, .io_operations = root.page_count, }, }; } pub fn accept(self: *Reader, bytes: []const u8) Error!Page { if (self.pages_remaining == 0) return error.InvalidManifest; const page = try verifyPage(bytes); if (!same(page.digest, self.expected_page)) return error.ManifestChanged; if (self.first and page.count != self.tail_entries) { return error.InvalidManifest; } if (page.count > self.segments_remaining) return error.InvalidManifest; const latest = try page.latest(); if (!same(latest.segment, self.expected_last_segment)) { return error.InvalidManifest; } const ordinal_count = segment.SegmentRange.advance(latest.ordinal) orelse return error.InvalidManifest; if (ordinal_count != self.segments_remaining) { return error.InvalidManifest; } const oldest = try page.entry(0); var next_demand = self.replay_demand; for (0..page.count) |index| { try addDemand(&next_demand, try page.entry(index)); } self.segments_remaining -= page.count; self.pages_remaining -= 1; self.expected_page = page.previous_page; self.expected_last_segment = oldest.previous_segment; self.first = false; self.replay_demand = next_demand; if ((self.pages_remaining == 0) != isZero(self.expected_page)) { return error.InvalidManifest; } if (self.pages_remaining == 0 and self.segments_remaining != 0) { return error.InvalidManifest; } if (self.pages_remaining == 0 and !isZero(self.expected_last_segment)) { return error.InvalidManifest; } return page; } pub fn done(self: Reader) bool { return self.pages_remaining == 0 and self.segments_remaining == 0; } pub fn demand(self: Reader) Error!ReplayDemand { if (!self.done()) return error.InvalidManifest; return self.replay_demand; }};pub fn append( current_root_bytes: ?[]const u8, current_tail_bytes: ?[]const u8, entry_value: Entry, page_out: *[page_bytes]u8, root_out: *[root_bytes]u8,) Error!Publication { if (entry_value.tier != .sealed or !isZero(entry_value.cold_receipt)) { return error.InvalidLifecycleTransition; } if (!entryWithinRanges(entry_value)) return error.InvalidManifest; const current = try validateCurrent(current_root_bytes, current_tail_bytes); if (current) |state| { if (entry_value.ordinal != state.root.segment_count or !same(entry_value.previous_segment, state.root.last_segment)) { return error.InvalidManifest; } const page_count = PageRange.advance( state.root.page_count, @intFromBool(state.page.count == entries_per_page), ) orelse return error.InvalidManifest; const segment_count = segment.SegmentRange.advance( state.root.segment_count, ) orelse return error.InvalidManifest; if (state.page.count < entries_per_page) { copyPageAndAppend(state.page, entry_value, page_out); } else { writePage(page_out, state.page.digest, entry_value); } const page = try verifyPage(page_out); writeRoot(root_out, .{ .tail_page = page.digest, .last_segment = entry_value.segment, .segment_count = segment_count, .page_count = page_count, .tail_entries = page.count, }); return .{ .root = try verifyRoot(root_out), .page = page }; } if (entry_value.ordinal != 0 or !isZero(entry_value.previous_segment)) { return error.InvalidManifest; } writePage(page_out, @splat(0), entry_value); const page = try verifyPage(page_out); writeRoot(root_out, .{ .tail_page = page.digest, .last_segment = entry_value.segment, .segment_count = 1, .page_count = 1, .tail_entries = 1, }); return .{ .root = try verifyRoot(root_out), .page = page };}pub fn promoteTailCold( current_root_bytes: []const u8, current_tail_bytes: []const u8, copied_segment: []const u8, page_out: *[page_bytes]u8, root_out: *[root_bytes]u8,) Error!Publication { const state = (try validateCurrent( current_root_bytes, current_tail_bytes, )) orelse return error.InvalidManifest; var latest = try state.page.latest(); if (latest.tier != .sealed or !isZero(latest.cold_receipt)) { return error.InvalidLifecycleTransition; } const verified = try segment.verify(copied_segment); if (!same(verified.digest, latest.segment) or copied_segment.len != latest.encoded_bytes) { return error.InvalidLifecycleTransition; } latest.tier = .cold; latest.cold_receipt = coldReceipt(latest.segment, copied_segment.len); page_out.* = state.page.bytes[0..page_bytes].*; const latest_index: usize = state.page.count - 1; const start = page_header_bytes + latest_index * entry_bytes; writeEntry(page_out[start..][0..entry_bytes], latest); finishPage(page_out, state.page.count); const page = try verifyPage(page_out); writeRoot(root_out, .{ .tail_page = page.digest, .last_segment = state.root.last_segment, .segment_count = state.root.segment_count, .page_count = state.root.page_count, .tail_entries = state.root.tail_entries, }); return .{ .root = try verifyRoot(root_out), .page = page };}pub fn verifyRoot(bytes: []const u8) Error!Root { if (bytes.len != root_bytes or !std.mem.eql(u8, bytes[0..8], &root_magic)) { return error.InvalidManifest; } if (std.mem.readInt(u32, bytes[8..12], .little) != format_version) { return error.InvalidManifest; } const root_checksum = digest(bytes[0 .. root_bytes - Sha256.digest_length]); if (!std.mem.eql(u8, &root_checksum, bytes[root_bytes - Sha256.digest_length ..])) { return error.InvalidManifest; } const root_digest = digest(bytes); const segment_count = std.mem.readInt(u64, bytes[16..24], .little); const page_count = std.mem.readInt(u64, bytes[24..32], .little); const tail_entries = std.mem.readInt(u32, bytes[12..16], .little); if (!segment.SegmentRange.containsCount(segment_count) or !PageRange.containsCount(page_count) or tail_entries == 0 or tail_entries > entries_per_page or page_count > segment_count) { return error.InvalidManifest; } const page_width: u64 = entries_per_page; const expected_page_count = (segment_count - 1) / page_width + 1; const expected_tail_entries = (segment_count - 1) % page_width + 1; if (page_count != expected_page_count or @as(u64, tail_entries) != expected_tail_entries) { return error.InvalidManifest; } return .{ .digest = root_digest, .tail_page = bytes[32..64].*, .last_segment = bytes[64..96].*, .segment_count = segment_count, .page_count = page_count, .tail_entries = tail_entries, };}pub fn verifyPage(bytes: []const u8) Error!Page { if (bytes.len != page_bytes or !std.mem.eql(u8, bytes[0..8], &page_magic)) { return error.InvalidManifest; } if (std.mem.readInt(u32, bytes[8..12], .little) != format_version) { return error.InvalidManifest; } const count = std.mem.readInt(u32, bytes[12..16], .little); if (count == 0 or count > entries_per_page) return error.InvalidManifest; const used_end = page_header_bytes + count * entry_bytes; if (!allZero(bytes[used_end .. page_bytes - page_trailer_bytes])) { return error.InvalidManifest; } if (!std.mem.eql(u8, bytes[64..96], &digest(bytes[page_header_bytes..used_end]))) { return error.InvalidManifest; } const page_checksum = digest(bytes[0 .. page_bytes - page_trailer_bytes]); if (!std.mem.eql(u8, &page_checksum, bytes[page_bytes - page_trailer_bytes ..])) { return error.InvalidManifest; } const page_digest = digest(bytes); const page = Page{ .bytes = bytes, .digest = page_digest, .previous_page = bytes[32..64].*, .first_ordinal = std.mem.readInt(u64, bytes[16..24], .little), .count = count, }; try verifyEntries(page); return page;}const Current = struct { root: Root, page: Page,};const RootFields = struct { tail_page: Hash, last_segment: Hash, segment_count: segment.SegmentRange.Count, page_count: PageRange.Count, tail_entries: u32,};fn validateCurrent( root_value: ?[]const u8, page_value: ?[]const u8,) Error!?Current { if ((root_value == null) != (page_value == null)) return error.InvalidManifest; const root_bytes_value = root_value orelse return null; const page_bytes_value = page_value.?; const root = try verifyRoot(root_bytes_value); const page = try verifyPage(page_bytes_value); if (!same(root.tail_page, page.digest) or root.tail_entries != page.count) { return error.ManifestChanged; } const latest = try page.latest(); const segment_count = segment.SegmentRange.advance(latest.ordinal) orelse return error.InvalidManifest; if (!same(root.last_segment, latest.segment) or root.segment_count != segment_count) { return error.InvalidManifest; } return .{ .root = root, .page = page };}fn verifyEntries(page: Page) Error!void { var previous: ?Entry = null; for (0..page.count) |index| { const entry_value = try page.entry(index); if (!entryWithinRanges(entry_value)) return error.InvalidManifest; const expected_ordinal = std.math.add( segment.SegmentRange.Count, page.first_ordinal, @intCast(index), ) catch return error.InvalidManifest; if (entry_value.ordinal != expected_ordinal) { return error.InvalidManifest; } if (entry_value.event_count == 0 or entry_value.encoded_bytes == 0 or entry_value.encoded_bytes > segment.encoded_bytes_max or isZero(entry_value.segment) or isZero(entry_value.stream_identity)) { return error.InvalidManifest; } if (previous) |prior| { if (!same(entry_value.previous_segment, prior.segment)) { return error.InvalidManifest; } } switch (entry_value.tier) { .sealed => if (!isZero(entry_value.cold_receipt)) { return error.InvalidManifest; }, .cold => if (isZero(entry_value.cold_receipt)) { return error.InvalidManifest; }, } previous = entry_value; }}fn addDemand(demand: *ReplayDemand, entry_value: Entry) Error!void { demand.segments = std.math.add(u64, demand.segments, 1) catch return error.InvalidManifest; demand.event_count = std.math.add( u64, demand.event_count, entry_value.event_count, ) catch return error.InvalidManifest; demand.segment_bytes = std.math.add( u64, demand.segment_bytes, entry_value.encoded_bytes, ) catch return error.InvalidManifest; demand.max_segment_bytes = @max( demand.max_segment_bytes, entry_value.encoded_bytes, ); demand.io_operations = std.math.add(u64, demand.io_operations, 1) catch return error.InvalidManifest; switch (entry_value.tier) { .sealed => demand.sealed_segments = std.math.add( u64, demand.sealed_segments, 1, ) catch return error.InvalidManifest, .cold => { demand.cold_segments = std.math.add( u64, demand.cold_segments, 1, ) catch return error.InvalidManifest; demand.cold_fetch_count = std.math.add( u64, demand.cold_fetch_count, 1, ) catch return error.InvalidManifest; demand.cold_bytes = std.math.add( u64, demand.cold_bytes, entry_value.encoded_bytes, ) catch return error.InvalidManifest; }, }}fn writeRoot(out: *[root_bytes]u8, fields: RootFields) void { std.debug.assert(segment.SegmentRange.containsCount(fields.segment_count)); std.debug.assert(PageRange.containsCount(fields.page_count)); std.debug.assert(fields.tail_entries != 0); @memset(out, 0); @memcpy(out[0..8], &root_magic); std.mem.writeInt(u32, out[8..12], format_version, .little); std.mem.writeInt(u32, out[12..16], fields.tail_entries, .little); std.mem.writeInt(u64, out[16..24], fields.segment_count, .little); std.mem.writeInt(u64, out[24..32], fields.page_count, .little); @memcpy(out[32..64], &fields.tail_page); @memcpy(out[64..96], &fields.last_segment); const root_checksum = digest(out[0 .. root_bytes - Sha256.digest_length]); @memcpy(out[root_bytes - Sha256.digest_length ..], &root_checksum);}fn writePage(out: *[page_bytes]u8, previous_page: Hash, entry_value: Entry) void { std.debug.assert(entryWithinRanges(entry_value)); @memset(out, 0); @memcpy(out[0..8], &page_magic); std.mem.writeInt(u32, out[8..12], format_version, .little); std.mem.writeInt(u32, out[12..16], 1, .little); std.mem.writeInt(u64, out[16..24], entry_value.ordinal, .little); @memcpy(out[32..64], &previous_page); writeEntry(out[page_header_bytes..][0..entry_bytes], entry_value); finishPage(out, 1);}fn copyPageAndAppend(page: Page, entry_value: Entry, out: *[page_bytes]u8) void { std.debug.assert(page.count < entries_per_page); out.* = page.bytes[0..page_bytes].*; const index: usize = page.count; const start = page_header_bytes + index * entry_bytes; writeEntry(out[start..][0..entry_bytes], entry_value); const count = page.count + 1; std.mem.writeInt(u32, out[12..16], count, .little); finishPage(out, count);}fn finishPage(out: *[page_bytes]u8, count: u32) void { const used_end = page_header_bytes + count * entry_bytes; @memset(out[used_end .. page_bytes - page_trailer_bytes], 0); const entries_digest = digest(out[page_header_bytes..used_end]); @memcpy(out[64..96], &entries_digest); const page_checksum = digest(out[0 .. page_bytes - page_trailer_bytes]); @memcpy(out[page_bytes - page_trailer_bytes ..], &page_checksum);}fn writeEntry(out: []u8, value: Entry) void { std.debug.assert(out.len == entry_bytes); std.debug.assert(entryWithinRanges(value)); @memset(out, 0); @memcpy(out[0..32], &value.segment); @memcpy(out[32..64], &value.previous_segment); @memcpy(out[64..96], &value.stream_identity); @memcpy(out[96..128], &value.checkpoint_root); @memcpy(out[128..160], &value.cold_receipt); std.mem.writeInt(u64, out[160..168], value.epoch, .little); std.mem.writeInt(u64, out[168..176], value.ordinal, .little); std.mem.writeInt(u64, out[176..184], value.first_event, .little); std.mem.writeInt(u64, out[184..192], value.event_count, .little); std.mem.writeInt(u64, out[192..200], value.encoded_bytes, .little); out[200] = @backingInt(value.tier);}fn readEntry(bytes: []const u8) Error!Entry { if (bytes.len != entry_bytes or !allZero(bytes[201..208])) { return error.InvalidManifest; } return .{ .segment = bytes[0..32].*, .previous_segment = bytes[32..64].*, .stream_identity = bytes[64..96].*, .checkpoint_root = bytes[96..128].*, .cold_receipt = bytes[128..160].*, .epoch = std.mem.readInt(u64, bytes[160..168], .little), .ordinal = std.mem.readInt(u64, bytes[168..176], .little), .first_event = std.mem.readInt(u64, bytes[176..184], .little), .event_count = std.mem.readInt(u64, bytes[184..192], .little), .encoded_bytes = std.mem.readInt(u64, bytes[192..200], .little), .tier = std.enums.fromInt(Tier, bytes[200]) orelse return error.InvalidManifest, };}fn entryWithinRanges(value: Entry) bool { return segment.SegmentRange.containsOrdinal(value.ordinal) and segment.EventRange.advance(value.first_event, value.event_count) != null;}fn coldReceipt(segment_digest: Hash, bytes: usize) Hash { var hasher = Sha256.init(.{}); hasher.update("tiny.sql.history.cold-receipt/v1"); hasher.update(&segment_digest); var frame: [8]u8 = undefined; std.mem.writeInt(u64, &frame, bytes, .little); hasher.update(&frame); var value: Hash = undefined; hasher.final(&value); return value;}fn digest(bytes: []const u8) Hash { var value: Hash = undefined; Sha256.hash(bytes, &value, .{}); return value;}fn same(left: Hash, right: Hash) bool { return std.mem.eql(u8, &left, &right);}fn isZero(value: Hash) bool { return same(value, @splat(0));}fn allZero(bytes: []const u8) bool { for (bytes) |byte| if (byte != 0) return false; return true;}Source: lib/sql/src/history/segment/root.zig:3
zig
pub const manifest = @import("manifest.zig");Complete call list for history.segment.manifest.append
10 direct calls.
tiny.sql.history.segment.manifest.PageRange.advance[function] atlib/sql/src/history/segment/manifest.zig:41lib.sql.src.history.segment.manifest.copyPageAndAppend[function] — private source atlib/sql/src/history/segment/manifest.zig:561in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.entryWithinRanges[function] — private source atlib/sql/src/history/segment/manifest.zig:618in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.isZero[function] — private source atlib/sql/src/history/segment/manifest.zig:645in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.same[function] — private source atlib/sql/src/history/segment/manifest.zig:641in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.validateCurrent[function] — private source atlib/sql/src/history/segment/manifest.zig:429in nearest public ownertiny.sql.history.segment.manifesttiny.sql.history.segment.manifest.verifyPage[function] atlib/sql/src/history/segment/manifest.zig:384tiny.sql.history.segment.manifest.verifyRoot[function] atlib/sql/src/history/segment/manifest.zig:345lib.sql.src.history.segment.manifest.writePage[function] — private source atlib/sql/src/history/segment/manifest.zig:549in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.writeRoot[function] — private source atlib/sql/src/history/segment/manifest.zig:533in nearest public ownertiny.sql.history.segment.manifest
Complete call list for history.segment.manifest.promoteTailCold
9 direct calls.
lib.sql.src.history.segment.manifest.coldReceipt[function] — private source atlib/sql/src/history/segment/manifest.zig:623in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.finishPage[function] — private source atlib/sql/src/history/segment/manifest.zig:572in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.isZero[function] — private source atlib/sql/src/history/segment/manifest.zig:645in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.same[function] — private source atlib/sql/src/history/segment/manifest.zig:641in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.validateCurrent[function] — private source atlib/sql/src/history/segment/manifest.zig:429in nearest public ownertiny.sql.history.segment.manifesttiny.sql.history.segment.manifest.verifyPage[function] atlib/sql/src/history/segment/manifest.zig:384tiny.sql.history.segment.manifest.verifyRoot[function] atlib/sql/src/history/segment/manifest.zig:345lib.sql.src.history.segment.manifest.writeEntry[function] — private source atlib/sql/src/history/segment/manifest.zig:581in nearest public ownertiny.sql.history.segment.manifestlib.sql.src.history.segment.manifest.writeRoot[function] — private source atlib/sql/src/history/segment/manifest.zig:533in nearest public ownertiny.sql.history.segment.manifest
Audit
| Definitions | 28 |
|---|---|
| Public names | 28 |
| Members | 43 |
| Version | 26.7.0 |
| Revision | daab053ee433 |