tigerbeetle-node 0.11.5 → 0.11.7

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 (77) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/dist/index.d.ts +41 -42
  3. package/dist/index.js +41 -42
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/src/index.ts +0 -1
  7. package/src/tigerbeetle/scripts/benchmark.bat +7 -3
  8. package/src/tigerbeetle/scripts/benchmark.sh +2 -3
  9. package/src/tigerbeetle/scripts/install.bat +7 -0
  10. package/src/tigerbeetle/scripts/install.sh +2 -3
  11. package/src/tigerbeetle/src/benchmark.zig +3 -3
  12. package/src/tigerbeetle/src/config.zig +24 -3
  13. package/src/tigerbeetle/src/constants.zig +8 -5
  14. package/src/tigerbeetle/src/ewah.zig +6 -5
  15. package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
  16. package/src/tigerbeetle/src/io/darwin.zig +19 -0
  17. package/src/tigerbeetle/src/io/linux.zig +8 -0
  18. package/src/tigerbeetle/src/io/windows.zig +20 -2
  19. package/src/tigerbeetle/src/iops.zig +7 -1
  20. package/src/tigerbeetle/src/lsm/compaction.zig +27 -72
  21. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +10 -11
  22. package/src/tigerbeetle/src/lsm/grid.zig +267 -267
  23. package/src/tigerbeetle/src/lsm/groove.zig +3 -0
  24. package/src/tigerbeetle/src/lsm/level_iterator.zig +18 -1
  25. package/src/tigerbeetle/src/lsm/manifest.zig +29 -1
  26. package/src/tigerbeetle/src/lsm/manifest_level.zig +1 -0
  27. package/src/tigerbeetle/src/lsm/manifest_log.zig +5 -5
  28. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +19 -11
  29. package/src/tigerbeetle/src/lsm/merge_iterator.zig +106 -0
  30. package/src/tigerbeetle/src/lsm/posted_groove.zig +1 -0
  31. package/src/tigerbeetle/src/lsm/segmented_array.zig +1 -0
  32. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +26 -70
  33. package/src/tigerbeetle/src/lsm/table.zig +56 -0
  34. package/src/tigerbeetle/src/lsm/table_iterator.zig +29 -2
  35. package/src/tigerbeetle/src/lsm/table_mutable.zig +49 -15
  36. package/src/tigerbeetle/src/lsm/test.zig +10 -7
  37. package/src/tigerbeetle/src/lsm/tree.zig +27 -6
  38. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +302 -263
  39. package/src/tigerbeetle/src/message_pool.zig +2 -1
  40. package/src/tigerbeetle/src/simulator.zig +22 -84
  41. package/src/tigerbeetle/src/{test/accounting → state_machine}/auditor.zig +8 -8
  42. package/src/tigerbeetle/src/{test/accounting → state_machine}/workload.zig +108 -48
  43. package/src/tigerbeetle/src/state_machine.zig +20 -14
  44. package/src/tigerbeetle/src/storage.zig +58 -6
  45. package/src/tigerbeetle/src/test/cluster.zig +14 -11
  46. package/src/tigerbeetle/src/test/conductor.zig +2 -3
  47. package/src/tigerbeetle/src/test/id.zig +10 -0
  48. package/src/tigerbeetle/src/test/state_checker.zig +1 -1
  49. package/src/tigerbeetle/src/test/state_machine.zig +151 -46
  50. package/src/tigerbeetle/src/test/storage.zig +22 -1
  51. package/src/tigerbeetle/src/tigerbeetle.zig +0 -1
  52. package/src/tigerbeetle/src/tracer.zig +50 -28
  53. package/src/tigerbeetle/src/unit_tests.zig +11 -6
  54. package/src/tigerbeetle/src/vopr.zig +4 -4
  55. package/src/tigerbeetle/src/vsr/client.zig +5 -5
  56. package/src/tigerbeetle/src/vsr/clock.zig +2 -2
  57. package/src/tigerbeetle/src/vsr/journal.zig +647 -537
  58. package/src/tigerbeetle/src/vsr/replica.zig +333 -333
  59. package/src/tigerbeetle/src/vsr/replica_format.zig +7 -4
  60. package/src/tigerbeetle/src/vsr/superblock.zig +87 -39
  61. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +114 -93
  62. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  63. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +11 -8
  64. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +3 -3
  65. package/src/tigerbeetle/src/vsr.zig +60 -13
  66. package/src/tigerbeetle/src/c/tb_client/context.zig +0 -304
  67. package/src/tigerbeetle/src/c/tb_client/echo_client.zig +0 -108
  68. package/src/tigerbeetle/src/c/tb_client/packet.zig +0 -80
  69. package/src/tigerbeetle/src/c/tb_client/signal.zig +0 -286
  70. package/src/tigerbeetle/src/c/tb_client/thread.zig +0 -88
  71. package/src/tigerbeetle/src/c/tb_client.h +0 -221
  72. package/src/tigerbeetle/src/c/tb_client.zig +0 -177
  73. package/src/tigerbeetle/src/c/tb_client_header.zig +0 -218
  74. package/src/tigerbeetle/src/c/tb_client_header_test.zig +0 -135
  75. package/src/tigerbeetle/src/c/test.zig +0 -371
  76. package/src/tigerbeetle/src/cli.zig +0 -375
  77. package/src/tigerbeetle/src/main.zig +0 -245
