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
@@ -52,6 +52,9 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
52
52
  /// This field is only used for safety checks, it does not affect the behavior.
53
53
  read_pending: bool = false,
54
54
 
55
+ // Used for verifying key order when constants.verify == true.
56
+ key_prev: ?Table.Key,
57
+
55
58
  pub fn init(allocator: mem.Allocator) !TableIterator {
56
59
  const index_block = try allocator.alignedAlloc(
57
60
  u8,
@@ -95,6 +98,7 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
95
98
  },
96
99
  },
97
100
  .value = undefined,
101
+ .key_prev = null,
98
102
  };
99
103
  }
100
104
 
@@ -132,10 +136,21 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
132
136
  .values = .{ .buffer = it.values.buffer },
133
137
  .data_blocks = .{ .buffer = it.data_blocks.buffer },
134
138
  .value = 0,
139
+ .key_prev = null,
135
140
  };
136
141
 
137
142
  assert(it.values.empty());
138
143
  assert(it.data_blocks.empty());
144
+
145
+ if (constants.verify) {
146
+ Table.verify(
147
+ Storage,
148
+ context.grid.superblock.storage,
149
+ context.address,
150
+ null,
151
+ null,
152
+ );
153
+ }
139
154
  }
140
155
 
141
156
  /// Try to buffer at least a full block of values to be peek()'d.
@@ -269,7 +284,7 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
269
284
  /// Returns either:
270
285
  /// - the next Key, if available.
271
286
  /// - error.Empty when there are no values remaining to iterate.
272
- /// - error.Drained when the iterator isn't empty, but some values
287
+ /// - error.Drained when the iterator isn't empty, but some values
273
288
  /// still need to be buffered into memory via tick().
