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.
- package/README.md +5 -10
- package/dist/bin/aarch64-linux-gnu/client.node +0 -0
- package/dist/bin/aarch64-linux-musl/client.node +0 -0
- package/dist/bin/aarch64-macos/client.node +0 -0
- package/dist/bin/x86_64-linux-gnu/client.node +0 -0
- package/dist/bin/x86_64-linux-musl/client.node +0 -0
- package/dist/bin/x86_64-macos/client.node +0 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package-lock.json +66 -0
- package/package.json +6 -16
- package/src/index.ts +56 -1
- package/src/node.zig +9 -9
- package/dist/.client.node.sha256 +0 -1
- package/scripts/build_lib.sh +0 -61
- package/scripts/download_node_headers.sh +0 -32
- package/src/tigerbeetle/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -336
- package/src/tigerbeetle/src/config.zig +0 -233
- package/src/tigerbeetle/src/constants.zig +0 -428
- package/src/tigerbeetle/src/ewah.zig +0 -286
- package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
- package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
- package/src/tigerbeetle/src/fifo.zig +0 -120
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -814
- package/src/tigerbeetle/src/io/linux.zig +0 -1071
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1183
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/iops.zig +0 -107
- package/src/tigerbeetle/src/lsm/README.md +0 -308
- package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
- package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
- package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
- package/src/tigerbeetle/src/lsm/direction.zig +0 -11
- package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
- package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
- package/src/tigerbeetle/src/lsm/forest.zig +0 -205
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
- package/src/tigerbeetle/src/lsm/grid.zig +0 -573
- package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
- package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
- package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
- package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
- package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
- package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
- package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
- package/src/tigerbeetle/src/lsm/table.zig +0 -1009
- package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
- package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
- package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
- package/src/tigerbeetle/src/lsm/test.zig +0 -439
- package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
- package/src/tigerbeetle/src/message_bus.zig +0 -1013
- package/src/tigerbeetle/src/message_pool.zig +0 -156
- package/src/tigerbeetle/src/ring_buffer.zig +0 -399
- package/src/tigerbeetle/src/simulator.zig +0 -580
- package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
- package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
- package/src/tigerbeetle/src/state_machine.zig +0 -2099
- package/src/tigerbeetle/src/static_allocator.zig +0 -65
- package/src/tigerbeetle/src/stdx.zig +0 -171
- package/src/tigerbeetle/src/storage.zig +0 -393
- package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
- package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
- package/src/tigerbeetle/src/testing/cluster.zig +0 -444
- package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
- package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
- package/src/tigerbeetle/src/testing/id.zig +0 -99
- package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
- package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
- package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
- package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
- package/src/tigerbeetle/src/testing/storage.zig +0 -757
- package/src/tigerbeetle/src/testing/table.zig +0 -247
- package/src/tigerbeetle/src/testing/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
- package/src/tigerbeetle/src/time.zig +0 -112
- package/src/tigerbeetle/src/tracer.zig +0 -529
- package/src/tigerbeetle/src/unit_tests.zig +0 -40
- package/src/tigerbeetle/src/vopr.zig +0 -495
- package/src/tigerbeetle/src/vsr/README.md +0 -209
- package/src/tigerbeetle/src/vsr/client.zig +0 -544
- package/src/tigerbeetle/src/vsr/clock.zig +0 -855
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
- package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
- package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
- 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
|
-
}
|