tigerbeetle-node 0.11.8 → 0.11.10

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.
Files changed (85) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +4 -3
  3. package/scripts/build_lib.sh +41 -0
  4. package/src/node.zig +1 -1
  5. package/src/tigerbeetle/scripts/validate_docs.sh +7 -1
  6. package/src/tigerbeetle/src/benchmark.zig +3 -3
  7. package/src/tigerbeetle/src/config.zig +31 -16
  8. package/src/tigerbeetle/src/constants.zig +48 -9
  9. package/src/tigerbeetle/src/ewah.zig +5 -5
  10. package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
  11. package/src/tigerbeetle/src/lsm/binary_search.zig +1 -1
  12. package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
  13. package/src/tigerbeetle/src/lsm/compaction.zig +34 -21
  14. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +84 -104
  15. package/src/tigerbeetle/src/lsm/grid.zig +19 -13
  16. package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -10
  17. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +18 -13
  18. package/src/tigerbeetle/src/lsm/merge_iterator.zig +1 -1
  19. package/src/tigerbeetle/src/lsm/segmented_array.zig +17 -17
  20. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +1 -1
  21. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +1 -1
  22. package/src/tigerbeetle/src/lsm/table.zig +8 -20
  23. package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
  24. package/src/tigerbeetle/src/lsm/table_iterator.zig +3 -3
  25. package/src/tigerbeetle/src/lsm/table_mutable.zig +14 -2
  26. package/src/tigerbeetle/src/lsm/test.zig +5 -4
  27. package/src/tigerbeetle/src/lsm/tree.zig +1 -2
  28. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +85 -115
  29. package/src/tigerbeetle/src/message_bus.zig +4 -4
  30. package/src/tigerbeetle/src/message_pool.zig +7 -10
  31. package/src/tigerbeetle/src/ring_buffer.zig +22 -12
  32. package/src/tigerbeetle/src/simulator.zig +366 -239
  33. package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
  34. package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
  35. package/src/tigerbeetle/src/state_machine.zig +190 -178
  36. package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
  37. package/src/tigerbeetle/src/storage.zig +13 -6
  38. package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
  39. package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
  40. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
  41. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
  42. package/src/tigerbeetle/src/testing/cluster.zig +443 -0
  43. package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
  44. package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
  45. package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
  46. package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
  47. package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
  48. package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
  49. package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
  50. package/src/tigerbeetle/src/testing/storage.zig +757 -0
  51. package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
  52. package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
  53. package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
  54. package/src/tigerbeetle/src/tracer.zig +3 -3
  55. package/src/tigerbeetle/src/unit_tests.zig +4 -4
  56. package/src/tigerbeetle/src/vopr.zig +2 -2
  57. package/src/tigerbeetle/src/vsr/client.zig +5 -2
  58. package/src/tigerbeetle/src/vsr/clock.zig +93 -53
  59. package/src/tigerbeetle/src/vsr/journal.zig +109 -98
  60. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
  61. package/src/tigerbeetle/src/vsr/replica.zig +1983 -1430
  62. package/src/tigerbeetle/src/vsr/replica_format.zig +13 -13
  63. package/src/tigerbeetle/src/vsr/superblock.zig +240 -142
  64. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
  65. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
  66. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  67. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +49 -14
  68. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
  69. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +48 -48
  70. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +51 -51
  71. package/src/tigerbeetle/src/vsr.zig +99 -33
  72. package/src/tigerbeetle/src/demo.zig +0 -132
  73. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  74. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  75. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -37
  76. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  77. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  78. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  79. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  80. package/src/tigerbeetle/src/test/cluster.zig +0 -352
  81. package/src/tigerbeetle/src/test/conductor.zig +0 -366
  82. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
  83. package/src/tigerbeetle/src/test/state_checker.zig +0 -169
  84. package/src/tigerbeetle/src/test/storage.zig +0 -864
  85. package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
@@ -4,7 +4,7 @@ const mem = std.mem;
4
4
 
5
5
  const constants = @import("../constants.zig");
6
6
  const vsr = @import("../vsr.zig");
7
- const util = @import("../util.zig");
7
+ const stdx = @import("../stdx.zig");
8
8
 
9
9
  const MessagePool = @import("../message_pool.zig").MessagePool;
10
10
 
@@ -128,7 +128,7 @@ pub const ClientTable = struct {
128
128
  size = new_size;
129
129
 
130
130
  for (entries) |entry| {
131
- util.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(entry.reply.header));
131
+ stdx.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(entry.reply.header));
132
132
  size += @sizeOf(vsr.Header);
133
133
  }
134
134
 
@@ -138,7 +138,7 @@ pub const ClientTable = struct {
138
138
  size = new_size;
139
139
 
140
140
  for (entries) |entry| {
141
- util.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(&entry.session));
141
+ stdx.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(&entry.session));
142
142
  size += @sizeOf(u64);
