lib/sql/src/history/instrumentation.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 pub const Phase = enum {
  4     batch_load,
  5     hash_verification,
  6     index_rebuild,
  7 };
  8 
  9 pub const Receipt = struct {
 10     sequence: usize,
 11     batch: usize,
 12     phase: Phase,
 13     duration_ns: u64,
 14     bytes: usize,
 15     records: usize,
 16 };
 17 
 18 pub const Recorder = struct {
 19     storage: []Receipt,
 20     len: usize = 0,
 21     required: usize = 0,
 22 
 23     pub fn init(storage: []Receipt) Recorder {
 24         return .{ .storage = storage };
 25     }
 26 
 27     pub fn reset(self: *Recorder) void {
 28         self.len = 0;
 29         self.required = 0;
 30     }
 31 
 32     pub fn record(
 33         self: *Recorder,
 34         batch: usize,
 35         phase: Phase,
 36         duration_ns: u64,
 37         bytes: usize,
 38         records: usize,
 39     ) void {
 40         const sequence = self.required;
 41         self.required += 1;
 42         if (self.len == self.storage.len) return;
 43         self.storage[self.len] = .{
 44             .sequence = sequence,
 45             .batch = batch,
 46             .phase = phase,
 47             .duration_ns = duration_ns,
 48             .bytes = bytes,
 49             .records = records,
 50         };
 51         self.len += 1;
 52     }
 53 
 54     pub fn receipts(self: *const Recorder) []const Receipt {
 55         return self.storage[0..self.len];
 56     }
 57 
 58     pub fn complete(self: *const Recorder) bool {
 59         return self.len != 0 and self.len == self.required;
 60     }
 61 
 62     pub fn ordered(self: *const Recorder) bool {
 63         if (!self.complete() or self.len % 3 != 0) return false;
 64         for (self.receipts(), 0..) |receipt, sequence| {
 65             if (receipt.sequence != sequence) return false;
 66             if (receipt.batch != sequence / 3) return false;
 67             const expected: Phase = switch (sequence % 3) {
 68                 0 => .batch_load,
 69                 1 => .hash_verification,
 70                 2 => .index_rebuild,
 71                 else => unreachable,
 72             };
 73             if (receipt.phase != expected) return false;
 74         }
 75         return true;
 76     }
 77 };
 78 
 79 pub fn elapsed(start_ns: i128, end_ns: i128) u64 {
 80     if (end_ns <= start_ns) return 0;
 81     return @intCast(@min(
 82         @as(u128, @intCast(end_ns - start_ns)),
 83         std.math.maxInt(u64),
 84     ));
 85 }
 86 
 87 test "replay recorder rejects incomplete storage and preserves phase order" {
 88     var complete_storage: [6]Receipt = undefined;
 89     var complete = Recorder.init(&complete_storage);
 90     inline for (0..2) |batch| {
 91         complete.record(batch, .batch_load, 1, 16, 2);
 92         complete.record(batch, .hash_verification, 2, 16, 2);
 93         complete.record(batch, .index_rebuild, 3, 16, 2);
 94     }
 95     try std.testing.expect(complete.complete());
 96     try std.testing.expect(complete.ordered());
 97 
 98     var short_storage: [2]Receipt = undefined;
 99     var short = Recorder.init(&short_storage);
100     short.record(0, .batch_load, 1, 16, 2);
101     short.record(0, .hash_verification, 2, 16, 2);
102     short.record(0, .index_rebuild, 3, 16, 2);
103     try std.testing.expect(!short.complete());
104     try std.testing.expect(!short.ordered());
105 }