tigerbeetle-node 0.11.13 → 0.12.1

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 (147) hide show
  1. package/README.md +5 -10
  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 +6 -16
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +9 -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 -55
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
  21. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  22. package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
  23. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  24. package/src/tigerbeetle/scripts/install.bat +0 -7
  25. package/src/tigerbeetle/scripts/install.sh +0 -21
  26. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  27. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  28. package/src/tigerbeetle/scripts/lint.zig +0 -199
  29. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  30. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
  31. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  32. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  33. package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
  34. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  35. package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
  36. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  37. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  38. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  39. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  40. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  41. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  42. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  43. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  44. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  45. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  46. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  47. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  48. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  49. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  50. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  51. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  52. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  53. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  54. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  55. package/src/tigerbeetle/src/benchmark.zig +0 -336
  56. package/src/tigerbeetle/src/config.zig +0 -233
  57. package/src/tigerbeetle/src/constants.zig +0 -428
  58. package/src/tigerbeetle/src/ewah.zig +0 -286
  59. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  60. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  61. package/src/tigerbeetle/src/fifo.zig +0 -120
  62. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  63. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  64. package/src/tigerbeetle/src/io/linux.zig +0 -1071
  65. package/src/tigerbeetle/src/io/test.zig +0 -643
  66. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  67. package/src/tigerbeetle/src/io.zig +0 -34
  68. package/src/tigerbeetle/src/iops.zig +0 -107
  69. package/src/tigerbeetle/src/lsm/README.md +0 -308
  70. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  71. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  72. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  73. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  74. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  75. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  76. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  77. package/src/tigerbeetle/src/lsm/forest.zig +0 -205
  78. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
  79. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  80. package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
  81. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  82. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  83. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  84. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
  85. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  86. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  87. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  88. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  89. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
  90. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
  91. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  92. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  93. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  94. package/src/tigerbeetle/src/lsm/table.zig +0 -1009
  95. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
  96. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  97. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
  98. package/src/tigerbeetle/src/lsm/test.zig +0 -439
  99. package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
  100. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
  101. package/src/tigerbeetle/src/message_bus.zig +0 -1013
  102. package/src/tigerbeetle/src/message_pool.zig +0 -156
  103. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  104. package/src/tigerbeetle/src/simulator.zig +0 -580
  105. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
  106. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  107. package/src/tigerbeetle/src/state_machine.zig +0 -2099
  108. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  109. package/src/tigerbeetle/src/stdx.zig +0 -171
  110. package/src/tigerbeetle/src/storage.zig +0 -393
  111. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  112. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  113. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  114. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  115. package/src/tigerbeetle/src/testing/cluster.zig +0 -444
  116. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  117. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  118. package/src/tigerbeetle/src/testing/id.zig +0 -99
  119. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
  120. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  121. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  122. package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
  123. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  124. package/src/tigerbeetle/src/testing/table.zig +0 -247
  125. package/src/tigerbeetle/src/testing/time.zig +0 -84
  126. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  127. package/src/tigerbeetle/src/time.zig +0 -112
  128. package/src/tigerbeetle/src/tracer.zig +0 -529
  129. package/src/tigerbeetle/src/unit_tests.zig +0 -40
  130. package/src/tigerbeetle/src/vopr.zig +0 -495
  131. package/src/tigerbeetle/src/vsr/README.md +0 -209
  132. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  133. package/src/tigerbeetle/src/vsr/clock.zig +0 -855
  134. package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
  135. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  136. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  137. package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
  138. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  139. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  140. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  141. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  142. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  143. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  144. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  145. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  146. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  147. package/src/tigerbeetle/src/vsr.zig +0 -1425
