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.
- package/dist/.client.node.sha256 +1 -1
- package/dist/index.d.ts +41 -42
- package/dist/index.js +41 -42
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +0 -1
- package/src/tigerbeetle/scripts/benchmark.bat +7 -3
- package/src/tigerbeetle/scripts/benchmark.sh +2 -3
- package/src/tigerbeetle/scripts/install.bat +7 -0
- package/src/tigerbeetle/scripts/install.sh +2 -3
- package/src/tigerbeetle/src/benchmark.zig +3 -3
- package/src/tigerbeetle/src/config.zig +24 -3
- package/src/tigerbeetle/src/constants.zig +8 -5
- package/src/tigerbeetle/src/ewah.zig +6 -5
- package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
- package/src/tigerbeetle/src/io/darwin.zig +19 -0
- package/src/tigerbeetle/src/io/linux.zig +8 -0
- package/src/tigerbeetle/src/io/windows.zig +20 -2
- package/src/tigerbeetle/src/iops.zig +7 -1
- package/src/tigerbeetle/src/lsm/compaction.zig +27 -72
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +10 -11
- package/src/tigerbeetle/src/lsm/grid.zig +267 -267
- package/src/tigerbeetle/src/lsm/groove.zig +3 -0
- package/src/tigerbeetle/src/lsm/level_iterator.zig +18 -1
- package/src/tigerbeetle/src/lsm/manifest.zig +29 -1
- package/src/tigerbeetle/src/lsm/manifest_level.zig +1 -0
- package/src/tigerbeetle/src/lsm/manifest_log.zig +5 -5
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +19 -11
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +106 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +1 -0
- package/src/tigerbeetle/src/lsm/segmented_array.zig +1 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +26 -70
- package/src/tigerbeetle/src/lsm/table.zig +56 -0
- package/src/tigerbeetle/src/lsm/table_iterator.zig +29 -2
- package/src/tigerbeetle/src/lsm/table_mutable.zig +49 -15
- package/src/tigerbeetle/src/lsm/test.zig +10 -7
- package/src/tigerbeetle/src/lsm/tree.zig +27 -6
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +302 -263
- package/src/tigerbeetle/src/message_pool.zig +2 -1
- package/src/tigerbeetle/src/simulator.zig +22 -84
- package/src/tigerbeetle/src/{test/accounting → state_machine}/auditor.zig +8 -8
- package/src/tigerbeetle/src/{test/accounting → state_machine}/workload.zig +108 -48
- package/src/tigerbeetle/src/state_machine.zig +20 -14
- package/src/tigerbeetle/src/storage.zig +58 -6
- package/src/tigerbeetle/src/test/cluster.zig +14 -11
- package/src/tigerbeetle/src/test/conductor.zig +2 -3
- package/src/tigerbeetle/src/test/id.zig +10 -0
- package/src/tigerbeetle/src/test/state_checker.zig +1 -1
- package/src/tigerbeetle/src/test/state_machine.zig +151 -46
- package/src/tigerbeetle/src/test/storage.zig +22 -1
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -1
- package/src/tigerbeetle/src/tracer.zig +50 -28
- package/src/tigerbeetle/src/unit_tests.zig +11 -6
- package/src/tigerbeetle/src/vopr.zig +4 -4
- package/src/tigerbeetle/src/vsr/client.zig +5 -5
- package/src/tigerbeetle/src/vsr/clock.zig +2 -2
- package/src/tigerbeetle/src/vsr/journal.zig +647 -537
- package/src/tigerbeetle/src/vsr/replica.zig +333 -333
- package/src/tigerbeetle/src/vsr/replica_format.zig +7 -4
- package/src/tigerbeetle/src/vsr/superblock.zig +87 -39
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +114 -93
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +11 -8
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +3 -3
- package/src/tigerbeetle/src/vsr.zig +60 -13
- package/src/tigerbeetle/src/c/tb_client/context.zig +0 -304
- package/src/tigerbeetle/src/c/tb_client/echo_client.zig +0 -108
- package/src/tigerbeetle/src/c/tb_client/packet.zig +0 -80
- package/src/tigerbeetle/src/c/tb_client/signal.zig +0 -286
- package/src/tigerbeetle/src/c/tb_client/thread.zig +0 -88
- package/src/tigerbeetle/src/c/tb_client.h +0 -221
- package/src/tigerbeetle/src/c/tb_client.zig +0 -177
- package/src/tigerbeetle/src/c/tb_client_header.zig +0 -218
- package/src/tigerbeetle/src/c/tb_client_header_test.zig +0 -135
- package/src/tigerbeetle/src/c/test.zig +0 -371
- package/src/tigerbeetle/src/cli.zig +0 -375
- 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
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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|
|
|
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
|
|
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
|
-
|
|
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",
|
|
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,
|
|
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
|
-
.
|
|
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(
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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;
|