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
@@ -28,8 +28,6 @@ pub fn format(
28
28
  .{
29
29
  .cluster = cluster,
30
30
  .replica = replica,
31
- // TODO Convert this to a runtime arg, to cap storage.
32
- .size_max = constants.size_max,
33
31
  },
34
32
  );
35
33
 
@@ -151,6 +149,7 @@ fn ReplicaFormatType(comptime Storage: type) type {
151
149
 
152
150
  test "format" {
153
151
  const superblock_zone_size = @import("./superblock.zig").superblock_zone_size;
152
+ const data_file_size_min = @import("./superblock.zig").data_file_size_min;
154
153
  const MessagePool = @import("../message_pool.zig").MessagePool;
155
154
  const Storage = @import("../test/storage.zig").Storage;
156
155
  const SuperBlock = vsr.SuperBlockType(Storage);
@@ -173,7 +172,11 @@ test "format" {
173
172
  var message_pool = try MessagePool.init(allocator, .replica);
174
173
  defer message_pool.deinit(allocator);
175
174
 
176
- var superblock = try SuperBlock.init(allocator, &storage, &message_pool);
175
+ var superblock = try SuperBlock.init(allocator, .{
176
+ .storage = &storage,
177
+ .storage_size_limit = data_file_size_min,
178
+ .message_pool = &message_pool,
179
+ });
177
180
  defer superblock.deinit(allocator);
178
181
 
179
182
  try format(Storage, allocator, cluster, replica, &storage, &superblock);
@@ -186,7 +189,7 @@ test "format" {
186
189
  try std.testing.expectEqual(sector.copy, copy);
187
190
  try std.testing.expectEqual(sector.replica, replica);
188
191
  try std.testing.expectEqual(sector.cluster, cluster);
189
- try std.testing.expectEqual(sector.size, storage.size);
192
+ try std.testing.expectEqual(sector.storage_size, storage.size);
190
193
  try std.testing.expectEqual(sector.sequence, 1);
191
194
  try std.testing.expectEqual(sector.vsr_state.commit_min, 0);
192
195
  try std.testing.expectEqual(sector.vsr_state.commit_max, 0);
@@ -52,11 +52,12 @@ pub const SuperBlockSector = extern struct {
52
52
  cluster: u32,
53
53
 
54
54
  /// The current size of the data file.
55
- size: u64,
55
+ storage_size: u64,
56
56
 
57
- /// The maximum size of the data file.
58
- // TODO Actually limit the file to this size.
59
- size_max: u64,
57
+ /// The maximum possible size of the data file.
58
+ /// The maximum allowed runtime storage_size_limit.
59
+ /// The FreeSet's on-disk size is a function of storage_size_max.
60
+ storage_size_max: u64,
60
61
 
61
62
  /// A monotonically increasing counter to locate the latest superblock at startup.
62
63
  sequence: u64,
@@ -245,8 +246,8 @@ pub const SuperBlockSector = extern struct {
245
246
  if (a.version != b.version) return false;
246
247
  if (a.replica != b.replica) return false;
247
248
  if (a.cluster != b.cluster) return false;
248
- if (a.size != b.size) return false;
249
- if (a.size_max != b.size_max) return false;
249
+ if (a.storage_size != b.storage_size) return false;
250
+ if (a.storage_size_max != b.storage_size_max) return false;
250
251
  if (a.sequence != b.sequence) return false;
251
252
  if (a.parent != b.parent) return false;
252
253
  if (a.manifest_checksum != b.manifest_checksum) return false;
@@ -321,25 +322,50 @@ pub const superblock_trailer_manifest_size_max = blk: {
321
322
  };
322
323
 
323
324
  pub const superblock_trailer_free_set_size_max = blk: {
324
- const encode_size_max = SuperBlockFreeSet.encode_size_max(constants.block_count_max);
325
+ const encode_size_max = SuperBlockFreeSet.encode_size_max(block_count_max);
325
326
  assert(encode_size_max > 0);
326
327
 
327
- // Round up to the nearest sector:
328
- break :blk div_ceil(encode_size_max, constants.sector_size) * constants.sector_size;
328
+ break :blk vsr.sector_ceil(encode_size_max);
329
329
  };
330
330
 
331
331
  pub const superblock_trailer_client_table_size_max = blk: {
332
332
  const encode_size_max = SuperBlockClientTable.encode_size_max;
333
333
  assert(encode_size_max > 0);
334
334
 
335
- // Round up to the nearest sector:
336
- break :blk div_ceil(encode_size_max, constants.sector_size) * constants.sector_size;
335
+ break :blk vsr.sector_ceil(encode_size_max);
337
336
  };
338
337
 
339
338
  pub const data_file_size_min = blk: {
340
339
  break :blk superblock_zone_size + constants.journal_size_max;
341
340
  };
342
341
 
342
+ /// The maximum number of blocks in the grid.
343
+ const block_count_max = blk: {
344
+ var size = constants.storage_size_max;
345
+ size -= constants.superblock_copies * @sizeOf(SuperBlockSector);
346
+ size -= constants.superblock_copies * superblock_trailer_client_table_size_max;
347
+ size -= constants.superblock_copies * superblock_trailer_manifest_size_max;
348
+ size -= constants.journal_size_max;
349
+ // At this point, the remainder of size is split between the grid and the freeset copies.
350
+ // The size of a freeset is related to the number of blocks it must store.
351
+ // Maximize the number of grid blocks.
352
+
353
+ var shard_count = @divFloor(size, constants.block_size * SuperBlockFreeSet.shard_bits);
354
+ while (true) : (shard_count -= 1) {
355
+ const block_count = shard_count * SuperBlockFreeSet.shard_bits;
356
+ const grid_size = block_count * constants.block_size;
357
+ const free_set_size = vsr.sector_ceil(SuperBlockFreeSet.encode_size_max(block_count));
358
+ const free_sets_size = constants.superblock_copies * free_set_size;
359
+ if (free_sets_size + grid_size <= size) break;
360
+ }
361
+ break :blk shard_count * SuperBlockFreeSet.shard_bits;
362
+ };
363
+
364
+ comptime {
365
+ assert(block_count_max > 0);
366
+ assert(block_count_max * constants.block_size + data_file_size_min <= constants.storage_size_max);
367
+ }
368
+
343
369
  /// This table shows the sequence number progression of the SuperBlock's sectors.
344
370
  ///
345
371
  /// action working staging disk
@@ -441,6 +467,8 @@ pub fn SuperBlockType(comptime Storage: type) type {
441
467
 
442
468
  /// Whether the superblock has been opened. An open superblock may not be formatted.
443
469
  opened: bool = false,
470
+ block_count_limit: usize,
471
+ storage_size_limit: u64,
444
472
 
445
473
  /// Beyond formatting and opening of the superblock, which are mutually exclusive of all
446
474
  /// other operations, only the following queue combinations are allowed:
@@ -452,11 +480,24 @@ pub fn SuperBlockType(comptime Storage: type) type {
452
480
  queue_head: ?*Context = null,
453
481
  queue_tail: ?*Context = null,
454
482
 
455
- pub fn init(
456
- allocator: mem.Allocator,
483
+ pub const Options = struct {
457
484
  storage: *Storage,
458
485
  message_pool: *MessagePool,
459
- ) !SuperBlock {
486
+ storage_size_limit: u64,
487
+ };
488
+
489
+ pub fn init(allocator: mem.Allocator, options: Options) !SuperBlock {
490
+ assert(options.storage_size_limit >= data_file_size_min);
491
+ assert(options.storage_size_limit <= constants.storage_size_max);
492
+ assert(options.storage_size_limit % constants.sector_size == 0);
493
+
494
+ const shard_count_limit = @intCast(usize, @divFloor(
495
+ options.storage_size_limit - data_file_size_min,
496
+ constants.block_size * FreeSet.shard_bits,
497
+ ));
498
+ const block_count_limit = shard_count_limit * FreeSet.shard_bits;
499
+ assert(block_count_limit <= block_count_max);
500
+
460
501
  const a = try allocator.allocAdvanced(SuperBlockSector, constants.sector_size, 1, .exact);
461
502
  errdefer allocator.free(a);
462
503
 
@@ -481,10 +522,10 @@ pub fn SuperBlockType(comptime Storage: type) type {
481
522
  );
482
523
  errdefer manifest.deinit(allocator);
483
524
 
484
- var free_set = try FreeSet.init(allocator, constants.block_count_max);
525
+ var free_set = try FreeSet.init(allocator, block_count_limit);
485
526
  errdefer free_set.deinit(allocator);
486
527
 
487
- var client_table = try ClientTable.init(allocator, message_pool);
528
+ var client_table = try ClientTable.init(allocator, options.message_pool);
488
529
  errdefer client_table.deinit(allocator);
489
530
 
490
531
  const manifest_buffer = try allocator.allocAdvanced(
@@ -498,7 +539,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
498
539
  const free_set_buffer = try allocator.allocAdvanced(
499
540
  u8,
500
541
  constants.sector_size,
501
- superblock_trailer_free_set_size_max,
542
+ SuperBlockFreeSet.encode_size_max(block_count_limit),
502
543
  .exact,
503
544
  );
504
545
  errdefer allocator.free(free_set_buffer);
@@ -512,7 +553,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
512
553
  errdefer allocator.free(client_table_buffer);
513
554
 
514
555
  return SuperBlock{
515
- .storage = storage,
556
+ .storage = options.storage,
516
557
  .working = &a[0],
517
558
  .staging = &b[0],
518
559
  .reading = &reading[0],
@@ -522,6 +563,8 @@ pub fn SuperBlockType(comptime Storage: type) type {
522
563
  .manifest_buffer = manifest_buffer,
523
564
  .free_set_buffer = free_set_buffer,
524
565
  .client_table_buffer = client_table_buffer,
566
+ .block_count_limit = block_count_limit,
567
+ .storage_size_limit = options.storage_size_limit,
525
568
  };
526
569
  }
527
570
 
@@ -542,9 +585,6 @@ pub fn SuperBlockType(comptime Storage: type) type {
542
585
  pub const FormatOptions = struct {
543
586
  cluster: u32,
544
587
  replica: u8,
545
-
546
- /// The maximum size of the entire data file.
547
- size_max: u64,
548
588
  };
549
589
 
550
590
  pub fn format(
@@ -556,8 +596,6 @@ pub fn SuperBlockType(comptime Storage: type) type {
556
596
  assert(!superblock.opened);
557
597
 
558
598
  assert(options.replica < constants.replicas_max);
559
- assert(options.size_max >= data_file_size_min);
560
- assert(options.size_max % constants.sector_size == 0);
561
599
 
562
600
  // This working copy provides the parent checksum, and will not be written to disk.
563
601
  // We therefore use zero values to make this parent checksum as stable as possible.
@@ -567,8 +605,8 @@ pub fn SuperBlockType(comptime Storage: type) type {
567
605
  .sequence = 0,
568
606
  .replica = options.replica,
569
607
  .cluster = options.cluster,
570
- .size = 0,
571
- .size_max = options.size_max,
608
+ .storage_size = 0,
609
+ .storage_size_max = constants.storage_size_max,
572
610
  .parent = 0,
573
611
  .manifest_checksum = 0,
574
612
  .free_set_checksum = 0,
@@ -757,7 +795,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
757
795
 
758
796
  fn write_staging_encode_free_set(superblock: *SuperBlock) void {
759
797
  const staging: *SuperBlockSector = superblock.staging;
760
- const encode_size_max = FreeSet.encode_size_max(constants.block_count_max);
798
+ const encode_size_max = FreeSet.encode_size_max(superblock.block_count_limit);
761
799
  const target = superblock.free_set_buffer[0..encode_size_max];
762
800
 
763
801
  superblock.free_set.include_staging();
@@ -765,15 +803,23 @@ pub fn SuperBlockType(comptime Storage: type) type {
765
803
 
766
804
  superblock.verify_manifest_blocks_are_acquired_in_free_set();
767
805
 
768
- staging.size = data_file_size_min;
806
+ staging.storage_size = data_file_size_min;
769
807
 
770
808
  if (superblock.free_set.highest_address_acquired()) |address| {
771
- staging.size += address * constants.block_size;
809
+ staging.storage_size += address * constants.block_size;
810
+ }
811
+ assert(staging.storage_size >= data_file_size_min);
812
+ assert(staging.storage_size <= staging.storage_size_max);
813
+ assert(staging.storage_size <= superblock.storage_size_limit);
814
+
815
+ if (superblock.free_set.count_acquired() == 0) {
816
+ // EWAH encodes a zero-length bitset to an empty slice anyway, but handle this
817
+ // condition separately so that during formatting it doesn't depend on the choice
818
+ // of storage_size_limit.
819
+ staging.free_set_size = 0;
820
+ } else {
821
+ staging.free_set_size = @intCast(u32, superblock.free_set.encode(target));
772
822
  }
773
- assert(staging.size >= data_file_size_min);
774
- assert(staging.size <= staging.size_max);
775
-
776
- staging.free_set_size = @intCast(u32, superblock.free_set.encode(target));
777
823
  staging.free_set_checksum = vsr.checksum(target[0..staging.free_set_size]);
778
824
  }
779
825
 
@@ -930,8 +976,8 @@ pub fn SuperBlockType(comptime Storage: type) type {
930
976
  assert(superblock.staging.cluster == superblock.working.cluster);
931
977
  assert(superblock.staging.replica == superblock.working.replica);
932
978
 
933
- assert(superblock.staging.size >= data_file_size_min);
934
- assert(superblock.staging.size <= superblock.staging.size_max);
979
+ assert(superblock.staging.storage_size >= data_file_size_min);
980
+ assert(superblock.staging.storage_size <= superblock.staging.storage_size_max);
935
981
 
936
982
  assert(context.copy.? < constants.superblock_copies);
937
983
  superblock.staging.copy = context.copy.?;
@@ -1059,6 +1105,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1059
1105
  if (superblock.quorums.working(superblock.reading, threshold)) |quorum| {
1060
1106
  assert(quorum.valid);
1061
1107
  assert(quorum.copies.count() >= threshold.count());
1108
+ assert(quorum.sector.storage_size_max == constants.storage_size_max);
1062
1109
 
1063
1110
  const working = quorum.sector;
1064
1111
  if (threshold == .verify) {
@@ -1070,9 +1117,9 @@ pub fn SuperBlockType(comptime Storage: type) type {
1070
1117
 
1071
1118
  if (context.caller == .format) {
1072
1119
  assert(working.sequence == 1);
1073
- assert(working.size == data_file_size_min);
1120
+ assert(working.storage_size == data_file_size_min);
1074
1121
  assert(working.manifest_size == 0);
1075
- assert(working.free_set_size == 8);
1122
+ assert(working.free_set_size == 0);
1076
1123
  assert(working.client_table_size == 4);
1077
1124
  assert(working.vsr_state.commit_min_checksum ==
1078
1125
  vsr.Header.root_prepare(working.cluster).checksum);
@@ -1097,7 +1144,7 @@ pub fn SuperBlockType(comptime Storage: type) type {
1097
1144
  superblock.working.sequence,
1098
1145
  superblock.working.cluster,
1099
1146
  superblock.working.replica,
1100
- superblock.working.size,
1147
+ superblock.working.storage_size,
1101
1148
  superblock.working.vsr_state.commit_min_checksum,
1102
1149
  superblock.working.vsr_state.commit_min,
1103
1150
  superblock.working.vsr_state.commit_max,
@@ -1246,9 +1293,10 @@ pub fn SuperBlockType(comptime Storage: type) type {
1246
1293
  if (vsr.checksum(slice) == superblock.working.free_set_checksum) {
1247
1294
  superblock.free_set.decode(slice);
1248
1295
 
1249
- log.debug("open: read_free_set: acquired blocks: {}/{}", .{
1296
+ log.debug("open: read_free_set: acquired blocks: {}/{}/{}", .{
1250
1297
  superblock.free_set.count_acquired(),
1251
- constants.block_count_max,
1298
+ superblock.block_count_limit,
1299
+ block_count_max,
1252
1300
  });
1253
1301
 
1254
1302
  superblock.verify_manifest_blocks_are_acquired_in_free_set();