@@ -1,878 +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
- 1, // Doesn't matter for this test.
421
- .general,
422
- );
423
-
424
- const TableInfo = @import("manifest.zig").TableInfoType(Table);
425
- const NodePool = @import("node_pool.zig").NodePool;
426
-
427
- const TestPool = NodePool(node_size, @alignOf(TableInfo));
428
- const TestLevel = ManifestLevelType(TestPool, Key, TableInfo, compare_keys, table_count_max);
429
- const KeyRange = TestLevel.KeyRange;
430
-
431
- random: std.rand.Random,
432
-
433
- pool: TestPool,
434
- level: TestLevel,
435
-
436
- snapshot_max: u64 = 1,
437
- snapshots: std.BoundedArray(u64, 8) = .{ .buffer = undefined },
438
- snapshot_tables: std.BoundedArray(std.ArrayList(TableInfo), 8) = .{ .buffer = undefined },
439
-
440
- /// Contains only tables with snapshot_max == lsm.snapshot_latest
441
- reference: std.ArrayList(TableInfo),
442
-
443
- inserts: u64 = 0,
444
- removes: u64 = 0,
445
-
446
- fn init(random: std.rand.Random) !Self {
447
- var pool = try TestPool.init(
448
- testing.allocator,
449
- TestLevel.Keys.node_count_max + TestLevel.Tables.node_count_max,
450
- );
451
- errdefer pool.deinit(testing.allocator);
452
-
453
- var level = try TestLevel.init(testing.allocator);
454
- errdefer level.deinit(testing.allocator, &pool);
455
-
456
- var reference = std.ArrayList(TableInfo).init(testing.allocator);
457
- errdefer reference.deinit();
458
-
459
- return Self{
460
- .random = random,
461
- .pool = pool,
462
- .level = level,
463
- .reference = reference,
464
- };
465
- }
466
-
467
- fn deinit(context: *Self) void {
468
- context.level.deinit(testing.allocator, &context.pool);
469
- context.pool.deinit(testing.allocator);
470
-
471
- for (context.snapshot_tables.slice()) |tables| tables.deinit();
472
-
473
- context.reference.deinit();
474
- }
475
-
476
- fn run(context: *Self) !void {
477
- if (log) std.debug.print("\n", .{});
478
-
479
- {
480
- var i: usize = 0;
481
- while (i < table_count_max * 2) : (i += 1) {
482
- switch (context.random.uintLessThanBiased(u32, 100)) {
483
- 0...59 => try context.insert_tables(),
484
- 60...69 => try context.create_snapshot(),
485
- 70...94 => try context.delete_tables(),
486
- 95...99 => try context.drop_snapshot(),
487
- else => unreachable,
488
- }
489
- }
490
- }
491
-
492
- {
493
- var i: usize = 0;
494
- while (i < table_count_max * 2) : (i += 1) {
495
- switch (context.random.uintLessThanBiased(u32, 100)) {
496
- 0...34 => try context.insert_tables(),
497
- 35...39 => try context.create_snapshot(),
498
- 40...89 => try context.delete_tables(),
499
- 90...99 => try context.drop_snapshot(),
500
- else => unreachable,
501
- }
502
- }
503
- }
504
-
505
- try context.remove_all();
506
- }
507
-
508
- fn insert_tables(context: *Self) !void {
509
- const count_free = table_count_max - context.level.keys.len();
510
-
511
- if (count_free == 0) return;
512
-
513
- var buffer: [13]TableInfo = undefined;
514
-
515
- const count_max = @minimum(count_free, 13);
516
- const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
517
-
518
- {
519
- var key: Key = context.random.uintAtMostBiased(Key, table_count_max * 64);
520
-
521
- for (buffer[0..count]) |*table| {
522
- table.* = context.random_greater_non_overlapping_table(key);
523
- key = table.key_max;
524
- }
525
- }
526
-
527
- for (buffer[0..count]) |*table| {
528
- context.level.insert_table(&context.pool, table);
529
- }
530
-
531
- for (buffer[0..count]) |table| {
532
- const index = binary_search.binary_search_values_raw(
533
- Key,
534
- TableInfo,
535
- key_min_from_table,
536
- compare_keys,
537
- context.reference.items,
538
- table.key_max,
539
- .{},
540
- );
541
- // Can't be equal as the tables may not overlap
542
- if (index < context.reference.items.len) {
543
- assert(context.reference.items[index].key_min > table.key_max);
544
- }
545
- context.reference.insert(index, table) catch unreachable;
546
- }
547
-
548
- context.inserts += count;
549
-
550
- try context.verify();
551
- }
552
-
553
- fn random_greater_non_overlapping_table(context: *Self, key: Key) TableInfo {
554
- var new_key_min = key + context.random.uintLessThanBiased(Key, 31) + 1;
555
-
556
- assert(compare_keys(new_key_min, key) == .gt);
557
-
558
- var i = binary_search.binary_search_values_raw(
559
- Key,
560
- TableInfo,
561
- key_min_from_table,
562
- compare_keys,
563
- context.reference.items,
564
- new_key_min,
565
- .{},
566
- );
567
-
568
- if (i > 0) {
569
- if (compare_keys(new_key_min, context.reference.items[i - 1].key_max) != .gt) {
570
- new_key_min = context.reference.items[i - 1].key_max + 1;
571
- }
572
- }
573
-
574
- const next_key_min = for (context.reference.items[i..]) |table| {
575
- switch (compare_keys(new_key_min, table.key_min)) {
576
- .lt => break table.key_min,
577
- .eq => new_key_min = table.key_max + 1,
578
- .gt => unreachable,
579
- }
580
- } else math.maxInt(Key);
581
-
582
- const max_delta = @minimum(32, next_key_min - 1 - new_key_min);
583
- const new_key_max = new_key_min + context.random.uintAtMostBiased(Key, max_delta);
584
-
585
- return .{
586
- .checksum = context.random.int(u128),
587
- .address = context.random.int(u64),
588
- .snapshot_min = context.take_snapshot(),
589
- .key_min = new_key_min,
590
- .key_max = new_key_max,
591
- };
592
- }
593
-
594
- /// See Manifest.take_snapshot()
595
- fn take_snapshot(context: *Self) u64 {
596
- // A snapshot cannot be 0 as this is a reserved value in the superblock.
597
- assert(context.snapshot_max > 0);
598
- // The constant snapshot_latest must compare greater than any issued snapshot.
599
- // This also ensures that we are not about to overflow the u64 counter.
600
- assert(context.snapshot_max < lsm.snapshot_latest - 1);
601
-
602
- context.snapshot_max += 1;
603
-
604
- return context.snapshot_max;
605
- }
606
-
607
- fn create_snapshot(context: *Self) !void {
608
- if (context.snapshots.len == context.snapshots.capacity()) return;
609
-
610
- context.snapshots.appendAssumeCapacity(context.take_snapshot());
611
-
612
- const tables = context.snapshot_tables.addOneAssumeCapacity();
613
- tables.* = std.ArrayList(TableInfo).init(testing.allocator);
614
- try tables.insertSlice(0, context.reference.items);
615
- }
616
-
617
- fn drop_snapshot(context: *Self) !void {
618
- if (context.snapshots.len == 0) return;
619
-
620
- const index = context.random.uintLessThanBiased(usize, context.snapshots.len);
621
-
622
- _ = context.snapshots.swapRemove(index);
623
- var tables = context.snapshot_tables.swapRemove(index);
624
- defer tables.deinit();
625
-
626
- // Use this memory as a scratch buffer since it's conveniently already allocated.
627
- tables.clearRetainingCapacity();
628
-
629
- const snapshots = context.snapshots.slice();
630
-
631
- // Ensure that iteration with a null key range in both directions is tested.
632
- if (context.random.boolean()) {
633
- var it = context.level.iterator(.invisible, snapshots, .ascending, null);
634
- while (it.next()) |table| try tables.append(table.*);
635
- } else {
636
- var it = context.level.iterator(.invisible, snapshots, .descending, null);
637
- while (it.next()) |table| try tables.append(table.*);
638
- mem.reverse(TableInfo, tables.items);
639
- }
640
-
641
- if (tables.items.len > 0) {
642
- for (tables.items) |*table| {
643
- context.level.remove_table(&context.pool, snapshots, table);
644
- }
645
- }
646
- }
647
-
648
- fn delete_tables(context: *Self) !void {
649
- const reference_len = @intCast(u32, context.reference.items.len);
650
- if (reference_len == 0) return;
651
-
652
- const count_max = @minimum(reference_len, 13);
653
- const count = context.random.uintAtMostBiased(u32, count_max - 1) + 1;
654
-
655
- assert(context.reference.items.len <= table_count_max);
656
- const index = context.random.uintAtMostBiased(u32, reference_len - count);
657
-
658
- const snapshot = context.take_snapshot();
659
-
660
- for (context.reference.items[index..][0..count]) |*table| {
661
- context.level.set_snapshot_max(snapshot, table);
662
- }
663
-
664
- for (context.snapshot_tables.slice()) |tables| {
665
- for (tables.items) |*table| {
666
- for (context.reference.items[index..][0..count]) |modified| {
667
- if (table.address == modified.address) {
668
- table.snapshot_max = snapshot;
669
- assert(table.equal(&modified));
670
- }
671
- }
672
- }
673
- }
674
-
675
- {
676
- var to_remove = std.ArrayList(TableInfo).init(testing.allocator);
677
- defer to_remove.deinit();
678
-
679
- for (context.reference.items[index..][0..count]) |table| {
680
- if (table.invisible(context.snapshots.slice())) {
681
- try to_remove.append(table);
682
- }
683
- }
684
-
685
- if (log) {
686
- std.debug.print("Removing tables: ", .{});
687
- for (to_remove.items) |t| {
688
- std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
689
- }
690
- std.debug.print("\n", .{});
691
- std.debug.print("\nactual: ", .{});
692
- var it = context.level.iterator(
693
- .invisible,
694
- context.snapshots.slice(),
695
- .ascending,
696
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
697
- );
698
- while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
699
- std.debug.print("\n", .{});
700
- }
701
-
702
- if (to_remove.items.len > 0) {
703
- for (to_remove.items) |*table| {
704
- context.level.remove_table(
705
- &context.pool,
706
- context.snapshots.slice(),
707
- table,
708
- );
709
- }
710
- }
711
- }
712
-
713
- context.reference.replaceRange(index, count, &[0]TableInfo{}) catch unreachable;
714
-
715
- context.removes += count;
716
-
717
- try context.verify();
718
- }
719
-
720
- fn remove_all(context: *Self) !void {
721
- while (context.snapshots.len > 0) try context.drop_snapshot();
722
- while (context.reference.items.len > 0) try context.delete_tables();
723
-
724
- try testing.expectEqual(@as(u32, 0), context.level.keys.len());
725
- try testing.expectEqual(@as(u32, 0), context.level.tables.len());
726
- try testing.expect(context.inserts > 0);
727
- try testing.expect(context.inserts == context.removes);
728
-
729
- if (log) {
730
- std.debug.print("\ninserts: {}, removes: {}\n", .{
731
- context.inserts,
732
- context.removes,
733
- });
734
- }
735
-
736
- try context.verify();
737
- }
738
-
739
- fn verify(context: *Self) !void {
740
- try context.verify_snapshot(lsm.snapshot_latest, context.reference.items);
741
-
742
- for (context.snapshots.slice()) |snapshot, i| {
743
- try context.verify_snapshot(snapshot, context.snapshot_tables.get(i).items);
744
- }
745
- }
746
-
747
- fn verify_snapshot(context: *Self, snapshot: u64, reference: []const TableInfo) !void {
748
- if (log) {
749
- std.debug.print("\nsnapshot: {}\n", .{snapshot});
750
- std.debug.print("expect: ", .{});
751
- for (reference) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
752
-
753
- std.debug.print("\nactual: ", .{});
754
- var it = context.level.iterator(
755
- .visible,
756
- @as(*const [1]u64, &snapshot),
757
- .ascending,
758
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
759
- );
760
- while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
761
- std.debug.print("\n", .{});
762
- }
763
-
764
- {
765
- var it = context.level.iterator(
766
- .visible,
767
- @as(*const [1]u64, &snapshot),
768
- .ascending,
769
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
770
- );
771
-
772
- for (reference) |expect| {
773
- const actual = it.next() orelse return error.TestUnexpectedResult;
774
- try testing.expectEqual(expect, actual.*);
775
- }
776
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
777
- }
778
-
779
- {
780
- var it = context.level.iterator(
781
- .visible,
782
- @as(*const [1]u64, &snapshot),
783
- .descending,
784
- KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
785
- );
786
-
787
- var i = reference.len;
788
- while (i > 0) {
789
- i -= 1;
790
-
791
- const expect = reference[i];
792
- const actual = it.next() orelse return error.TestUnexpectedResult;
793
- try testing.expectEqual(expect, actual.*);
794
- }
795
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
796
- }
797
-
798
- if (reference.len > 0) {
799
- const reference_len = @intCast(u32, reference.len);
800
- const start = context.random.uintLessThanBiased(u32, reference_len);
801
- const end = context.random.uintLessThanBiased(u32, reference_len - start) + start;
802
-
803
- const key_min = reference[start].key_min;
804
- const key_max = reference[end].key_max;
805
-
806
- {
807
- var it = context.level.iterator(
808
- .visible,
809
- @as(*const [1]u64, &snapshot),
810
- .ascending,
811
- KeyRange{ .key_min = key_min, .key_max = key_max },
812
- );
813
-
814
- for (reference[start .. end + 1]) |expect| {
815
- const actual = it.next() orelse return error.TestUnexpectedResult;
816
- try testing.expectEqual(expect, actual.*);
817
- }
818
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
819
- }
820
-
821
- {
822
- var it = context.level.iterator(
823
- .visible,
824
- @as(*const [1]u64, &snapshot),
825
- .descending,
826
- KeyRange{ .key_min = key_min, .key_max = key_max },
827
- );
828
-
829
- var i = end + 1;
830
- while (i > start) {
831
- i -= 1;
832
-
833
- const expect = reference[i];
834
- const actual = it.next() orelse return error.TestUnexpectedResult;
835
- try testing.expectEqual(expect, actual.*);
836
- }
837
- try testing.expectEqual(@as(?*const TableInfo, null), it.next());
838
- }
839
- }
840
- }
841
-
842
- inline fn key_min_from_table(table: *const TableInfo) Key {
843
- return table.key_min;
844
- }
845
- };
846
- }
847
-
848
- test "ManifestLevel" {
849
- const seed = 42;
850
-
851
- var prng = std.rand.DefaultPrng.init(seed);
852
- const random = prng.random();
853
-
854
- const Options = struct {
855
- key_type: type,
856
- node_size: u32,
857
- table_count_max: u32,
858
- };
859
-
860
- inline for (.{
861
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 33 },
862
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 34 },
863
- Options{ .key_type = u64, .node_size = 256, .table_count_max = 1024 },
864
- Options{ .key_type = u64, .node_size = 512, .table_count_max = 1024 },
865
- Options{ .key_type = u64, .node_size = 1024, .table_count_max = 1024 },
866
- }) |options| {
867
- const Context = TestContext(
868
- options.node_size,
869
- options.key_type,
870
- options.table_count_max,
871
- );
872
-
873
- var context = try Context.init(random);
874
- defer context.deinit();
875
-
876
- try context.run();
877
- }
878
- }