143
143
  }
144
144
 
@@ -150,7 +150,7 @@ pub const ClientTable = struct {
150
150
  for (entries) |entry| {
151
151
  const body = entry.reply.body();
152
152
  assert(body.len == (entry.reply.header.size - @sizeOf(vsr.Header)));
153
- util.copy_disjoint(.inexact, u8, target[size..], body);
153
+ stdx.copy_disjoint(.inexact, u8, target[size..], body);
154
154
  size += body.len;
155
155
  }
156
156
 
@@ -159,7 +159,7 @@ pub const ClientTable = struct {
159
159
  std.mem.set(u8, target[size..new_size], 0);
160
160
  size = new_size;
161
161
 
162
- util.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(&entries_count));
162
+ stdx.copy_disjoint(.inexact, u8, target[size..], mem.asBytes(&entries_count));
163
163
  size += @sizeOf(u32);
164
164
 
165
165
  assert(size <= encode_size_max);
@@ -169,7 +169,7 @@ pub const ClientTable = struct {
169
169
  pub fn decode(client_table: *ClientTable, source: []align(@alignOf(vsr.Header)) const u8) void {
170
170
  // Read the entry count at the end of the buffer to determine how many there are.
171
171
  var entries_count: u32 = undefined;
172
- util.copy_disjoint(.exact, u8, mem.asBytes(&entries_count), source[source.len - @sizeOf(u32) ..]);
172
+ stdx.copy_disjoint(.exact, u8, mem.asBytes(&entries_count), source[source.len - @sizeOf(u32) ..]);
173
173
  assert(entries_count <= client_table.sorted.len);
174
174
 
175
175
  assert(client_table.count() == 0);
@@ -214,7 +214,7 @@ pub const ClientTable = struct {
214
214
 
215
215
  // Read the message body for the entry.
216
216
  assert(bodies.len >= body_size);
217
- util.copy_disjoint(.exact, u8, body, bodies[0..body_size]);
217
+ stdx.copy_disjoint(.exact, u8, body, bodies[0..body_size]);
218
218
  bodies = bodies[body_size..];
219
219
  assert(entry.reply.header.valid_checksum_body(body));
220
220
 
@@ -8,7 +8,7 @@ const MaskInt = DynamicBitSetUnmanaged.MaskInt;
8
8
  const constants = @import("../constants.zig");
9
9
 
10
10
  const ewah = @import("../ewah.zig").ewah(usize);
11
- const div_ceil = @import("../util.zig").div_ceil;
11
+ const div_ceil = @import("../stdx.zig").div_ceil;
12
12
 
13
13
  /// This is logically a range of addresses within the FreeSet, but its actual fields are block
14
14
  /// indexes for ease of calculation.
@@ -7,7 +7,7 @@ const log = std.log.scoped(.fuzz_vsr_superblock_free_set);
7
7
 
8
8
  const FreeSet = @import("./superblock_free_set.zig").FreeSet;
9
9
  const Reservation = @import("./superblock_free_set.zig").Reservation;
10
- const fuzz = @import("../test/fuzz.zig");
10
+ const fuzz = @import("../testing/fuzz.zig");
11
11
 
12
12
  pub const tigerbeetle_config = @import("../config.zig").configs.test_min;
13
13
 
@@ -15,16 +15,17 @@ const assert = std.debug.assert;
15
15
  const log = std.log.scoped(.fuzz_vsr_superblock);
16
16
 
17
17
  const constants = @import("../constants.zig");
18
- const util = @import("../util.zig");
18
+ const stdx = @import("../stdx.zig");
19
19
  const vsr = @import("../vsr.zig");
20
- const Storage = @import("../test/storage.zig").Storage;
20
+ const Storage = @import("../testing/storage.zig").Storage;
21
+ const StorageFaultAtlas = @import("../testing/storage.zig").ClusterFaultAtlas;
21
22
  const MessagePool = @import("../message_pool.zig").MessagePool;
22
23
  const superblock_zone_size = @import("superblock.zig").superblock_zone_size;
23
24
  const data_file_size_min = @import("superblock.zig").data_file_size_min;
24
- const VSRState = @import("superblock.zig").SuperBlockSector.VSRState;
25
+ const VSRState = @import("superblock.zig").SuperBlockHeader.VSRState;
25
26
  const SuperBlockType = @import("superblock.zig").SuperBlockType;
26
27
  const SuperBlock = SuperBlockType(Storage);
27
- const fuzz = @import("../test/fuzz.zig");
28
+ const fuzz = @import("../testing/fuzz.zig");
28
29
 
29
30
  pub const tigerbeetle_config = @import("../config.zig").configs.test_min;
30
31
 
@@ -44,7 +45,14 @@ fn run_fuzz(allocator: std.mem.Allocator, seed: u64, transitions_count_total: us
44
45
  var prng = std.rand.DefaultPrng.init(seed);
45
46
  const random = prng.random();
46
47
 
48
+ const storage_fault_atlas = StorageFaultAtlas.init(1, random, .{
49
+ .faulty_superblock = true,
50
+ .faulty_wal_headers = false,
51
+ .faulty_wal_prepares = false,
52
+ });
53
+
47
54
  const storage_options = .{
55
+ .replica_index = 0,
48
56
  .seed = random.int(u64),
49
57
  // SuperBlock's IO is all serial, so latencies never reorder reads/writes.
50
58
  .read_latency_min = 1,
@@ -56,7 +64,7 @@ fn run_fuzz(allocator: std.mem.Allocator, seed: u64, transitions_count_total: us
56
64
  .read_fault_probability = 25 + random.uintLessThan(u8, 76),
57
65
  .write_fault_probability = 25 + random.uintLessThan(u8, 76),
58
66
  .crash_fault_probability = 50 + random.uintLessThan(u8, 51),
59
- .faulty_superblock = true,
67
+ .fault_atlas = &storage_fault_atlas,
60
68
  };
61
69
 
62
70
  var storage = try Storage.init(allocator, superblock_zone_size, storage_options);
@@ -143,9 +151,10 @@ const Environment = struct {
143
151
  /// Indexed by sequence.
144
152
  const SequenceStates = std.ArrayList(struct {
145
153
  vsr_state: VSRState,
154
+ vsr_headers: vsr.ViewChangeHeaders.BoundedArray,
146
155
  /// Track the expected `checksum(free_set)`.
147
156
  /// Note that this is a checksum of the decoded free set; it is not the same as
148
- /// `SuperBlockSector.free_set_checksum`.
157
+ /// `SuperBlockHeader.free_set_checksum`.
149
158
  free_set: u128,
150
159
  });
151
160
 
@@ -158,7 +167,7 @@ const Environment = struct {
158
167
  latest_sequence: u64 = 0,
159
168
  latest_checksum: u128 = 0,
160
169
  latest_parent: u128 = 0,
161
- latest_vsr_state: VSRState = std.mem.zeroInit(VSRState, .{}),
170
+ latest_vsr_state: VSRState = std.mem.zeroes(VSRState),
162
171
 
163
172
  context_format: SuperBlock.Context = undefined,
164
173
  context_open: SuperBlock.Context = undefined,
@@ -224,7 +233,7 @@ const Environment = struct {
224
233
  if (env.latest_checksum != 0) {
225
234
  if (env.latest_sequence + 1 == env.superblock_verify.working.sequence) {
226
235
  // After a checkpoint() or view_change(), the parent points to the previous
227
- // working sector.
236
+ // working header.
228
237
  assert(env.superblock_verify.working.parent == env.latest_checksum);
229
238
  }
230
239
  }
@@ -260,10 +269,14 @@ const Environment = struct {
260
269
  .replica = 0,
261
270
  });
262
271
 
272
+ var vsr_headers = vsr.ViewChangeHeaders.BoundedArray{ .buffer = undefined };
273
+ vsr_headers.appendAssumeCapacity(vsr.Header.root_prepare(cluster));
274
+
263
275
  assert(env.sequence_states.items.len == 0);
264
276
  try env.sequence_states.append(undefined); // skip sequence=0
265
277
  try env.sequence_states.append(.{
266
278
  .vsr_state = VSRState.root(cluster),
279
+ .vsr_headers = vsr_headers,
267
280
  .free_set = checksum_free_set(env.superblock),
268
281
  });
269
282
  }
@@ -298,18 +311,33 @@ const Environment = struct {
298
311
  .commit_min_checksum = env.superblock.staging.vsr_state.commit_min_checksum,
299
312
  .commit_min = env.superblock.staging.vsr_state.commit_min,
300
313
  .commit_max = env.superblock.staging.vsr_state.commit_max + 3,
301
- .view_normal = env.superblock.staging.vsr_state.view_normal + 4,
314
+ .log_view = env.superblock.staging.vsr_state.log_view + 4,
302
315
  .view = env.superblock.staging.vsr_state.view + 5,
303
316
  };
304
317
 
318
+ var vsr_headers = vsr.ViewChangeHeaders.BoundedArray{ .buffer = undefined };
319
+ var vsr_head = std.mem.zeroInit(vsr.Header, .{
320
+ .command = .prepare,
321
+ .op = env.superblock.staging.vsr_state.commit_min,
322
+ });
323
+ vsr_head.set_checksum_body(&.{});
324
+ vsr_head.set_checksum();
325
+ vsr_headers.appendAssumeCapacity(vsr_head);
326
+
305
327
  assert(env.sequence_states.items.len == env.superblock.staging.sequence + 1);
306
328
  try env.sequence_states.append(.{
307
329
  .vsr_state = vsr_state,
330
+ .vsr_headers = vsr_headers,
308
331
  .free_set = env.sequence_states.items[env.sequence_states.items.len - 1].free_set,
309
332
  });
310
333
 
311
334
  env.pending.insert(.view_change);
312
- env.superblock.view_change(view_change_callback, &env.context_view_change, vsr_state);
335
+ env.superblock.view_change(view_change_callback, &env.context_view_change, .{
336
+ .commit_max = vsr_state.commit_max,
337
+ .log_view = vsr_state.log_view,
338
+ .view = vsr_state.view,
339
+ .headers = vsr_headers,
340
+ });
313
341
  }
314
342
 
315
343
  fn view_change_callback(context: *SuperBlock.Context) void {
@@ -326,18 +354,25 @@ const Environment = struct {
326
354
  .commit_min_checksum = env.superblock.staging.vsr_state.commit_min_checksum + 1,
327
355
  .commit_min = env.superblock.staging.vsr_state.commit_min + 1,
328
356
  .commit_max = env.superblock.staging.vsr_state.commit_max + 1,
329
- .view_normal = env.superblock.staging.vsr_state.view_normal + 1,
330
- .view = env.superblock.staging.vsr_state.view + 1,
357
+ .log_view = env.superblock.staging.vsr_state.log_view,
358
+ .view = env.superblock.staging.vsr_state.view,
331
359
  };
332
360
 
333
361
  assert(env.sequence_states.items.len == env.superblock.staging.sequence + 1);
334
362
  try env.sequence_states.append(.{
335
363
  .vsr_state = vsr_state,
364
+ .vsr_headers = vsr.ViewChangeHeaders.BoundedArray.fromSlice(
365
+ env.superblock.staging.vsr_headers().slice,
366
+ ) catch unreachable,
336
367
  .free_set = checksum_free_set(env.superblock),
337
368
  });
338
369
 
339
370
  env.pending.insert(.checkpoint);
340
- env.superblock.checkpoint(checkpoint_callback, &env.context_checkpoint, vsr_state);
371
+ env.superblock.checkpoint(checkpoint_callback, &env.context_checkpoint, .{
372
+ .commit_min_checksum = vsr_state.commit_min_checksum,
373
+ .commit_min = vsr_state.commit_min,
374
+ .commit_max = vsr_state.commit_max,
375
+ });
341
376
  }
342
377
 
343
378
  fn checkpoint_callback(context: *SuperBlock.Context) void {
@@ -350,6 +385,6 @@ const Environment = struct {
350
385
  fn checksum_free_set(superblock: *const SuperBlock) u128 {
351
386
  const mask_bits = @bitSizeOf(std.DynamicBitSetUnmanaged.MaskInt);
352
387
  const count_bits = superblock.free_set.blocks.bit_length;
353
- const count_words = util.div_ceil(count_bits, mask_bits);
388
+ const count_words = stdx.div_ceil(count_bits, mask_bits);
354
389
  return vsr.checksum(std.mem.sliceAsBytes(superblock.free_set.blocks.masks[0..count_words]));
355
390
  }
@@ -6,7 +6,7 @@ const log = std.log.scoped(.superblock_manifest);
6
6
  const mem = std.mem;
7
7
 
8
8
  const constants = @import("../constants.zig");
9
- const util = @import("../util.zig");
9
+ const stdx = @import("../stdx.zig");
10
10
 
11
11
  // TODO Compute & use the upper bound of manifest blocks (per tree) to size the trailer zone.
12
12
 
@@ -29,7 +29,7 @@ pub const Manifest = struct {
29
29
  /// A map from table address to the manifest block and entry that is the latest extent version.
30
30
  /// Used to determine whether a table should be dropped in a compaction.
31
31
  /// Shared by all trees and sized to accommodate all possible tables.
32
- tables: std.AutoHashMapUnmanaged(u64, TableExtent),
32
+ tables: Tables,
33
33
 
34
34
  /// A set of block addresses that have free entries.
35
35
  /// Used to determine whether a block should be compacted.
@@ -37,6 +37,13 @@ pub const Manifest = struct {
37
37
  /// This enables us to track fragmentation even in unflushed blocks.
38
38
  compaction_set: std.AutoHashMapUnmanaged(u64, void),
39
39
 
40
+ pub const Tables = std.AutoHashMapUnmanaged(TableExtentKey, TableExtent);
41
+
42
+ pub const TableExtentKey = struct {
43
+ tree_hash: u128,
44
+ table: u64,
45
+ };
46
+
40
47
  pub const TableExtent = struct {
41
48
  block: u64, // ManifestLog block address.
42
49
  entry: u32, // Index within the ManifestLog Label/TableInfo arrays.
@@ -60,7 +67,7 @@ pub const Manifest = struct {
60
67
  const addresses = try allocator.alloc(u64, manifest_block_count_max);
61
68
  errdefer allocator.free(addresses);
62
69
 
63
- var tables = std.AutoHashMapUnmanaged(u64, TableExtent){};
70
+ var tables = Tables{};
64
71
  try tables.ensureTotalCapacity(allocator, forest_table_count_max);
65
72
  errdefer tables.deinit(allocator);
66
73
 
@@ -100,7 +107,7 @@ pub const Manifest = struct {
100
107
  var size: u64 = 0;
101
108
 
102
109
  const trees = target[size..][0 .. manifest.count * @sizeOf(u128)];
103
- util.copy_disjoint(
110
+ stdx.copy_disjoint(
104
111
  .exact,
105
112
  u128,
106
113
  mem.bytesAsSlice(u128, trees),
@@ -109,7 +116,7 @@ pub const Manifest = struct {
109
116
  size += trees.len;
110
117
 
111
118
  const checksums = target[size..][0 .. manifest.count * @sizeOf(u128)];
112
- util.copy_disjoint(
119
+ stdx.copy_disjoint(
113
120
  .exact,
114
121
  u128,
115
122
  mem.bytesAsSlice(u128, checksums),
@@ -118,7 +125,7 @@ pub const Manifest = struct {
118
125
  size += checksums.len;
119
126
 
120
127
  const addresses = target[size..][0 .. manifest.count * @sizeOf(u64)];
121
- util.copy_disjoint(
128
+ stdx.copy_disjoint(
122
129
  .exact,
123
130
  u64,
124
131
  mem.bytesAsSlice(u64, addresses),
@@ -144,7 +151,7 @@ pub const Manifest = struct {
144
151
  var size: u64 = 0;
145
152
 
146
153
  const trees = source[size..][0 .. manifest.count * @sizeOf(u128)];
147
- util.copy_disjoint(
154
+ stdx.copy_disjoint(
148
155
  .exact,
149
156
  u128,
150
157
  manifest.trees[0..manifest.count],
@@ -153,7 +160,7 @@ pub const Manifest = struct {
153
160
  size += trees.len;
154
161
 
155
162
  const checksums = source[size..][0 .. manifest.count * @sizeOf(u128)];
156
- util.copy_disjoint(
163
+ stdx.copy_disjoint(
157
164
  .exact,
158
165
  u128,
159
166
  manifest.checksums[0..manifest.count],
@@ -162,7 +169,7 @@ pub const Manifest = struct {
162
169
  size += checksums.len;
163
170
 
164
171
  const addresses = source[size..][0 .. manifest.count * @sizeOf(u64)];
165
- util.copy_disjoint(
172
+ stdx.copy_disjoint(
166
173
  .exact,
167
174
  u64,
168
175
  manifest.addresses[0..manifest.count],
@@ -224,9 +231,9 @@ pub const Manifest = struct {
224
231
  manifest.verify_index_tree_checksum_address(index, tree, checksum, address);
225
232
 
226
233
  const tail = manifest.count - (index + 1);
227
- util.copy_left(.inexact, u128, manifest.trees[index..], manifest.trees[index + 1 ..][0..tail]);
228
- util.copy_left(.inexact, u128, manifest.checksums[index..], manifest.checksums[index + 1 ..][0..tail]);
229
- util.copy_left(.inexact, u64, manifest.addresses[index..], manifest.addresses[index + 1 ..][0..tail]);
234
+ stdx.copy_left(.inexact, u128, manifest.trees[index..], manifest.trees[index + 1 ..][0..tail]);
235
+ stdx.copy_left(.inexact, u128, manifest.checksums[index..], manifest.checksums[index + 1 ..][0..tail]);
236
+ stdx.copy_left(.inexact, u64, manifest.addresses[index..], manifest.addresses[index + 1 ..][0..tail]);
230
237
  manifest.count -= 1;
231
238
 
232
239
  manifest.trees[manifest.count] = 0;
@@ -293,11 +300,14 @@ pub const Manifest = struct {
293
300
 
294
301
  /// Inserts the table extent if it does not yet exist, and returns true.
295
302
  /// Otherwise, returns false.
296
- pub fn insert_table_extent(manifest: *Manifest, table: u64, block: u64, entry: u32) bool {
303
+ pub fn insert_table_extent(manifest: *Manifest, tree_hash: u128, table: u64, block: u64, entry: u32) bool {
297
304
  assert(table > 0);
298
305
  assert(block > 0);
299
306
 
300
- var extent = manifest.tables.getOrPutAssumeCapacity(table);
307
+ var extent = manifest.tables.getOrPutAssumeCapacity(.{
308
+ .tree_hash = tree_hash,
309
+ .table = table,
310
+ });
301
311
  if (extent.found_existing) return false;
302
312
 
303
313
  extent.value_ptr.* = .{
@@ -311,11 +321,14 @@ pub const Manifest = struct {
311
321
  /// Inserts or updates the table extent, and returns the previous block address if any.
312
322
  /// The table extent must be updated immediately when appending, without delay.
313
323
  /// Otherwise, ManifestLog.compact() may append a stale version over the latest.
314
- pub fn update_table_extent(manifest: *Manifest, table: u64, block: u64, entry: u32) ?u64 {
324
+ pub fn update_table_extent(manifest: *Manifest, tree_hash: u128, table: u64, block: u64, entry: u32) ?u64 {
315
325
  assert(table > 0);
316
326
  assert(block > 0);
317
327
 
318
- var extent = manifest.tables.getOrPutAssumeCapacity(table);
328
+ var extent = manifest.tables.getOrPutAssumeCapacity(.{
329
+ .tree_hash = tree_hash,
330
+ .table = table,
331
+ });
319
332
  const previous_block = if (extent.found_existing) extent.value_ptr.block else null;
320
333
 
321
334
  extent.value_ptr.* = .{
@@ -328,13 +341,19 @@ pub const Manifest = struct {
328
341
 
329
342
  /// Removes the table extent if { block, entry } is the latest version, and returns true.
330
343
  /// Otherwise, returns false.
331
- pub fn remove_table_extent(manifest: *Manifest, table: u64, block: u64, entry: u32) bool {
344
+ pub fn remove_table_extent(manifest: *Manifest, tree_hash: u128, table: u64, block: u64, entry: u32) bool {
332
345
  assert(table > 0);
333
346
  assert(block > 0);
334
347
 
335
- const extent = manifest.tables.getPtr(table).?;
348
+ const extent = manifest.tables.getPtr(.{
349
+ .tree_hash = tree_hash,
350
+ .table = table,
351
+ }).?;
336
352
  if (extent.block == block and extent.entry == entry) {
337
- assert(manifest.tables.remove(table));
353
+ assert(manifest.tables.remove(.{
354
+ .tree_hash = tree_hash,
355
+ .table = table,
356
+ }));
338
357
 
339
358
  return true;
340
359
  } else {
@@ -3,7 +3,7 @@ const assert = std.debug.assert;
3
3
  const log = std.log.scoped(.superblock_quorums);
4
4
 
5
5
  const superblock = @import("./superblock.zig");
6
- const SuperBlockSector = superblock.SuperBlockSector;
6
+ const SuperBlockHeader = superblock.SuperBlockHeader;
7
7
  const SuperBlockVersion = superblock.SuperBlockVersion;
8
8
  const fuzz = @import("./superblock_quorums_fuzz.zig");
9
9
 
@@ -16,10 +16,10 @@ pub fn QuorumsType(comptime options: Options) type {
16
16
  const Quorums = @This();
17
17
 
18
18
  const Quorum = struct {
19
- sector: *const SuperBlockSector,
19
+ header: *const SuperBlockHeader,
20
20
  valid: bool = false,
21
21
  /// Track which copies are a member of the quorum.
22
- /// Used to ignore duplicate copies of a sector when determining a quorum.
22
+ /// Used to ignore duplicate copies of a header when determining a quorum.
23
23
  copies: QuorumCount = QuorumCount.initEmpty(),
24
24
  /// An integer value indicates the copy index found in the corresponding slot.
25
25
  /// A `null` value indicates that the copy is invalid or not a member of the working
@@ -86,7 +86,7 @@ pub fn QuorumsType(comptime options: Options) type {
86
86
  /// trailers may be damaged.
87
87
  pub fn working(
88
88
  quorums: *Quorums,
89
- copies: []SuperBlockSector,
89
+ copies: []SuperBlockHeader,
90
90
  threshold: Threshold,
91
91
  ) Error!Quorum {
92
92
  assert(copies.len == options.superblock_copies);
@@ -102,17 +102,17 @@ pub fn QuorumsType(comptime options: Options) type {
102
102
  for (quorums.slice()) |quorum| {
103
103
  if (quorum.copies.count() == options.superblock_copies) {
104
104
  log.debug("quorum: checksum={x} parent={x} sequence={} count={} valid={}", .{
105
- quorum.sector.checksum,
106
- quorum.sector.parent,
107
- quorum.sector.sequence,
105
+ quorum.header.checksum,
106
+ quorum.header.parent,
107
+ quorum.header.sequence,
108
108
  quorum.copies.count(),
109
109
  quorum.valid,
110
110
  });
111
111
  } else {
112
112
  log.warn("quorum: checksum={x} parent={x} sequence={} count={} valid={}", .{
113
- quorum.sector.checksum,
114
- quorum.sector.parent,
115
- quorum.sector.sequence,
113
+ quorum.header.checksum,
114
+ quorum.header.parent,
115
+ quorum.header.sequence,
116
116
  quorum.copies.count(),
117
117
  quorum.valid,
118
118
  });
@@ -128,7 +128,7 @@ pub fn QuorumsType(comptime options: Options) type {
128
128
  // Verify that the remaining quorums are correctly sorted:
129
129
  for (quorums.slice()[1..]) |a| {
130
130
  assert(sort_priority_descending({}, b, a));
131
- assert(a.sector.valid_checksum());
131
+ assert(a.header.valid_checksum());
132
132
  }
133
133
 
134
134
  // Even the best copy with the most quorum still has inadequate quorum.
@@ -140,61 +140,61 @@ pub fn QuorumsType(comptime options: Options) type {
140
140
  // - a lost or misdirected write
141
141
  // - a latent sector error that prevented a write
142
142
  for (quorums.slice()[1..]) |a| {
143
- if (a.sector.cluster != b.sector.cluster) {
143
+ if (a.header.cluster != b.header.cluster) {
144
144
  log.warn("superblock copy={} has cluster={} instead of {}", .{
145
- a.sector.copy,
146
- a.sector.cluster,
147
- b.sector.cluster,
145
+ a.header.copy,
146
+ a.header.cluster,
147
+ b.header.cluster,
148
148
  });
149
149
  continue;
150
150
  }
151
151
 
152
- if (a.sector.replica != b.sector.replica) {
152
+ if (a.header.replica != b.header.replica) {
153
153
  log.warn("superblock copy={} has replica={} instead of {}", .{
154
- a.sector.copy,
155
- a.sector.replica,
156
- b.sector.replica,
154
+ a.header.copy,
155
+ a.header.replica,
156
+ b.header.replica,
157
157
  });
158
158
  continue;
159
159
  }
160
160
 
161
- if (a.sector.sequence == b.sector.sequence) {
161
+ if (a.header.sequence == b.header.sequence) {
162
162
  // Two quorums, same cluster+replica+sequence, but different checksums.
163
163
  // This shouldn't ever happen — but if it does, we can't safely repair.
164
- assert(a.sector.checksum != b.sector.checksum);
164
+ assert(a.header.checksum != b.header.checksum);
165
165
  return error.Fork;
166
166
  }
167
167
 
168
- if (a.sector.sequence > b.sector.sequence + 1) {
168
+ if (a.header.sequence > b.header.sequence + 1) {
169
169
  // We read sequences such as (2,2,2,4) — 2 isn't safe to use, but there isn't a
170
170
  // valid quorum for 4 either.
171
171
  return error.ParentSkipped;
172
172
  }
173
173
 
174
- if (a.sector.sequence + 1 == b.sector.sequence) {
175
- assert(a.sector.checksum != b.sector.checksum);
176
- assert(a.sector.cluster == b.sector.cluster);
177
- assert(a.sector.replica == b.sector.replica);
174
+ if (a.header.sequence + 1 == b.header.sequence) {
175
+ assert(a.header.checksum != b.header.checksum);
176
+ assert(a.header.cluster == b.header.cluster);
177
+ assert(a.header.replica == b.header.replica);
178
178
 
179
- if (a.sector.checksum != b.sector.parent) {
179
+ if (a.header.checksum != b.header.parent) {
180
180
  return error.ParentNotConnected;
181
- } else if (!a.sector.vsr_state.monotonic(b.sector.vsr_state)) {
181
+ } else if (!a.header.vsr_state.monotonic(b.header.vsr_state)) {
182
182
  return error.VSRStateNotMonotonic;
183
183
  } else {
184
- assert(b.sector.valid_checksum());
184
+ assert(b.header.valid_checksum());
185
185
 
186
186
  return b;
187
187
  }
188
188
  }
189
189
  }
190
190
 
191
- assert(b.sector.valid_checksum());
191
+ assert(b.header.valid_checksum());
192
192
  return b;
193
193
  }
194
194
 
195
195
  fn count_copy(
196
196
  quorums: *Quorums,
197
- copy: *const SuperBlockSector,
197
+ copy: *const SuperBlockHeader,
198
198
  slot: usize,
199
199
  threshold: Threshold,
200
200
  ) void {
@@ -240,12 +240,12 @@ pub fn QuorumsType(comptime options: Options) type {
240
240
  }
241
241
 
242
242
  var quorum = quorums.find_or_insert_quorum_for_copy(copy);
243
- assert(quorum.sector.checksum == copy.checksum);
244
- assert(quorum.sector.equal(copy));
243
+ assert(quorum.header.checksum == copy.checksum);
244
+ assert(quorum.header.equal(copy));
245
245
 
246
246
  if (copy.copy >= options.superblock_copies) {
247
- // This sector is a valid member of the quorum, but with an unexpected copy number.
248
- // The "SuperBlockSector.copy" field is not protected by the checksum, so if that byte
247
+ // This header is a valid member of the quorum, but with an unexpected copy number.
248
+ // The "SuperBlockHeader.copy" field is not protected by the checksum, so if that byte
249
249
  // (and only that byte) is corrupted, the superblock is still valid — but we don't know
250
250
  // for certain which copy this was supposed to be.
251
251
  // We make the assumption that this was not a double-fault (corrupt + misdirect) —
@@ -262,13 +262,13 @@ pub fn QuorumsType(comptime options: Options) type {
262
262
  quorum.valid = quorum.copies.count() >= threshold.count();
263
263
  }
264
264
 
265
- fn find_or_insert_quorum_for_copy(quorums: *Quorums, copy: *const SuperBlockSector) *Quorum {
265
+ fn find_or_insert_quorum_for_copy(quorums: *Quorums, copy: *const SuperBlockHeader) *Quorum {
266
266
  assert(copy.valid_checksum());
267
267
 
268
268
  for (quorums.array[0..quorums.count]) |*quorum| {
269
- if (copy.checksum == quorum.sector.checksum) return quorum;
269
+ if (copy.checksum == quorum.header.checksum) return quorum;
270
270
  } else {
271
- quorums.array[quorums.count] = Quorum{ .sector = copy };
271
+ quorums.array[quorums.count] = Quorum{ .header = copy };
272
272
  quorums.count += 1;
273
273
 
274
274
  return &quorums.array[quorums.count - 1];
@@ -280,26 +280,26 @@ pub fn QuorumsType(comptime options: Options) type {
280
280
  }
281
281
 
282
282
  fn sort_priority_descending(_: void, a: Quorum, b: Quorum) bool {
283
- assert(a.sector.checksum != b.sector.checksum);
283
+ assert(a.header.checksum != b.header.checksum);
284
284
 
285
285
  if (a.valid and !b.valid) return true;
286
286
  if (b.valid and !a.valid) return false;
287
287
 
288
- if (a.sector.sequence > b.sector.sequence) return true;
289
- if (b.sector.sequence > a.sector.sequence) return false;
288
+ if (a.header.sequence > b.header.sequence) return true;
289
+ if (b.header.sequence > a.header.sequence) return false;
290
290
 
291
291
  if (a.copies.count() > b.copies.count()) return true;
292
292
  if (b.copies.count() > a.copies.count()) return false;
293
293
 
294
294
  // The sort order must be stable and deterministic:
295
- return a.sector.checksum > b.sector.checksum;
295
+ return a.header.checksum > b.header.checksum;
296
296
  }
297
297
 
298
298
  /// Repair a quorum's copies in the safest known order.
299
299
  /// Repair is complete when every copy is on-disk (not necessarily in its home slot).
300
300
  ///
301
- /// We must be careful when repairing superblock sectors to avoid endangering our quorum if
302
- /// an additional fault occurs. We primarily guard against torn sector writes — preventing a
301
+ /// We must be careful when repairing superblock headers to avoid endangering our quorum if
302
+ /// an additional fault occurs. We primarily guard against torn header writes — preventing a
303
303
  /// misdirected write from derailing repair is far more expensive and complex — but they are
304
304
  /// likewise far less likely to occur.
305
305
  ///
@@ -342,9 +342,9 @@ pub fn QuorumsType(comptime options: Options) type {
342
342
  }
343
343
 
344
344
  // In descending order, our priorities for repair are:
345
- // 1. The slot holds no sector, and the copy was not found anywhere.
346
- // 2. The slot holds no sector, but its copy was found elsewhere.
347
- // 3. The slot holds a misdirected sector, but that copy is in another slot as well.
345
+ // 1. The slot holds no header, and the copy was not found anywhere.
346
+ // 2. The slot holds no header, but its copy was found elsewhere.
347
+ // 3. The slot holds a misdirected header, but that copy is in another slot as well.
348
348
  var a: ?u8 = null;
349
349
  var b: ?u8 = null;
350
350
  var c: ?u8 = null;
@@ -388,7 +388,7 @@ test "Quorum.repairs" {
388
388
  defer std.testing.log_level = level;
389
389
 
390
390
  try fuzz.fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 4 });
391
- // TODO: Enable these once SuperBlockSector is generic over its Constants.
391
+ // TODO: Enable these once SuperBlockHeader is generic over its Constants.
392
392
  // try fuzz.fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 6 });
393
393
  // try fuzz.fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 8 });
394
394
  }