@@ -62,7 +62,7 @@ pub const IO = struct {
62
62
  };
63
63
 
64
64
  fn flush(self: *IO, mode: FlushMode) !void {
65
- if (self.completed.peek() == null) {
65
+ if (self.completed.empty()) {
66
66
  // Compute how long to poll by flushing timeout completions.
67
67
  // NOTE: this may push to completed queue
68
68
  var timeout_ms: ?os.windows.DWORD = null;
@@ -78,7 +78,7 @@ pub const IO = struct {
78
78
  }
79
79
 
80
80
  // Poll for IO iff theres IO pending and flush_timeouts() found no ready completions
81
- if (self.io_pending > 0 and self.completed.peek() == null) {
81
+ if (self.io_pending > 0 and self.completed.empty()) {
82
82
  // In blocking mode, we're always waiting at least until the timeout by run_for_ns.
83
83
  // In non-blocking mode, we shouldn't wait at all.
84
84
  const io_timeout = switch (mode) {
@@ -868,6 +868,24 @@ pub const IO = struct {
868
868
  completion: *Completion,
869
869
  nanoseconds: u63,
870
870
  ) void {
871
+ // Special case a zero timeout as a yield.
872
+ if (nanoseconds == 0) {
873
+ completion.* = .{
874
+ .next = null,
875
+ .context = @ptrCast(?*anyopaque, context),
876
+ .operation = undefined,
877
+ .callback = struct {
878
+ fn on_complete(ctx: Completion.Context) void {
879
+ const _context = @intToPtr(Context, @ptrToInt(ctx.completion.context));
880
+ callback(_context, ctx.completion, {});
881
+ }
882
+ }.on_complete,
883
+ };
884
+
885
+ self.completed.push(completion);
886
+ return;
887
+ }
888
+
871
889
  self.submit(
872
890
  context,
873
891
  callback,
@@ -19,11 +19,17 @@ pub fn IOPS(comptime T: type, comptime size: u6) type {
19
19
 
20
20
  pub fn release(self: *Self, item: *T) void {
21
21
  item.* = undefined;
22
- const i = (@ptrToInt(item) - @ptrToInt(&self.items)) / @sizeOf(T);
22
+ const i = self.index(item);
23
23
  assert(!self.free.isSet(i));
24
24
  self.free.set(i);
25
25
  }
26
26
 
27
+ pub fn index(self: *Self, item: *T) usize {
28
+ const i = (@ptrToInt(item) - @ptrToInt(&self.items)) / @sizeOf(T);
29
+ assert(i < size);
30
+ return i;
31
+ }
32
+
27
33
  /// Returns the count of IOPs available.
28
34
  pub fn available(self: *const Self) usize {
29
35
  return self.free.count();
@@ -42,7 +42,7 @@ const constants = @import("../constants.zig");
42
42
 
43
43
  const GridType = @import("grid.zig").GridType;
44
44
  const ManifestType = @import("manifest.zig").ManifestType;
45
- const KWayMergeIterator = @import("k_way_merge.zig").KWayMergeIterator;
45
+ const MergeIteratorType = @import("merge_iterator.zig").MergeIteratorType;
46
46
  const TableIteratorType = @import("table_iterator.zig").TableIteratorType;
47
47
  const LevelIteratorType = @import("level_iterator.zig").LevelIteratorType;
48
48
 
@@ -51,8 +51,6 @@ pub fn CompactionType(
51
51
  comptime Storage: type,
52
52
  comptime IteratorAType: anytype,
53
53
  ) type {
54
- const Key = Table.Key;
55
- const Value = Table.Value;
56
54
  const tombstone = Table.tombstone;
57
55
 
58
56
  return struct {
@@ -63,7 +61,7 @@ pub fn CompactionType(
63
61
  const BlockPtrConst = Grid.BlockPtrConst;
64
62
  const BlockWrite = struct {
65
63
  write: Grid.Write = undefined,
66
- block: BlockPtr = undefined,
64
+ block: *BlockPtr = undefined,
67
65
  writable: bool = false,
68
66
  };
69
67
 
@@ -73,47 +71,12 @@ pub fn CompactionType(
73
71
  const IteratorA = IteratorAType(Table, Storage);
74
72
  const IteratorB = LevelIteratorType(Table, Storage);
75
73
 
76
- const k = 2;
77
- const MergeIterator = KWayMergeIterator(
78
- Compaction,
79
- Table.Key,
80
- Table.Value,
81
- Table.key_from_value,
82
- Table.compare_keys,
83
- k,
84
- MergeStreamSelector.peek,
85
- MergeStreamSelector.pop,
86
- MergeStreamSelector.precedence,
74
+ const MergeIterator = MergeIteratorType(
75
+ Table,
76
+ IteratorA,
77
+ IteratorB,
87
78
  );
88
79
 
89
- const MergeStreamSelector = struct {
90
- fn peek(compaction: *const Compaction, stream_id: u32) error{ Empty, Drained }!Key {
91
- return switch (stream_id) {
92
- 0 => compaction.iterator_a.peek(),
93
- 1 => compaction.iterator_b.peek(),
94
- else => unreachable,
95
- };
96
- }
97
-
98
- fn pop(compaction: *Compaction, stream_id: u32) Value {
99
- return switch (stream_id) {
100
- 0 => compaction.iterator_a.pop(),
101
- 1 => compaction.iterator_b.pop(),
102
- else => unreachable,
103
- };
104
- }
105
-
106
- /// Returns true if stream A has higher precedence than stream B.
107
- /// This is used to deduplicate values across streams.
108
- fn precedence(compaction: *const Compaction, stream_a: u32, stream_b: u32) bool {
109
- _ = compaction;
110
- assert(stream_a + stream_b == 1);
111
-
112
- // All tables in iterator_a (stream=0) have a higher precedence.
113
- return stream_a == 0;
114
- }
115
- };
116
-
117
80
  pub const Callback = fn (it: *Compaction) void;
118
81
 
119
82
  const Status = enum {
@@ -122,7 +85,8 @@ pub fn CompactionType(
122
85
  done,
123
86
  };
124
87
 
125
- tree_name: [:0]const u8,
88
+ /// Used only for debugging/tracing.
89
+ name: [:0]const u8,
126
90
 
127
91
  grid: *Grid,
128
92
  grid_reservation: Grid.Reservation,
@@ -161,7 +125,7 @@ pub fn CompactionType(
161
125
 
162
126
  tracer_slot: ?tracer.SpanStart = null,
163
127
 
164
- pub fn init(allocator: mem.Allocator, tree_name: [:0]const u8) !Compaction {
128
+ pub fn init(allocator: mem.Allocator, name: [:0]const u8) !Compaction {
165
129
  var iterator_a = try IteratorA.init(allocator);
166
130
  errdefer iterator_a.deinit(allocator);
167
131
 
@@ -172,7 +136,7 @@ pub fn CompactionType(
172
136
  errdefer table_builder.deinit(allocator);
173
137
 
174
138
  return Compaction{
175
- .tree_name = tree_name,
139
+ .name = name,
176
140
 
177
141
  // Assigned by start()
178
142
  .grid = undefined,
@@ -239,7 +203,7 @@ pub fn CompactionType(
239
203
  assert(drop_tombstones or level_b < constants.lsm_levels - 1);
240
204
 
241
205
  compaction.* = .{
242
- .tree_name = compaction.tree_name,
206
+ .name = compaction.name,
243
207
 
244
208
  .grid = grid,
245
209
  // Reserve enough blocks to write our output tables in the worst case, where:
@@ -350,11 +314,8 @@ pub fn CompactionType(
350
314
 
351
315
  tracer.start(
352
316
  &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
- } },
317
+ .{ .tree_compaction = .{ .compaction_name = compaction.name } },
318
+ .{ .tree_compaction_tick = .{ .level_b = compaction.level_b } },
358
319
  @src(),
359
320
  );
360
321
 
@@ -397,7 +358,7 @@ pub fn CompactionType(
397
358
  write_callback,
398
359
  &block_write.write,
399
360
  block_write.block,
400
- Table.block_address(block_write.block),
361
+ Table.block_address(block_write.block.*),
401
362
  );
402
363
  }
403
364
  }
@@ -429,18 +390,18 @@ pub fn CompactionType(
429
390
  var tracer_slot: ?tracer.SpanStart = null;
430
391
  tracer.start(
431
392
  &tracer_slot,
432
- .{ .tree = .{ .tree_name = compaction.tree_name } },
433
- .{ .tree_compaction_merge = .{
434
- .tree_name = compaction.tree_name,
435
- .level_b = compaction.level_b,
436
- } },
393
+ .{ .tree_compaction = .{ .compaction_name = compaction.name } },
394
+ .{ .tree_compaction_merge = .{ .level_b = compaction.level_b } },
437
395
  @src(),
438
396
  );
439
397
 
440
398
  // Create the merge iterator only when we can peek() from the read iterators.
441
399
  // This happens after IO for the first reads complete.
442
400
  if (compaction.merge_iterator == null) {
443
- compaction.merge_iterator = MergeIterator.init(compaction, k, .ascending);
401
+ compaction.merge_iterator = MergeIterator.init(
402
+ &compaction.iterator_a,
403
+ &compaction.iterator_b,
404
+ );
444
405
  assert(!compaction.merge_iterator.?.empty());
445
406
  }
446
407
 
@@ -456,19 +417,13 @@ pub fn CompactionType(
456
417
 
457
418
  tracer.end(
458
419
  &tracer_slot,
459
- .{ .tree = .{ .tree_name = compaction.tree_name } },
460
- .{ .tree_compaction_merge = .{
461
- .tree_name = compaction.tree_name,
462
- .level_b = compaction.level_b,
463
- } },
420
+ .{ .tree_compaction = .{ .compaction_name = compaction.name } },
421
+ .{ .tree_compaction_merge = .{ .level_b = compaction.level_b } },
464
422
  );
465
423
  tracer.end(
466
424
  &compaction.tracer_slot,
467
- .{ .tree = .{ .tree_name = compaction.tree_name } },
468
- .{ .tree_compaction_tick = .{
469
- .tree_name = compaction.tree_name,
470
- .level_b = compaction.level_b,
471
- } },
425
+ .{ .tree_compaction = .{ .compaction_name = compaction.name } },
426
+ .{ .tree_compaction_tick = .{ .level_b = compaction.level_b } },
472
427
  );
473
428
 
474
429
  // TODO Implement pacing here by deciding if we should do another compact_tick()
@@ -514,7 +469,7 @@ pub fn CompactionType(
514
469
  });
515
470
 
516
471
  // Mark the finished data block as writable for the next compact_tick() call.
517
- compaction.data.block = compaction.table_builder.data_block;
472
+ compaction.data.block = &compaction.table_builder.data_block;
518
473
  assert(!compaction.data.writable);
519
474
  compaction.data.writable = true;
520
475
  }
@@ -531,7 +486,7 @@ pub fn CompactionType(
531
486
  });
532
487
 
533
488
  // Mark the finished filter block as writable for the next compact_tick() call.
534
- compaction.filter.block = compaction.table_builder.filter_block;
489
+ compaction.filter.block = &compaction.table_builder.filter_block;
535
490
  assert(!compaction.filter.writable);
536
491
  compaction.filter.writable = true;
537
492
  }
@@ -551,7 +506,7 @@ pub fn CompactionType(
551
506
  compaction.manifest.insert_table(compaction.level_b, &table);
552
507
 
553
508
  // Mark the finished index block as writable for the next compact_tick() call.
554
- compaction.index.block = compaction.table_builder.index_block;
509
+ compaction.index.block = &compaction.table_builder.index_block;
555
510
  assert(!compaction.index.writable);
556
511
  compaction.index.writable = true;
557
512
 
@@ -41,18 +41,12 @@ const FuzzOpTag = std.meta.Tag(FuzzOp);
41
41
  const Environment = struct {
42
42
  const cluster = 32;
43
43
  const replica = 4;
44
- // TODO Is this appropriate for the number of fuzz_ops we want to run?
45
- const size_max = vsr.Zone.superblock.size().? +
46
- vsr.Zone.wal_headers.size().? +
47
- vsr.Zone.wal_prepares.size().? +
48
- 1024 * 1024 * 1024;
49
44
 
50
45
  const node_count = 1024;
51
46
  // This is the smallest size that set_associative_cache will allow us.
52
47
  const cache_entries_max = 2048;
53
48
  const forest_options = StateMachine.forest_options(.{
54
- // Ignored by StateMachine.forest_options().
55
- .lsm_forest_node_count = undefined,
49
+ .lsm_forest_node_count = node_count,
56
50
  .cache_entries_accounts = cache_entries_max,
57
51
  .cache_entries_transfers = cache_entries_max,
58
52
  .cache_entries_posted = cache_entries_max,
@@ -98,7 +92,11 @@ const Environment = struct {
98
92
  env.message_pool = try MessagePool.init(allocator, .replica);
99
93
  errdefer env.message_pool.deinit(allocator);
100
94
 
101
- env.superblock = try SuperBlock.init(allocator, env.storage, &env.message_pool);
95
+ env.superblock = try SuperBlock.init(allocator, .{
96
+ .storage = env.storage,
97
+ .storage_size_limit = constants.storage_size_max,
98
+ .message_pool = &env.message_pool,
99
+ });
102
100
  errdefer env.superblock.deinit(allocator);
103
101
 
104
102
  env.grid = try Grid.init(allocator, &env.superblock);
@@ -126,7 +124,7 @@ const Environment = struct {
126
124
  }
127
125
 
128
126
  fn tick(env: *Environment) void {
129
- env.grid.tick();
127
+ // env.grid.tick();
130
128
  env.storage.tick();
131
129
  }
132
130
 
@@ -152,7 +150,6 @@ const Environment = struct {
152
150
  env.superblock.format(superblock_format_callback, &env.superblock_context, .{
153
151
  .cluster = cluster,
154
152
  .replica = replica,
155
- .size_max = size_max,
156
153
  });
157
154
  env.tick_until_state_change(.init, .formatted);
158
155
  }
@@ -265,6 +262,8 @@ const Environment = struct {
265
262
  if (compact.checkpoint) env.checkpoint(compact.op);
266
263
  },
267
264
  .put_account => |account| {
265
+ // The forest requires prefetch before put.
266
+ env.prefetch_account(account.id);
268
267
  env.forest.grooves.accounts.put(&account);
269
268
  try model.put(account.id, account);
270
269
  },
@@ -292,7 +291,7 @@ const Environment = struct {
292
291
 
293
292
  pub fn run_fuzz_ops(storage_options: Storage.Options, fuzz_ops: []const FuzzOp) !void {
294
293
  // Init mocked storage.
295
- var storage = try Storage.init(allocator, Environment.size_max, storage_options);
294
+ var storage = try Storage.init(allocator, constants.storage_size_max, storage_options);
296
295
  defer storage.deinit(allocator);
297
296
 
298
297
  try Environment.format(&storage);