tigerbeetle-node 0.11.12 → 0.12.0

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 (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,877 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const math = std.math;
4
- const mem = std.mem;
5
- const meta = std.meta;
6
-
7
- const constants = @import("../constants.zig");
8
- const lsm = @import("tree.zig");
9
- const binary_search = @import("binary_search.zig");
10
-
11
- const Direction = @import("direction.zig").Direction;
12
- const SegmentedArray = @import("segmented_array.zig").SegmentedArray;
13
- const SortedSegmentedArray = @import("segmented_array.zig").SortedSegmentedArray;
14
-
15
- pub fn ManifestLevelType(
16
- comptime NodePool: type,
17
- comptime Key: type,
18
- comptime TableInfo: type,
19
- comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
20
- comptime table_count_max: u32,
21
- ) type {
22
- return struct {
23
- const Self = @This();
24
-
25
- pub const Keys = SortedSegmentedArray(
26
- Key,
27
- NodePool,
28
- table_count_max,
29
- Key,
30
- struct {
31
- inline fn key_from_value(value: *const Key) Key {
32
- return value.*;
33
- }
34
- }.key_from_value,
35
- compare_keys,
36
- .{},
37
- );
38
-
39
- pub const Tables = SegmentedArray(TableInfo, NodePool, table_count_max, .{});
40
-
41
- // These two segmented arrays are parallel. That is, the absolute indexes of maximum key
42
- // and corresponding TableInfo are the same. However, the number of nodes, node index, and
43
- // relative index into the node differ as the elements per node are different.
44
- //
45
- // Ordered by ascending (maximum) key. Keys may repeat due to snapshots.
46
- keys: Keys,
47
- tables: Tables,
48
-
49
- /// The number of tables visible to snapshot_latest.
50
- /// Used to enforce table_count_max_for_level().
51
- table_count_visible: u32 = 0,
52
-
53
- pub fn init(allocator: mem.Allocator) !Self {
54
- var keys = try Keys.init(allocator);
55
- errdefer keys.deinit(allocator, null);
56
-
57
- var tables = try Tables.init(allocator);
58
- errdefer tables.deinit(allocator, null);
59
-
60
- return Self{
61
- .keys = keys,
62
- .tables = tables,
63
- };
64
- }
65
-
66
- pub fn deinit(level: *Self, allocator: mem.Allocator, node_pool: *NodePool) void {
67
- level.keys.deinit(allocator, node_pool);
68
- level.tables.deinit(allocator, node_pool);
69
- }
70
-
71
- /// Inserts an ordered batch of tables into the level, then rebuilds the indexes.
72
- pub fn insert_table(level: *Self, node_pool: *NodePool, table: *const TableInfo) void {
73
- assert(level.keys.len() == level.tables.len());
74
-
75
- const absolute_index = level.keys.insert_element(node_pool, table.key_max);
76
- assert(absolute_index < level.keys.len());
77
- level.tables.insert_elements(node_pool, absolute_index, &[_]TableInfo{table.*});
78
-
79
- if (table.visible(lsm.snapshot_latest)) level.table_count_visible += 1;
80
-
81
- assert(level.keys.len() == level.tables.len());
82
- }
83
-
84
- /// Set snapshot_max for the given table in the ManifestLevel.
85
- ///
86
- /// * The table is mutable so that this function can update its snapshot.
87
- /// * Asserts that the table currently has snapshot_max of math.maxInt(u64).
88
- /// * Asserts that the table exists in the manifest.
89
- pub fn set_snapshot_max(level: *Self, snapshot: u64, table: *TableInfo) void {
90
- assert(snapshot < lsm.snapshot_latest);
91
- assert(table.snapshot_max == math.maxInt(u64));
92
-
93
- const key_min = table.key_min;
94
- const key_max = table.key_max;
95
- assert(compare_keys(key_min, key_max) != .gt);
96
-
97
- var it = level.iterator(
98
- .visible,
99
- @as(*const [1]u64, &lsm.snapshot_latest),
100
- .ascending,
101
- KeyRange{ .key_min = key_min, .key_max = key_max },
102
- );
103
-
104
- const level_table_const = it.next().?;
105
- // This const cast is safe as we know that the memory pointed to is in fact
106
- // mutable. That is, the table is not in the .text or .rodata section. We do this
107
- // to avoid duplicating the iterator code in order to expose only a const iterator
108
- // in the public API.
109
- const level_table = @intToPtr(*TableInfo, @ptrToInt(level_table_const));
110
- assert(level_table.equal(table));
111
- assert(level_table.snapshot_max == math.maxInt(u64));
112
-
113
- level_table.snapshot_max = snapshot;
114
- table.snapshot_max = snapshot;
115
-
116
- assert(it.next() == null);
117
- level.table_count_visible -= 1;
118
- }
119
-
120
- /// Remove the given table from the ManifestLevel, asserting that it is not visible
121
- /// by any snapshot in `snapshots` or by `lsm.snapshot_latest`.
122
- pub fn remove_table(
123
- level: *Self,
124
- node_pool: *NodePool,
125
- snapshots: []const u64,
126
- table: *const TableInfo,
127
- ) void {
128
- assert(level.keys.len() == level.tables.len());
129
- // The batch may contain a single table, with a single key, i.e. key_min == key_max:
130
- assert(compare_keys(table.key_min, table.key_max) != .gt);
131
-
132
- // Use `key_min` for both ends of the iterator; we are looking for a single table.
133
- const cursor_start = level.iterator_start(table.key_min, table.key_min, .ascending).?;
134
- var absolute_index = level.keys.absolute_index_for_cursor(cursor_start);
135
-
136
- var it = level.tables.iterator_from_index(absolute_index, .ascending);
137
- while (it.next()) |level_table| : (absolute_index += 1) {
138
- if (level_table.invisible(snapshots)) {
139
- assert(level_table.equal(table));
140
-
141
- level.keys.remove_elements(node_pool, absolute_index, 1);
142
- level.tables.remove_elements(node_pool, absolute_index, 1);
143
- break;
144
- }
145
- } else {
146
- unreachable;
147
- }
148
-
149
- assert(level.keys.len() == level.tables.len());
150
- }
151
-
152
- pub const Visibility = enum {
153
- visible,
154
- invisible,
155
- };
156
-
157
- pub const KeyRange = struct {
158
- key_min: Key, // Inclusive.
159
- key_max: Key, // Inclusive.
160
- };
161
-
162
- pub fn iterator(
163
- level: *const Self,
164
- visibility: Visibility,
165
- snapshots: []const u64,
166
- direction: Direction,
167
- key_range: ?KeyRange,
168
- ) Iterator {
169
- for (snapshots) |snapshot| {
170
- assert(snapshot <= lsm.snapshot_latest);
171
- }
172
-
173
- const inner = blk: {
174
- if (key_range) |range| {
175
- assert(compare_keys(range.key_min, range.key_max) != .gt);
176
-
177
- if (level.iterator_start(range.key_min, range.key_max, direction)) |start| {
178
- break :blk level.tables.iterator_from_index(
179
- level.keys.absolute_index_for_cursor(start),
180
- direction,
181
- );
182
- } else {
183
- break :blk Tables.Iterator{
184
- .array = &level.tables,
185
- .direction = direction,
186
- .cursor = .{ .node = 0, .relative_index = 0 },
187
- .done = true,
188
- };
189
- }
190
- } else {
191
- switch (direction) {
192
- .ascending => break :blk level.tables.iterator_from_index(0, direction),
193
- .descending => {
194
- break :blk level.tables.iterator_from_cursor(level.tables.last(), .descending);
195
- },
196
- }
197
- }
198
- };
199
-
200
- return .{
201
- .level = level,
202
- .inner = inner,
203
- .visibility = visibility,
204
- .snapshots = snapshots,
205
- .direction = direction,
206
- .key_range = key_range,
207
- };
208
- }
209
-
210
- pub const Iterator = struct {
211
- level: *const Self,
212
- inner: Tables.Iterator,
213
- visibility: Visibility,
214
- snapshots: []const u64,
215
- direction: Direction,
216
- key_range: ?KeyRange,
217
-
218
- pub fn next(it: *Iterator) ?*const TableInfo {
219
- while (it.inner.next()) |table| {
220
- // We can't assert !it.inner.done as inner.next() may set done before returning.
221
-
222
- // Skip tables that don't match the provided visibility interests.
223
- switch (it.visibility) {
224
- .invisible => blk: {
225
- if (table.invisible(it.snapshots)) break :blk;
226
- continue;
227
- },
228
- .visible => blk: {
229
- for (it.snapshots) |snapshot| {
230
- if (table.visible(snapshot)) break :blk;
231
- }
232
- continue;
233
- },
234
- }
235
-
236
- // Filter the table using the key range if provided.
237
- if (it.key_range) |key_range| {
238
- switch (it.direction) {
239
- .ascending => {
240
- // Assert that the table is not out of bounds to the left.
241
- //
242
- // We can assert this as it is exactly the same key comparison when
243
- // we binary search in iterator_start(), and since we move in
244
- // ascending order this remains true beyond the first iteration.
245
- assert(compare_keys(table.key_max, key_range.key_min) != .lt);
246
-
247
- // Check if the table is out of bounds to the right.
248
- if (compare_keys(table.key_min, key_range.key_max) == .gt) {
249
- it.inner.done = true;
250
- return null;
251
- }
252
- },
253
- .descending => {
254
- // Check if the table is out of bounds to the right.
255
- //
256
- // Unlike in the ascending case, it is not guaranteed that
257
- // table.key_min is less than or equal to key_range.key_max on the
258
- // first iteration as only the key_max of a table is stored in our
259
- // key nodes. On subsequent iterations this check will always
260
- // be false.
261
- if (compare_keys(table.key_min, key_range.key_max) == .gt) {
262
- continue;
263
- }
264
-
265
- // Check if the table is out of bounds to the left.
266
- if (compare_keys(table.key_max, key_range.key_min) == .lt) {
267
- it.inner.done = true;
268
- return null;
269
- }
270
- },
271
- }
272
- }
273
-
274
- return table;
275
- }
276
-
277
- assert(it.inner.done);
278
- return null;
279
- }
280
- };
281
-
282
- /// Returns the keys segmented array cursor at which iteration should be started.
283
- /// May return null if there is nothing to iterate because we know for sure that the key
284
- /// range is disjoint with the tables stored in this level.
285
- ///
286
- /// However, the cursor returned is not guaranteed to be in range for the query as only
287
- /// the key_max is stored in the index structures, not the key_min, and only the start
288
- /// bound for the given direction is checked here.
289
- fn iterator_start(
290
- level: Self,
291
- key_min: Key,
292
- key_max: Key,
293
- direction: Direction,
294
- ) ?Keys.Cursor {
295
- assert(compare_keys(key_min, key_max) != .gt);
296
- assert(level.keys.len() == level.tables.len());
297
-
298
- if (level.keys.len() == 0) return null;
299
-
300
- // Ascending: Find the first table where table.key_max ≤ iterator.key_min.
301
- // Descending: Find the first table where table.key_max ≤ iterator.key_max.
302
- const target = level.keys.search(switch (direction) {
303
- .ascending => key_min,
304
- .descending => key_max,
305
- });
306
- assert(target.node <= level.keys.node_count);
307
-
308
- if (level.keys.absolute_index_for_cursor(target) == level.keys.len()) {
309
- return switch (direction) {
310
- // The key_min of the target range is greater than the key_max of the last
311
- // table in the level and we are ascending, so this range matches no tables
312
- // on this level.
313
- .ascending => null,
314
- // The key_max of the target range is greater than the key_max of the last
315
- // table in the level and we are descending, so we need to start iteration
316
- // at the last table in the level.
317
- .descending => level.keys.last(),
318
- };
319
- } else {
320
- // Multiple tables in the level may share a key.
321
- // Scan to the edge so that the iterator will cover them all.
322
- return level.iterator_start_boundary(target, direction);
323
- }
324
- }
325
-
326
- /// This function exists because there may be tables in the level with the same
327
- /// key_max but non-overlapping snapshot visibility.
328
- ///
329
- /// Put differently, there may be several tables with different snapshots but the same
330
- /// `key_max`, and `iterator_start`'s binary search (`key_cursor`) may have landed in the
331
- /// middle of them.
332
- fn iterator_start_boundary(
333
- level: Self,
334
- key_cursor: Keys.Cursor,
335
- direction: Direction,
336
- ) Keys.Cursor {
337
- var reverse = level.keys.iterator_from_cursor(key_cursor, direction.reverse());
338
- assert(meta.eql(reverse.cursor, key_cursor));
339
-
340
- // This cursor will always point to a key equal to start_key.
341
- var adjusted = reverse.cursor;
342
- const start_key = reverse.next().?.*;
343
- assert(compare_keys(start_key, level.keys.element_at_cursor(adjusted)) == .eq);
344
-
345
- var adjusted_next = reverse.cursor;
346
- while (reverse.next()) |k| {
347
- if (compare_keys(start_key, k.*) != .eq) break;
348
- adjusted = adjusted_next;
349
- adjusted_next = reverse.cursor;
350
- } else {
351
- switch (direction) {
352
- .ascending => assert(meta.eql(adjusted, level.keys.first())),
353
- .descending => assert(meta.eql(adjusted, level.keys.last())),
354
- }
355
- }
356
- assert(compare_keys(start_key, level.keys.element_at_cursor(adjusted)) == .eq);
357
-
358
- return adjusted;
359
- }
360
-
361
- /// The function is only used for verification; it is not performance-critical.
362
- pub fn contains(level: Self, table: *const TableInfo) bool {
363
- assert(constants.verify);
364
-
365
- var level_tables = level.iterator(.visible, &.{
366
- table.snapshot_min,
367
- }, .ascending, KeyRange{
368
- .key_min = table.key_min,
369
- .key_max = table.key_max,
370
- });
371
- while (level_tables.next()) |level_table| {
372
- if (level_table.equal(table)) return true;
373
- }
374
- return false;
375
- }
376
- };
377
- }
378
-
379
- pub fn TestContext(
380
- comptime node_size: u32,
381
- comptime Key: type,
382
- comptime table_count_max: u32,
383
- ) type {
384
- return struct {
385
- const Self = @This();
386
-
387
- const testing = std.testing;
388
-
389
- const log = false;
390
-
391
- const Value = struct {
392
- key: Key,
393
- tombstone: bool,
394
- };
395
-
396
- inline fn compare_keys(a: Key, b: Key) math.Order {
397
- return math.order(a, b);
398
- }
399
-
400
- inline fn key_from_value(value: *const Value) Key {
401
- return value.key;
402
- }
403
-
404
- inline fn tombstone_from_key(key: Key) Value {
405
- return .{ .key = key, .tombstone = true };
406
- }
407
-
408
- inline fn tombstone(value: *const Value) bool {
409
- return value.tombstone;
410
- }
411
-
412
- const Table = @import("table.zig").TableType(
413
- Key,
414
- Value,
415
- compare_keys,
416
- key_from_value,
417
- std.math.maxInt(Key),
418
- tombstone,
419
- tombstone_from_key,
420
- .general,
421
- );
422
-
423
- const TableInfo = @import("manifest.zig").TableInfoType(Table);
424
- const NodePool = @import("node_pool.zig").NodePool;
425
-
426
- const TestPool = NodePool(node_size, @alignOf(TableInfo));
427
- const TestLevel = ManifestLevelType(TestPool, Key, TableInfo, compare_keys, table_count_max);
428
- const KeyRange = TestLevel.KeyRange;
429
-
430
- random: std.rand.Random,
431
-
432
- pool: TestPool,
433
- level: TestLevel,
434
-
435
- snapshot_max: u64 = 1,
436
- snapshots: std.BoundedArray(u64, 8) = .{ .buffer = undefined },
437
- snapshot_tables: std.BoundedArray(std.ArrayList(TableInfo), 8) = .{ .buffer = undefined },
438
-
439
- /// Contains only tables with snapshot_max == lsm.snapshot_latest
440
- reference: std.ArrayList(TableInfo),
441
-
442
- inserts: u64 = 0,
443
- removes: u64 = 0,
444
-
445
- fn init(random: std.rand.Random) !Self {
446
- var pool = try TestPool.init(
447
- testing.allocator,
448
- TestLevel.Keys.node_count_max + TestLevel.Tables.node_count_max,
449
- );
450
- errdefer pool.deinit(testing.allocator);
451
-
452
- var level = try TestLevel.init(testing.allocator);
453
- errdefer level.deinit(testing.allocator, &pool);
454
-
455
- var reference = std.ArrayList(TableInfo).init(testing.allocator);
456
- errdefer reference.deinit();
457
-
458
- return Self{
459
- .random = random,
460
- .pool = pool,
461
- .level = level,
462
- .reference = reference,
463
- };
464
- }
465
-
466
- fn deinit(context: *Self) void {
467
- context.level.deinit(testing.allocator, &context.pool);
468
- context.pool.deinit(testing.allocator);
469
-
470
- for (context.snapshot_tables.slice()) |tables| tables.deinit();
471
-
472
- context.reference.deinit();
473
- }
474
-
475
- fn run(context: *Self) !void {
476
- if (log) std.debug.print("\n", .{});
477
-
478
- {
479
- var i: usize = 0;
480
- while (i < table_count_max * 2) : (i += 1) {
481
- switch (context.random.uintLessThanBiased(u32, 100)) {
482
- 0...59 => try context.insert_tables(),
483
- 60...69 => try context.create_snapshot(),
484
- 70...94 => try context.delete_tables(),
485
- 95...99 => try context.drop_snapshot(),
486
- else => unreachable,
487
- }
488
- }
489
- }
490
-
491
- {
492
- var i: usize = 0;
493
- while (i < table_count_max * 2) : (i += 1) {
494
- switch (context.random.uintLessThanBiased(u32, 100)) {
495
- 0...34 => try context.insert_tables(),
496
- 35...39 => try context.create_snapshot(),
497
- 40...89 => try context.delete_tables(),
498
- 90...99 => try context.drop_snapshot(),
499
- else => unreachable,
500
- }
501
- }
502
- }
503
-
504
- try context.remove_all();
505
- }
506
-
507
- fn insert_tables(context: *Self) !void {
508
- const count_free = table_count_max - context.level.keys.len();
509
-
510
- if (count_free == 0) return;
511
-
512
- var buffer: [13]TableInfo = undefined;
513
-
514
- const count_max = @minimum(count_free, 13);
515
- const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
516
-
517
- {
518
- var key: Key = context.random.uintAtMostBiased(Key, table_count_max * 64);
519
-
520
- for (buffer[0..count]) |*table| {
521
- table.* = context.random_greater_non_overlapping_table(key);
522
- key = table.key_max;
523
- }
524
- }
525
-
526
- for (buffer[0..count]) |*table| {
527
- context.level.insert_table(&context.pool, table);
528
- }
529
-
530
- for (buffer[0..count]) |table| {
531
- const index = binary_search.binary_search_values_raw(
532
- Key,
533
- TableInfo,
534
- key_min_from_table,
535
- compare_keys,
536
- context.reference.items,
537
- table.key_max,
538
- .{},
539
- );
540
- // Can't be equal as the tables may not overlap
541
- if (index < context.reference.items.len) {
542
- assert(context.reference.items[index].key_min > table.key_max);
543
- }
544
- context.reference.insert(index, table) catch unreachable;
545
- }
546
-
547
- context.inserts += count;
548
-
549
- try context.verify();
550
- }
551
-
552
- fn random_greater_non_overlapping_table(context: *Self, key: Key) TableInfo {
553
- var new_key_min = key + context.random.uintLessThanBiased(Key, 31) + 1;
554
-
555
- assert(compare_keys(new_key_min, key) == .gt);
556
-
557
- var i = binary_search.binary_search_values_raw(
558
- Key,
559
- TableInfo,
560
- key_min_from_table,
561
- compare_keys,
562
- context.reference.items,
563
- new_key_min,
564
- .{},
565
- );
566
-
567
- if (i > 0) {
568
- if (compare_keys(new_key_min, context.reference.items[i - 1].key_max) != .gt) {
569
- new_key_min = context.reference.items[i - 1].key_max + 1;
570
- }
571
- }
572
-
573
- const next_key_min = for (context.reference.items[i..]) |table| {
574
- switch (compare_keys(new_key_min, table.key_min)) {
575
- .lt => break table.key_min,
576
- .eq => new_key_min = table.key_max + 1,
577
- .gt => unreachable,
578
- }
579
- } else math.maxInt(Key);
580
-
581
- const max_delta = @minimum(32, next_key_min - 1 - new_key_min);
582
- const new_key_max = new_key_min + context.random.uintAtMostBiased(Key, max_delta);
583
-
584
- return .{
585
- .checksum = context.random.int(u128),
586
- .address = context.random.int(u64),
587
- .snapshot_min = context.take_snapshot(),
588
- .key_min = new_key_min,
589
- .key_max = new_key_max,
590
- };
591
- }
592
-
593
- /// See Manifest.take_snapshot()
594
- fn take_snapshot(context: *Self) u64 {
595
- // A snapshot cannot be 0 as this is a reserved value in the superblock.
596
- assert(context.snapshot_max > 0);
597
- // The constant snapshot_latest must compare greater than any issued snapshot.
598
- // This also ensures that we are not about to overflow the u64 counter.
599
- assert(context.snapshot_max < lsm.snapshot_latest - 1);
600
-
601
- context.snapshot_max += 1;
602
-
603
- return context.snapshot_max;
604
- }
605
-
606
- fn create_snapshot(context: *Self) !void {
607
- if (context.snapshots.len == context.snapshots.capacity()) return;
608
-
609
- context.snapshots.appendAssumeCapacity(context.take_snapshot());
610
-
611
- const tables = context.snapshot_tables.addOneAssumeCapacity();
612
- tables.* = std.ArrayList(TableInfo).init(testing.allocator);
613
- try tables.insertSlice(0, context.reference.items);
614
- }
615
-
616
- fn drop_snapshot(context: *Self) !void {
617
- if (context.snapshots.len == 0) return;
618
-
619
- const index = context.random.uintLessThanBiased(usize, context.snapshots.len);
620
-
621
- _ = context.snapshots.swapRemove(index);
622
- var tables = context.snapshot_tables.swapRemove(index);
623
- defer tables.deinit();
624
-
625
- // Use this memory as a scratch buffer since it's conveniently already allocated.
626
- tables.clearRetainingCapacity();
627
-
628
- const snapshots = context.snapshots.slice();
629
-
630
- // Ensure that iteration with a null key range in both directions is tested.
631
- if (context.random.boolean()) {
632
- var it = context.level.iterator(.invisible, snapshots, .ascending, null);
633
- while (it.next()) |table| try tables.append(table.*);
634
- } else {
635
- var it = context.level.iterator(.invisible, snapshots, .descending, null);
636
- while (it.next()) |table| try tables.append(table.*);
637
- mem.reverse(TableInfo, tables.items);
638
- }
639
-
640
- if (tables.items.len > 0) {
641
- for (tables.items) |*table| {
642
- context.level.remove_table(&context.pool, snapshots, table);
643
- }
644
- }
645
- }
646
-
647
- fn delete_tables(context: *Self) !void {
648
- const reference_len = @intCast(u32, context.reference.items.len);
649
- if (reference_len == 0) return;
650
-
651
- const count_max = @minimum(reference_len, 13);
652
- const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
653
-
654
- assert(context.reference.items.len <= table_count_max);
655
- const index = context.random.uintAtMostBiased(u32, reference_len - count);
656
-
657
- const snapshot = context.take_snapshot();
658
-
659
- for (context.reference.items[index..][0..count]) |*table| {
660
- context.level.set_snapshot_max(snapshot, table);
661
- }
662
-
663
- for (context.snapshot_tables.slice()) |tables| {
664
- for (tables.items) |*table| {
665
- for (context.reference.items[index..][0..count]) |modified| {
666
- if (table.address == modified.address) {
667
- table.snapshot_max = snapshot;
668
- assert(table.equal(&modified));
669
- }
670
- }
671
- }
672
- }
673
-
674
- {
675
- var to_remove = std.ArrayList(TableInfo).init(testing.allocator);
676
- defer to_remove.deinit();
677
-
678
- for (context.reference.items[index..][0..count]) |table| {
679
- if (table.invisible(context.snapshots.slice())) {
680
- try to_remove.append(table);
681
- }
682
- }
683
-
684
- if (log) {
685
- std.debug.print("Removing tables: ", .{});
686
- for (to_remove.items) |t| {
687
- std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
688
- }
689
- std.debug.print("\n", .{});
690
- std.debug.print("\nactual: ", .{});
691
- var it = context.level.iterator(
692
- .invisible,
693
- context.snapshots.slice(),
694
- .ascending,
695
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
696
- );
697
- while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
698
- std.debug.print("\n", .{});
699
- }
700
-
701
- if (to_remove.items.len > 0) {
702
- for (to_remove.items) |*table| {
703
- context.level.remove_table(
704
- &context.pool,
705
- context.snapshots.slice(),
706
- table,
707
- );
708
- }
709
- }
710
- }
711
-
712
- context.reference.replaceRange(index, count, &[0]TableInfo{}) catch unreachable;
713
-
714
- context.removes += count;
715
-
716
- try context.verify();
717
- }
718
-
719
- fn remove_all(context: *Self) !void {
720
- while (context.snapshots.len > 0) try context.drop_snapshot();
721
- while (context.reference.items.len > 0) try context.delete_tables();
722
-
723
- try testing.expectEqual(@as(u32, 0), context.level.keys.len());
724
- try testing.expectEqual(@as(u32, 0), context.level.tables.len());
725
- try testing.expect(context.inserts > 0);
726
- try testing.expect(context.inserts == context.removes);
727
-
728
- if (log) {
729
- std.debug.print("\ninserts: {}, removes: {}\n", .{
730
- context.inserts,
731
- context.removes,
732
- });
733
- }
734
-
735
- try context.verify();
736
- }
737
-
738
- fn verify(context: *Self) !void {
739
- try context.verify_snapshot(lsm.snapshot_latest, context.reference.items);
740
-
741
- for (context.snapshots.slice()) |snapshot, i| {
742
- try context.verify_snapshot(snapshot, context.snapshot_tables.get(i).items);
743
- }
744
- }
745
-
746
- fn verify_snapshot(context: *Self, snapshot: u64, reference: []const TableInfo) !void {
747
- if (log) {
748
- std.debug.print("\nsnapshot: {}\n", .{snapshot});
749
- std.debug.print("expect: ", .{});
750
- for (reference) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
751
-
752
- std.debug.print("\nactual: ", .{});
753
- var it = context.level.iterator(
754
- .visible,
755
- @as(*const [1]u64, &snapshot),
756
- .ascending,
757
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
758
- );
759
- while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
760
- std.debug.print("\n", .{});
761
- }
762
-
763
- {
764
- var it = context.level.iterator(
765
- .visible,
766
- @as(*const [1]u64, &snapshot),
767
- .ascending,
768
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
769
- );
770
-
771
- for (reference) |expect| {
772
- const actual = it.next() orelse return error.TestUnexpectedResult;
773
- try testing.expectEqual(expect, actual.*);
774
- }
775
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
776
- }
777
-
778
- {
779
- var it = context.level.iterator(
780
- .visible,
781
- @as(*const [1]u64, &snapshot),
782
- .descending,
783
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
784
- );
785
-
786
- var i = reference.len;
787
- while (i > 0) {
788
- i -= 1;
789
-
790
- const expect = reference[i];
791
- const actual = it.next() orelse return error.TestUnexpectedResult;
792
- try testing.expectEqual(expect, actual.*);
793
- }
794
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
795
- }
796
-
797
- if (reference.len > 0) {
798
- const reference_len = @intCast(u32, reference.len);
799
- const start = context.random.uintLessThanBiased(u32, reference_len);
800
- const end = context.random.uintLessThanBiased(u32, reference_len - start) + start;
801
-
802
- const key_min = reference[start].key_min;
803
- const key_max = reference[end].key_max;
804
-
805
- {
806
- var it = context.level.iterator(
807
- .visible,
808
- @as(*const [1]u64, &snapshot),
809
- .ascending,
810
- KeyRange{ .key_min = key_min, .key_max = key_max },
811
- );
812
-
813
- for (reference[start .. end + 1]) |expect| {
814
- const actual = it.next() orelse return error.TestUnexpectedResult;
815
- try testing.expectEqual(expect, actual.*);
816
- }
817
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
818
- }
819
-
820
- {
821
- var it = context.level.iterator(
822
- .visible,
823
- @as(*const [1]u64, &snapshot),
824
- .descending,
825
- KeyRange{ .key_min = key_min, .key_max = key_max },
826
- );
827
-
828
- var i = end + 1;
829
- while (i > start) {
830
- i -= 1;
831
-
832
- const expect = reference[i];
833
- const actual = it.next() orelse return error.TestUnexpectedResult;
834
- try testing.expectEqual(expect, actual.*);
835
- }
836
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
837
- }
838
- }
839
- }
840
-
841
- inline fn key_min_from_table(table: *const TableInfo) Key {
842
- return table.key_min;
843
- }
844
- };
845
- }
846
-
847
- test "ManifestLevel" {
848
- const seed = 42;
849
-
850
- var prng = std.rand.DefaultPrng.init(seed);
851
- const random = prng.random();
852
-
853
- const Options = struct {
854
- key_type: type,
855
- node_size: u32,
856
- table_count_max: u32,
857
- };
858
-
859
- inline for (.{
860
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 33 },
861
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 34 },
862
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 1024 },
863
- Options{ .key_type = u64, .node_size = 512, .table_count_max = 1024 },
864
- Options{ .key_type = u64, .node_size = 1024, .table_count_max = 1024 },
865
- }) |options| {
866
- const Context = TestContext(
867
- options.node_size,
868
- options.key_type,
869
- options.table_count_max,
870
- );
871
-
872
- var context = try Context.init(random);
873
- defer context.deinit();
874
-
875
- try context.run();
876
- }
877
- }