274
289
  pub fn peek(it: TableIterator) error{ Empty, Drained }!Table.Key {
275
290
  assert(!it.read_pending);
@@ -280,7 +295,7 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
280
295
  const block = it.data_blocks.head() orelse {
281
296
  // NOTE: Even if there are no values to peek, some may be unbuffered.
282
297
  // We call buffered_all_values() to distinguish between the iterator
283
- // being empty and needing to tic() to refill values.
298
+ // being empty and needing to tick() to refill values.
284
299
  if (!it.buffered_all_values()) return error.Drained;
285
300
  return error.Empty;
286
301
  };
@@ -291,6 +306,18 @@ pub fn TableIteratorType(comptime Table: type, comptime Storage: type) type {
291
306
 
292
307
  /// This may only be called after peek() returns a Key (and not Empty or Drained)
293
308
  pub fn pop(it: *TableIterator) Table.Value {
309
+ const value = it.pop_internal();
310
+
311
+ if (constants.verify) {
312
+ const key = Table.key_from_value(&value);
313
+ if (it.key_prev) |k| assert(Table.compare_keys(k, key) == .lt);
314
+ it.key_prev = key;
315
+ }
316
+
317
+ return value;
318
+ }
319
+
320
+ fn pop_internal(it: *TableIterator) Table.Value {
294
321
  assert(!it.read_pending);
295
322
  assert(!it.read_table_index);
296
323
 
@@ -14,6 +14,8 @@ pub fn TableMutableType(comptime Table: type) type {
14
14
  const compare_keys = Table.compare_keys;
15
15
  const key_from_value = Table.key_from_value;
16
16
  const tombstone_from_key = Table.tombstone_from_key;
17
+ const tombstone = Table.tombstone;
18
+ const usage = Table.usage;
17
19
 
18
20
  return struct {
19
21
  const TableMutable = @This();
@@ -96,26 +98,52 @@ pub fn TableMutableType(comptime Table: type) type {
96
98
  }
97
99
 
98
100
  pub fn put(table: *TableMutable, value: *const Value) void {
99
- // If the key is already present in the hash map, the old key will not be overwritten
100
- // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
101
- // level getOrPut() API and manually overwrite the old key.
102
- const upsert = table.values.getOrPutAssumeCapacity(value.*);
103
- upsert.key_ptr.* = value.*;
101
+ switch (usage) {
102
+ .secondary_index => {
103
+ const existing = table.values.fetchRemove(value.*);
104
+ if (existing) |kv| {
105
+ // If there was a previous operation on this key then it must have been a remove.
106
+ // The put and remove cancel out.
107
+ assert(tombstone(&kv.key));
108
+ } else {
109
+ table.values.putAssumeCapacityNoClobber(value.*, {});
110
+ }
111
+ },
112
+ .general => {
113
+ // If the key is already present in the hash map, the old key will not be overwritten
114
+ // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
115
+ // level getOrPut() API and manually overwrite the old key.
116
+ const upsert = table.values.getOrPutAssumeCapacity(value.*);
117
+ upsert.key_ptr.* = value.*;
118
+ },
119
+ }
104
120
 
105
121
  // The hash map's load factor may allow for more capacity because of rounding:
106
122
  assert(table.values.count() <= table.value_count_max);
107
-
108
- if (table.values_cache) |cache| {
109
- cache.insert(key_from_value(value)).* = value.*;
110
- }
111
123
  }
112
124
 
113
125
  pub fn remove(table: *TableMutable, value: *const Value) void {
114
- // If the key is already present in the hash map, the old key will not be overwritten
115
- // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
116
- // level getOrPut() API and manually overwrite the old key.
117
- const upsert = table.values.getOrPutAssumeCapacity(value.*);
118
- upsert.key_ptr.* = tombstone_from_key(key_from_value(value));
126
+ switch (usage) {
127
+ .secondary_index => {
128
+ const existing = table.values.fetchRemove(value.*);
129
+ if (existing) |kv| {
130
+ // The previous operation on this key then it must have been a put.
131
+ // The put and remove cancel out.
132
+ assert(!tombstone(&kv.key));
133
+ } else {
134
+ // If the put is already on-disk, then we need to follow it with a tombstone.
135
+ // The put and the tombstone may cancel each other out later during compaction.
136
+ table.values.putAssumeCapacityNoClobber(tombstone_from_key(key_from_value(value)), {});
137
+ }
138
+ },
139
+ .general => {
140
+ // If the key is already present in the hash map, the old key will not be overwritten
141
+ // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
142
+ // level getOrPut() API and manually overwrite the old key.
143
+ const upsert = table.values.getOrPutAssumeCapacity(value.*);
144
+ upsert.key_ptr.* = tombstone_from_key(key_from_value(value));
145
+ },
146
+ }
119
147
 
120
148
  assert(table.values.count() <= table.value_count_max);
121
149
  }
@@ -154,7 +182,13 @@ pub fn TableMutableType(comptime Table: type) type {
154
182
  while (it.next()) |value| : (i += 1) {
155
183
  values_max[i] = value.*;
156
184
 
157
- if (table.values_cache) |cache| cache.insert(key_from_value(value)).* = value.*;
185
+ if (table.values_cache) |cache| {
186
+ if (tombstone(value)) {
187
+ cache.remove(key_from_value(value));
188
+ } else {
189
+ cache.insert(value);
190
+ }
191
+ }
158
192
  }
159
193
 
160
194
  const values = values_max[0..i];
@@ -27,7 +27,7 @@ const SuperBlock = vsr.SuperBlockType(Storage);
27
27
  const Environment = struct {
28
28
  const cluster = 32;
29
29
  const replica = 4;
30
- const size_max = vsr.Zone.superblock.size().? +
30
+ const storage_size_max = vsr.Zone.superblock.size().? +
31
31
  vsr.Zone.wal_headers.size().? +
32
32
  vsr.Zone.wal_prepares.size().? +
33
33
  (512 + 64) * 1024 * 1024;
@@ -35,8 +35,7 @@ const Environment = struct {
35
35
  const node_count = 1024;
36
36
  const cache_entries_max = 2 * 1024 * 1024;
37
37
  const forest_options = StateMachine.forest_options(.{
38
- // Ignored by StateMachine.forest_options().
39
- .lsm_forest_node_count = undefined,
38
+ .lsm_forest_node_count = node_count,
40
39
  .cache_entries_accounts = cache_entries_max,
41
40
  .cache_entries_transfers = cache_entries_max,
42
41
  .cache_entries_posted = cache_entries_max,
@@ -73,7 +72,7 @@ const Environment = struct {
73
72
  env.dir_fd = try IO.open_dir(dir_path);
74
73
  errdefer std.os.close(env.dir_fd);
75
74
 
76
- env.fd = try IO.open_file(env.dir_fd, "test_forest", size_max, must_create);
75
+ env.fd = try IO.open_file(env.dir_fd, "test_forest", storage_size_max, must_create);
77
76
  errdefer std.os.close(env.fd);
78
77
 
79
78
  env.io = try IO.init(128, 0);
@@ -85,7 +84,11 @@ const Environment = struct {
85
84
  env.message_pool = try MessagePool.init(allocator, .replica);
86
85
  errdefer env.message_pool.deinit(allocator);
87
86
 
88
- env.superblock = try SuperBlock.init(allocator, &env.storage, &env.message_pool);
87
+ env.superblock = try SuperBlock.init(allocator, .{
88
+ .storage = &env.storage,
89
+ .storage_size_limit = constants.storage_size_max,
90
+ .message_pool = &env.message_pool,
91
+ });
89
92
  env.superblock_context = undefined;
90
93
  errdefer env.superblock.deinit(allocator);
91
94
 
@@ -117,7 +120,7 @@ const Environment = struct {
117
120
  }
118
121
 
119
122
  fn tick(env: *Environment) !void {
120
- env.grid.tick();
123
+ // env.grid.tick();
121
124
  try env.io.tick();
122
125
  }
123
126
 
@@ -132,7 +135,7 @@ const Environment = struct {
132
135
  env.superblock.format(superblock_format_callback, &env.superblock_context, .{
133
136
  .cluster = cluster,
134
137
  .replica = replica,
135
- .size_max = size_max,
138
+ .storage_size_max = storage_size_max,
136
139
  });
137
140
 
138
141
  while (true) {
@@ -212,13 +212,26 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
212
212
  var manifest = try Manifest.init(allocator, node_pool, grid, tree_hash);
213
213
  errdefer manifest.deinit(allocator);
214
214
 
215
- var compaction_table_immutable = try CompactionTableImmutable.init(allocator, tree_name);
215
+ var compaction_table_immutable = try CompactionTableImmutable.init(
216
+ allocator,
217
+ std.fmt.comptimePrint("{s}(immutable->0)", .{tree_name}),
218
+ );
216
219
  errdefer compaction_table_immutable.deinit(allocator);
217
220
 
218
221
  var compaction_table: [@divFloor(constants.lsm_levels, 2)]CompactionTable = undefined;
219
- for (compaction_table) |*compaction, i| {
220
- errdefer for (compaction_table[0..i]) |*c| c.deinit(allocator);
221
- compaction.* = try CompactionTable.init(allocator, tree_name);
222
+ {
223
+ comptime var i: usize = 0;
224
+ inline while (i < compaction_table.len) : (i += 1) {
225
+ errdefer for (compaction_table[0..i]) |*c| c.deinit(allocator);
226
+ const compaction_name = std.fmt.comptimePrint("{s}({}->{}/{}->{})", .{
227
+ tree_name,
228
+ 2 * i,
229
+ 2 * i + 1,
230
+ 2 * i + 1,
231
+ 2 * i + 2,
232
+ });
233
+ compaction_table[i] = try CompactionTable.init(allocator, compaction_name);
234
+ }
222
235
  }
223
236
  errdefer for (compaction_table) |*c| c.deinit(allocator);
224
237
 
@@ -580,10 +593,14 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
580
593
  assert(tree.compaction_io_pending == 0);
581
594
  assert(tree.compaction_callback == null);
582
595
 
596
+ if (constants.verify) {
597
+ tree.manifest.verify(tree.compaction_op);
598
+ }
599
+
583
600
  tracer.start(
584
601
  &tree.tracer_slot,
585
602
  .{ .tree = .{ .tree_name = tree_name } },
586
- .{ .tree_compaction_beat = .{ .tree_name = tree_name } },
603
+ .tree_compaction_beat,
587
604
  @src(),
588
605
  );
589
606
 
@@ -959,9 +976,13 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
959
976
  tracer.end(
960
977
  &tree.tracer_slot,
961
978
  .{ .tree = .{ .tree_name = tree_name } },
962
- .{ .tree_compaction_beat = .{ .tree_name = tree_name } },
979
+ .tree_compaction_beat,
963
980
  );
964
981
 
982
+ if (constants.verify) {
983
+ tree.manifest.verify(tree.compaction_op);
984
+ }
985
+
965
986
  // Invoke the compact() callback after the manifest compacts at the end of the beat.
966
987
  const callback = tree.compaction_callback.?;
967
988
  tree.compaction_callback = null;