lib/machine/src/checkpoint/roots/maintenance.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const manifest = @import("manifest.zig");
  2 const owner = @import("owner.zig");
  3 const std = @import("std");
  4 const tree = @import("tree.zig");
  5 const types = @import("types.zig");
  6 
  7 pub const CollectionCapacity = types.CollectionCapacity;
  8 pub const CollectionLimits = types.CollectionLimits;
  9 pub const CollectionReport = types.CollectionReport;
 10 pub const CollectionSnapshot = types.CollectionSnapshot;
 11 pub const ReachabilityClass = types.ReachabilityClass;
 12 pub const Retention = types.Retention;
 13 pub const SeedCounts = types.SeedCounts;
 14 pub const compaction_capacity = types.compaction_capacity;
 15 pub const compaction_limits = types.compaction_limits;
 16 
 17 /// The call opens a manifest chain again, authenticates it, and walks the
 18 /// newest page tree in full before publishing a single manifest with no parent
 19 /// that carries the newest state. A parentless input returns its existing root
 20 /// without publishing anything. Any publication failure aborts the compaction
 21 /// transaction. A caller turns a long chain into a single record, so later
 22 /// reopens walk one manifest.
 23 pub fn compact(
 24     storage: types.Storage,
 25     expected: types.ManifestRoot,
 26 ) types.Error!types.Binding {
 27     const value = try owner.reopen(storage, expected);
 28     try tree.verify(storage, value.pages);
 29     if (value.parent == null) {
 30         return .{ .root = expected, .manifest = value };
 31     }
 32     var compacted = value;
 33     compacted.parent = null;
 34     compacted.dirty_pages = @intCast(types.page_count);
 35     try storage.begin(types.compaction_capacity);
 36     var transaction_open = true;
 37     defer if (transaction_open) storage.abort();
 38     const root = try manifest.stage(storage, compacted);
 39     try storage.commit(root);
 40     transaction_open = false;
 41     return .{ .root = root, .manifest = compacted };
 42 }
 43 
 44 /// The call traces every owner class the provider snapshotted, then commits one
 45 /// atomic physical sweep. The call validates the snapshot against the supplied
 46 /// capacity before marking begins, returning `RootCollectionCapacityExceeded`
 47 /// when the snapshot exceeds it. A failure during tracing ends the collection
 48 /// with no object removed. A caller runs collection to get space back from
 49 /// captures nobody holds.
 50 pub fn collect(
 51     storage: types.Storage,
 52     capacity: types.CollectionCapacity,
 53 ) types.Error!types.CollectionReport {
 54     const snapshot = try storage.beginCollection(capacity);
 55     var collection_open = true;
 56     defer if (collection_open) storage.abortCollection();
 57     try validateSnapshot(capacity, snapshot);
 58     inline for (types.reachability_classes) |class| {
 59         var index: u16 = 0;
 60         while (index < snapshot.seeds.count(class)) : (index += 1) {
 61             const root = try storage.readSeed(class, index);
 62             try retainRoot(storage, root);
 63         }
 64     }
 65     const report = try storage.commitCollection();
 66     collection_open = false;
 67     validateReport(snapshot, report);
 68     return report;
 69 }
 70 
 71 fn retainRoot(
 72     storage: types.Storage,
 73     root: types.ManifestRoot,
 74 ) types.Error!void {
 75     const retention = try storage.retainObject(.manifest, root.digest);
 76     if (retention == .existing) return;
 77     _ = try owner.openShared(storage, root);
 78     var current = root;
 79     var depth: usize = 0;
 80     while (depth < types.chain_limit) : (depth += 1) {
 81         const value = try manifest.read(storage, current);
 82         try tree.retain(storage, value.pages);
 83         _ = try storage.retainBlock(value.block);
 84         if (value.parent) |parent| {
 85             const parent_retention = try storage.retainObject(
 86                 .manifest,
 87                 parent.digest,
 88             );
 89             if (parent_retention == .existing) return;
 90             current = parent;
 91         } else {
 92             return;
 93         }
 94     }
 95     return error.DeltaChainCapacityExceeded;
 96 }
 97 
 98 fn validateSnapshot(
 99     capacity: types.CollectionCapacity,
100     snapshot: types.CollectionSnapshot,
101 ) types.StorageError!void {
102     const limits = capacity.limits;
103     inline for (types.reachability_classes) |class| {
104         if (snapshot.seeds.count(class) > limits.seeds.count(class)) {
105             return error.RootCollectionCapacityExceeded;
106         }
107     }
108     if (snapshot.roots > limits.roots or
109         snapshot.objects > limits.objects or
110         snapshot.blocks > limits.blocks)
111     {
112         return error.RootCollectionCapacityExceeded;
113     }
114 }
115 
116 fn validateReport(
117     snapshot: types.CollectionSnapshot,
118     report: types.CollectionReport,
119 ) void {
120     std.debug.assert(
121         @as(u32, report.retained_roots) + report.collected_roots ==
122             snapshot.roots,
123     );
124     std.debug.assert(
125         report.retained_objects + report.collected_objects == snapshot.objects,
126     );
127     std.debug.assert(
128         @as(u32, report.retained_blocks) + report.collected_blocks ==
129             snapshot.blocks,
130     );
131 }