tigerbeetle-node 0.11.2 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- 5e64232ef290c0ed5f8b48762a2f9d03683e4a2ac5d74fc06ea594c035ec4b8b dist/client.node
1
+ cd58e7f1a46beef96d550cfb8423c01542980ff7f9832127e73ad2e5cd0e3a63 dist/client.node
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tigerbeetle-node",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "TigerBeetle Node.js client",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -2,6 +2,8 @@ const std = @import("std");
2
2
  const assert = std.debug.assert;
3
3
  const vsr = @import("vsr.zig");
4
4
 
5
+ const build_options = @import("tigerbeetle_build_options");
6
+
5
7
  const Environment = enum {
6
8
  development,
7
9
  production,
@@ -330,6 +332,10 @@ pub const verify = true;
330
332
  pub const journal_size_headers = journal_slot_count * @sizeOf(vsr.Header);
331
333
  pub const journal_size_prepares = journal_slot_count * message_size_max;
332
334
 
335
+ // Which backend to use for ./tracer.zig.
336
+ // Default is `.none`.
337
+ pub const tracer_backend = build_options.tracer_backend;
338
+
333
339
  // TODO Move these into a separate `config_valid.zig` which we import here:
334
340
  comptime {
335
341
  // vsr.parse_address assumes that config.address/config.port are valid.
@@ -160,6 +160,24 @@ pub fn ewah(comptime Word: type) type {
160
160
  };
161
161
  }
162
162
 
163
+ test "ewah encode→decode cycle" {
164
+ const fuzz = @import("./ewah_fuzz.zig");
165
+ var prng = std.rand.DefaultPrng.init(123);
166
+
167
+ inline for (.{ u8, u16, u32, u64, usize }) |Word| {
168
+ var decoded: [4096]Word = undefined;
169
+
170
+ std.mem.set(Word, &decoded, 0);
171
+ try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
172
+
173
+ std.mem.set(Word, &decoded, std.math.maxInt(Word));
174
+ try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
175
+
176
+ prng.random().bytes(std.mem.asBytes(&decoded));
177
+ try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
178
+ }
179
+ }
180
+
163
181
  test "ewah Word=u8 decode→encode→decode" {
164
182
  try test_decode_with_word(u8);
165
183
 
@@ -179,35 +197,6 @@ test "ewah Word=u8 decode→encode→decode" {
179
197
  }
180
198
  }
181
199
 
182
- test "ewah Word=u8 encode→decode→encode" {
183
- const codec = ewah(u8);
184
-
185
- var seed: u64 = undefined;
186
- try std.os.getrandom(mem.asBytes(&seed));
187
-
188
- var prng = std.rand.DefaultPrng.init(seed);
189
- const random = prng.random();
190
-
191
- var decoded_expect: [4096]u8 = undefined;
192
- var decoded_actual: [4096]u8 = undefined;
193
-
194
- const encoded_actual = try std.testing.allocator.alignedAlloc(
195
- u8,
196
- @alignOf(u8),
197
- codec.encode_size_max(decoded_expect.len),
198
- );
199
- defer std.testing.allocator.free(encoded_actual);
200
-
201
- var t: usize = 0;
202
- while (t < 100) : (t += 1) {
203
- random.bytes(decoded_expect[0..]);
204
- _ = codec.encode(decoded_expect[0..], encoded_actual);
205
- const decoded_actual_length = codec.decode(encoded_actual[0..], decoded_actual[0..]);
206
- try std.testing.expectEqual(decoded_expect.len, decoded_actual_length);
207
- try std.testing.expectEqual(decoded_expect, decoded_actual);
208
- }
209
- }
210
-
211
200
  test "ewah Word=u16" {
212
201
  try test_decode_with_word(u16);
213
202
  }
@@ -0,0 +1,128 @@
1
+ //! Fuzz EWAH encode/decode cycle.
2
+ const std = @import("std");
3
+ const assert = std.debug.assert;
4
+ const log = std.log.scoped(.fuzz_ewah);
5
+
6
+ const ewah = @import("./ewah.zig");
7
+ const fuzz = @import("./test/fuzz.zig");
8
+
9
+ pub fn main() !void {
10
+ const allocator = std.testing.allocator;
11
+ const args = try fuzz.parse_fuzz_args(allocator);
12
+
13
+ inline for (.{ u8, u16, u32, u64, usize }) |Word| {
14
+ var prng = std.rand.DefaultPrng.init(args.seed);
15
+ const random = prng.random();
16
+
17
+ const decoded_size_max = @divExact(1024 * 1024, @sizeOf(Word));
18
+ const decoded_size = 1 + random.uintLessThan(usize, decoded_size_max);
19
+ const decoded = try allocator.alloc(Word, decoded_size);
20
+ defer allocator.free(decoded);
21
+
22
+ const decoded_bits_total = decoded_size * @bitSizeOf(Word);
23
+ const decoded_bits = random.uintLessThan(usize, decoded_bits_total);
24
+ generate_bits(random, std.mem.sliceAsBytes(decoded[0..decoded_size]), decoded_bits);
25
+
26
+ var context = try ContextType(Word).init(allocator, decoded.len);
27
+ defer context.deinit(allocator);
28
+
29
+ const encoded_size = try context.test_encode_decode(decoded);
30
+
31
+ log.info("word={} decoded={} encoded={} compression_ratio={d:.2} set={d:.2}", .{
32
+ Word,
33
+ decoded_size,
34
+ encoded_size,
35
+ @intToFloat(f64, decoded_size) / @intToFloat(f64, encoded_size),
36
+ @intToFloat(f64, decoded_bits) / @intToFloat(f64, decoded_bits_total),
37
+ });
38
+ }
39
+ }
40
+
41
+ pub fn fuzz_encode_decode(
42
+ comptime Word: type,
43
+ allocator: std.mem.Allocator,
44
+ decoded: []const Word,
45
+ ) !void {
46
+ var context = try ContextType(Word).init(allocator, decoded.len);
47
+ defer context.deinit(allocator);
48
+
49
+ _ = try context.test_encode_decode(decoded);
50
+ }
51
+
52
+ /// Modify `data` such that it has exactly `bits_set_total` randomly-chosen bits set,
53
+ /// with the remaining bits unset.
54
+ fn generate_bits(random: std.rand.Random, data: []u8, bits_set_total: usize) void {
55
+ const bits_total = data.len * @bitSizeOf(u8);
56
+ assert(bits_set_total <= bits_total);
57
+
58
+ // Start off full or empty to save some work.
59
+ const init_empty = bits_set_total < @divExact(bits_total, 2);
60
+ std.mem.set(u8, data, if (init_empty) @as(u8, 0) else std.math.maxInt(u8));
61
+
62
+ var bits_set = if (init_empty) 0 else bits_total;
63
+ while (bits_set != bits_set_total) {
64
+ const bit = random.uintLessThan(usize, bits_total);
65
+ const word = @divFloor(bit, @bitSizeOf(u8));
66
+ const mask = @as(u8, 1) << @intCast(std.math.Log2Int(u8), bit % @bitSizeOf(u8));
67
+
68
+ if (init_empty) {
69
+ if (data[word] & mask != 0) continue;
70
+ data[word] |= mask;
71
+ bits_set += 1;
72
+ } else {
73
+ if (data[word] & mask == 0) continue;
74
+ data[word] &= ~mask;
75
+ bits_set -= 1;
76
+ }
77
+ }
78
+ }
79
+
80
+ fn ContextType(comptime Word: type) type {
81
+ return struct {
82
+ const Self = @This();
83
+ const Codec = ewah.ewah(Word);
84
+
85
+ decoded_actual: []Word,
86
+ encoded_actual: []align(@alignOf(Word)) u8,
87
+
88
+ fn init(allocator: std.mem.Allocator, size_max: usize) !Self {
89
+ const decoded_actual = try allocator.alloc(Word, size_max);
90
+ errdefer allocator.free(decoded_actual);
91
+
92
+ const encoded_actual = try allocator.alignedAlloc(
93
+ u8,
94
+ @alignOf(Word),
95
+ Codec.encode_size_max(size_max),
96
+ );
97
+ errdefer allocator.free(encoded_actual);
98
+
99
+ return Self{
100
+ .decoded_actual = decoded_actual,
101
+ .encoded_actual = encoded_actual,
102
+ };
103
+ }
104
+
105
+ fn deinit(context: *Self, allocator: std.mem.Allocator) void {
106
+ allocator.free(context.decoded_actual);
107
+ allocator.free(context.encoded_actual);
108
+ }
109
+
110
+ fn test_encode_decode(context: Self, decoded_expect: []const Word) !usize {
111
+ assert(decoded_expect.len > 0);
112
+
113
+ const encoded_size = Codec.encode(decoded_expect, context.encoded_actual);
114
+ const decoded_actual_size = Codec.decode(
115
+ context.encoded_actual[0..encoded_size],
116
+ context.decoded_actual[0..],
117
+ );
118
+
119
+ try std.testing.expectEqual(decoded_expect.len, decoded_actual_size);
120
+ try std.testing.expectEqualSlices(
121
+ Word,
122
+ decoded_expect,
123
+ context.decoded_actual[0..decoded_actual_size],
124
+ );
125
+ return encoded_size;
126
+ }
127
+ };
128
+ }
@@ -36,6 +36,8 @@ const math = std.math;
36
36
  const assert = std.debug.assert;
37
37
 
38
38
  const log = std.log.scoped(.compaction);
39
+ const tracer = @import("../tracer.zig");
40
+
39
41
  const config = @import("../config.zig");
40
42
 
41
43
  const GridType = @import("grid.zig").GridType;
@@ -120,6 +122,8 @@ pub fn CompactionType(
120
122
  done,
121
123
  };
122
124
 
125
+ tree_name: []const u8,
126
+
123
127
  grid: *Grid,
124
128
  grid_reservation: Grid.Reservation,
125
129
  range: Manifest.CompactionRange,
@@ -155,7 +159,9 @@ pub fn CompactionType(
155
159
 
156
160
  tables_output_count: usize = 0,
157
161
 
158
- pub fn init(allocator: mem.Allocator) !Compaction {
162
+ tracer_slot: ?tracer.SpanStart = null,
163
+
164
+ pub fn init(allocator: mem.Allocator, tree_name: []const u8) !Compaction {
159
165
  var iterator_a = try IteratorA.init(allocator);
160
166
  errdefer iterator_a.deinit(allocator);
161
167
 
@@ -166,6 +172,8 @@ pub fn CompactionType(
166
172
  errdefer table_builder.deinit(allocator);
167
173
 
168
174
  return Compaction{
175
+ .tree_name = tree_name,
176
+
169
177
  // Assigned by start()
170
178
  .grid = undefined,
171
179
  .grid_reservation = undefined,
@@ -216,6 +224,7 @@ pub fn CompactionType(
216
224
  assert(compaction.callback == null);
217
225
  assert(compaction.io_pending == 0);
218
226
  assert(!compaction.merge_done and compaction.merge_iterator == null);
227
+ assert(compaction.tracer_slot == null);
219
228
 
220
229
  assert(op_min % @divExact(config.lsm_batch_multiple, 2) == 0);
221
230
  assert(range.table_count > 0);
@@ -230,6 +239,8 @@ pub fn CompactionType(
230
239
  assert(drop_tombstones or level_b < config.lsm_levels - 1);
231
240
 
232
241
  compaction.* = .{
242
+ .tree_name = compaction.tree_name,
243
+
233
244
  .grid = grid,
234
245
  // Reserve enough blocks to write our output tables in the worst case, where:
235
246
  // - no tombstones are dropped,
@@ -337,6 +348,15 @@ pub fn CompactionType(
337
348
 
338
349
  compaction.callback = callback;
339
350
 
351
+ tracer.start(
352
+ &compaction.tracer_slot,
353
+ .{ .tree = .{ .tree_name = compaction.tree_name } },
354
+ .{ .tree_compaction_tick = .{
355
+ .tree_name = compaction.tree_name,
356
+ .level_b = compaction.level_b,
357
+ } },
358
+ );
359
+
340
360
  // Generate fake IO to make sure io_pending doesn't reach zero multiple times from
341
361
  // IO being completed inline down below.
342
362
  // The fake IO is immediately resolved and triggers the cpu_merge_start if all
@@ -405,6 +425,16 @@ pub fn CompactionType(
405
425
  assert(compaction.io_pending == 0);
406
426
  assert(!compaction.merge_done);
407
427
 
428
+ var tracer_slot: ?tracer.SpanStart = null;
429
+ tracer.start(
430
+ &tracer_slot,
431
+ .{ .tree = .{ .tree_name = compaction.tree_name } },
432
+ .{ .tree_compaction_merge = .{
433
+ .tree_name = compaction.tree_name,
434
+ .level_b = compaction.level_b,
435
+ } },
436
+ );
437
+
408
438
  // Create the merge iterator only when we can peek() from the read iterators.
409
439
  // This happens after IO for the first reads complete.
410
440
  if (compaction.merge_iterator == null) {
@@ -422,6 +452,23 @@ pub fn CompactionType(
422
452
  compaction.cpu_merge_finish();
423
453
  }
424
454
 
455
+ tracer.end(
456
+ &tracer_slot,
457
+ .{ .tree = .{ .tree_name = compaction.tree_name } },
458
+ .{ .tree_compaction_merge = .{
459
+ .tree_name = compaction.tree_name,
460
+ .level_b = compaction.level_b,
461
+ } },
462
+ );
463
+ tracer.end(
464
+ &compaction.tracer_slot,
465
+ .{ .tree = .{ .tree_name = compaction.tree_name } },
466
+ .{ .tree_compaction_tick = .{
467
+ .tree_name = compaction.tree_name,
468
+ .level_b = compaction.level_b,
469
+ } },
470
+ );
471
+
425
472
  // TODO Implement pacing here by deciding if we should do another compact_tick()
426
473
  // instead of invoking the callback, using compaction.range.table_count as the heuristic.
427
474
 
@@ -560,6 +607,7 @@ pub fn CompactionType(
560
607
  assert(compaction.callback == null);
561
608
  assert(compaction.io_pending == 0);
562
609
  assert(compaction.merge_done);
610
+ assert(compaction.tracer_slot == null);
563
611
 
564
612
  // TODO(Beat Pacing) This should really be where the compaction callback is invoked,
565
613
  // but currently that can occur multiple times per beat.
@@ -6,7 +6,9 @@ const assert = std.debug.assert;
6
6
  const config = @import("../config.zig");
7
7
  const fuzz = @import("../test/fuzz.zig");
8
8
  const vsr = @import("../vsr.zig");
9
+
9
10
  const log = std.log.scoped(.lsm_forest_fuzz);
11
+ const tracer = @import("../tracer.zig");
10
12
 
11
13
  const MessagePool = @import("../message_pool.zig").MessagePool;
12
14
  const Transfer = @import("../tigerbeetle.zig").Transfer;
@@ -395,6 +397,9 @@ pub fn generate_fuzz_ops(random: std.rand.Random) ![]const FuzzOp {
395
397
  }
396
398
 
397
399
  pub fn main() !void {
400
+ try tracer.init(allocator);
401
+ defer tracer.deinit(allocator);
402
+
398
403
  const fuzz_args = try fuzz.parse_fuzz_args(allocator);
399
404
  var rng = std.rand.DefaultPrng.init(fuzz_args.seed);
400
405
  const random = rng.random();
@@ -7,6 +7,7 @@ const mem = std.mem;
7
7
  const os = std.os;
8
8
 
9
9
  const log = std.log.scoped(.tree);
10
+ const tracer = @import("../tracer.zig");
10
11
 
11
12
  const config = @import("../config.zig");
12
13
  const div_ceil = @import("../util.zig").div_ceil;
@@ -144,6 +145,8 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
144
145
  checkpoint_callback: ?fn (*Tree) void,
145
146
  open_callback: ?fn (*Tree) void,
146
147
 
148
+ tracer_slot: ?tracer.SpanStart = null,
149
+
147
150
  pub const Options = struct {
148
151
  /// The maximum number of keys that may be committed per batch.
149
152
  ///
@@ -188,13 +191,13 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
188
191
  var manifest = try Manifest.init(allocator, node_pool, grid, tree_hash);
189
192
  errdefer manifest.deinit(allocator);
190
193
 
191
- var compaction_table_immutable = try CompactionTableImmutable.init(allocator);
194
+ var compaction_table_immutable = try CompactionTableImmutable.init(allocator, tree_name);
192
195
  errdefer compaction_table_immutable.deinit(allocator);
193
196
 
194
197
  var compaction_table: [@divFloor(config.lsm_levels, 2)]CompactionTable = undefined;
195
198
  for (compaction_table) |*compaction, i| {
196
199
  errdefer for (compaction_table[0..i]) |*c| c.deinit(allocator);
197
- compaction.* = try CompactionTable.init(allocator);
200
+ compaction.* = try CompactionTable.init(allocator, tree_name);
198
201
  }
199
202
  errdefer for (compaction_table) |*c| c.deinit(allocator);
200
203
 
@@ -221,6 +224,8 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
221
224
  }
222
225
 
223
226
  pub fn deinit(tree: *Tree, allocator: mem.Allocator) void {
227
+ assert(tree.tracer_slot == null);
228
+
224
229
  tree.compaction_table_immutable.deinit(allocator);
225
230
  for (tree.compaction_table) |*compaction| compaction.deinit(allocator);
226
231
 
@@ -548,6 +553,12 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
548
553
  assert(tree.compaction_io_pending == 0);
549
554
  assert(tree.compaction_callback == null);
550
555
 
556
+ tracer.start(
557
+ &tree.tracer_slot,
558
+ .{ .tree = .{ .tree_name = tree_name } },
559
+ .{ .tree_compaction_beat = .{ .tree_name = tree_name } },
560
+ );
561
+
551
562
  tree.compaction_callback = callback;
552
563
 
553
564
  const compaction_beat = tree.compaction_op % config.lsm_batch_multiple;
@@ -917,6 +928,12 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
917
928
  assert(tree.compaction_io_pending == 0);
918
929
  assert(tree.table_mutable.can_commit_batch(tree.options.commit_entries_max));
919
930
 
931
+ tracer.end(
932
+ &tree.tracer_slot,
933
+ .{ .tree = .{ .tree_name = tree_name } },
934
+ .{ .tree_compaction_beat = .{ .tree_name = tree_name } },
935
+ );
936
+
920
937
  // Invoke the compact() callback after the manifest compacts at the end of the beat.
921
938
  const callback = tree.compaction_callback.?;
922
939
  tree.compaction_callback = null;
@@ -6,7 +6,9 @@ const assert = std.debug.assert;
6
6
  const config = @import("../config.zig");
7
7
  const fuzz = @import("../test/fuzz.zig");
8
8
  const vsr = @import("../vsr.zig");
9
+
9
10
  const log = std.log.scoped(.lsm_tree_fuzz);
11
+ const tracer = @import("../tracer.zig");
10
12
 
11
13
  const MessagePool = @import("../message_pool.zig").MessagePool;
12
14
  const Transfer = @import("../tigerbeetle.zig").Transfer;
@@ -27,7 +29,7 @@ const Table = @import("table.zig").TableType(
27
29
  Key.tombstone,
28
30
  Key.tombstone_from_key,
29
31
  );
30
- const Tree = @import("tree.zig").TreeType(Table, Storage, @typeName(Table) ++ "_test");
32
+ const Tree = @import("tree.zig").TreeType(Table, Storage, "Key.Value");
31
33
 
32
34
  const Grid = GridType(Storage);
33
35
  const SuperBlock = vsr.SuperBlockType(Storage);
@@ -440,6 +442,9 @@ pub fn generate_fuzz_ops(random: std.rand.Random) ![]const FuzzOp {
440
442
  }
441
443
 
442
444
  pub fn main() !void {
445
+ try tracer.init(allocator);
446
+ defer tracer.deinit(allocator);
447
+
443
448
  const fuzz_args = try fuzz.parse_fuzz_args(allocator);
444
449
  var rng = std.rand.DefaultPrng.init(fuzz_args.seed);
445
450
  const random = rng.random();
@@ -9,6 +9,7 @@ const log = std.log.scoped(.main);
9
9
  const build_options = @import("tigerbeetle_build_options");
10
10
  const config = @import("config.zig");
11
11
  pub const log_level: std.log.Level = @intToEnum(std.log.Level, config.log_level);
12
+ const tracer = @import("tracer.zig");
12
13
 
13
14
  const cli = @import("cli.zig");
14
15
  const fatal = cli.fatal;
@@ -130,6 +131,9 @@ const Command = struct {
130
131
  ) !void {
131
132
  const allocator = arena.allocator();
132
133
 
134
+ try tracer.init(allocator);
135
+ defer tracer.deinit(allocator);
136
+
133
137
  var command: Command = undefined;
134
138
  try command.init(allocator, path, false);
135
139
  defer command.deinit(allocator);
@@ -333,7 +333,7 @@ pub fn main() !void {
333
333
  var crashes = cluster.replica_normal_count() -| replica_normal_min;
334
334
 
335
335
  for (cluster.storages) |*storage, replica| {
336
- if (cluster.replicas[replica].journal.recovered) {
336
+ if (cluster.replicas[replica].journal.status == .recovered) {
337
337
  // TODO Remove this workaround when VSR recovery protocol is disabled.
338
338
  // When only the minimum number of replicas are healthy (no more crashes allowed),
339
339
  // disable storage faults on all healthy replicas.
@@ -2,7 +2,9 @@ const std = @import("std");
2
2
  const assert = std.debug.assert;
3
3
  const math = std.math;
4
4
  const mem = std.mem;
5
+
5
6
  const log = std.log.scoped(.state_machine);
7
+ const tracer = @import("tracer.zig");
6
8
 
7
9
  const tb = @import("tigerbeetle.zig");
8
10
  const snapshot_latest = @import("lsm/tree.zig").snapshot_latest;
@@ -114,6 +116,8 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
114
116
  compact_callback: ?fn (*StateMachine) void = null,
115
117
  checkpoint_callback: ?fn (*StateMachine) void = null,
116
118
 
119
+ tracer_slot: ?tracer.SpanStart,
120
+
117
121
  pub fn init(allocator: mem.Allocator, grid: *Grid, options: Options) !StateMachine {
118
122
  var forest = try Forest.init(
119
123
  allocator,
@@ -127,10 +131,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
127
131
  .prepare_timestamp = 0,
128
132
  .commit_timestamp = 0,
129
133
  .forest = forest,
134
+ .tracer_slot = null,
130
135
  };
131
136
  }
132
137
 
133
138
  pub fn deinit(self: *StateMachine, allocator: mem.Allocator) void {
139
+ assert(self.tracer_slot == null);
140
+
134
141
  self.forest.deinit(allocator);
135
142
  }
136
143
 
@@ -218,6 +225,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
218
225
  return;
219
226
  }
220
227
 
228
+ tracer.start(
229
+ &self.tracer_slot,
230
+ .main,
231
+ .state_machine_prefetch,
232
+ );
233
+
221
234
  self.prefetch_input = input;
222
235
  self.prefetch_callback = callback;
223
236
 
@@ -248,6 +261,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
248
261
  const callback = self.prefetch_callback.?;
249
262
  self.prefetch_input = null;
250
263
  self.prefetch_callback = null;
264
+
265
+ tracer.end(
266
+ &self.tracer_slot,
267
+ .main,
268
+ .state_machine_prefetch,
269
+ );
270
+
251
271
  callback(self);
252
272
  }
253
273
 
@@ -367,6 +387,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
367
387
  _ = client;
368
388
  assert(op != 0);
369
389
 
390
+ tracer.start(
391
+ &self.tracer_slot,
392
+ .main,
393
+ .state_machine_commit,
394
+ );
395
+
370
396
  const result = switch (operation) {
371
397
  .root => unreachable,
372
398
  .register => 0,
@@ -377,6 +403,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
377
403
  else => unreachable,
378
404
  };
379
405
 
406
+ tracer.end(
407
+ &self.tracer_slot,
408
+ .main,
409
+ .state_machine_commit,
410
+ );
411
+
380
412
  return result;
381
413
  }
382
414
 
@@ -384,6 +416,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
384
416
  assert(self.compact_callback == null);
385
417
  assert(self.checkpoint_callback == null);
386
418
 
419
+ tracer.start(
420
+ &self.tracer_slot,
421
+ .main,
422
+ .state_machine_compact,
423
+ );
424
+
387
425
  self.compact_callback = callback;
388
426
  self.forest.compact(compact_finish, op);
389
427
  }
@@ -392,6 +430,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
392
430
  const self = @fieldParentPtr(StateMachine, "forest", forest);
393
431
  const callback = self.compact_callback.?;
394
432
  self.compact_callback = null;
433
+
434
+ tracer.end(
435
+ &self.tracer_slot,
436
+ .main,
437
+ .state_machine_compact,
438
+ );
439
+
395
440
  callback(self);
396
441
  }
397
442
 
@@ -72,7 +72,7 @@ pub const StateChecker = struct {
72
72
  pub fn check_state(state_checker: *StateChecker, replica_index: u8) !void {
73
73
  const replica = state_checker.replicas[replica_index];
74
74
  const commit_header = header: {
75
- if (replica.journal.recovered) {
75
+ if (replica.journal.status == .recovered) {
76
76
  const commit_header = replica.journal.header_with_op(replica.commit_min);
77
77
  assert(commit_header != null or replica.commit_min == replica.op_checkpoint);
78
78
  break :header replica.journal.header_with_op(replica.commit_min);
@@ -751,6 +751,15 @@ pub const Storage = struct {
751
751
  }
752
752
  }
753
753
 
754
+ pub fn superblock_sector(
755
+ storage: *const Storage,
756
+ copy_: u8,
757
+ ) *const superblock.SuperBlockSector {
758
+ const offset = vsr.Zone.superblock.offset(superblock.Layout.offset_sector(copy_));
759
+ const bytes = storage.memory[offset..][0..@sizeOf(superblock.SuperBlockSector)];
760
+ return mem.bytesAsValue(superblock.SuperBlockSector, bytes);
761
+ }
762
+
754
763
  pub fn wal_headers(storage: *const Storage) []const vsr.Header {
755
764
  const offset = vsr.Zone.wal_headers.offset(0);
756
765
  const size = vsr.Zone.wal_headers.size().?;