tigerbeetle-node 0.10.0 → 0.11.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 +302 -101
- package/dist/index.d.ts +70 -72
- package/dist/index.js +70 -72
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/scripts/download_node_headers.sh +14 -7
- package/src/index.ts +6 -10
- package/src/node.zig +6 -3
- package/src/tigerbeetle/scripts/benchmark.sh +4 -4
- package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
- package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
- package/src/tigerbeetle/scripts/install.sh +19 -4
- package/src/tigerbeetle/scripts/install_zig.bat +5 -1
- package/src/tigerbeetle/scripts/install_zig.sh +24 -14
- package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
- package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
- package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
- package/src/tigerbeetle/src/benchmark.zig +29 -13
- package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
- package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
- package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
- package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
- package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -257
- package/src/tigerbeetle/src/c/tb_client.h +118 -84
- package/src/tigerbeetle/src/c/tb_client.zig +88 -23
- package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
- package/src/tigerbeetle/src/c/test.zig +371 -1
- package/src/tigerbeetle/src/cli.zig +37 -7
- package/src/tigerbeetle/src/config.zig +58 -17
- package/src/tigerbeetle/src/demo.zig +5 -2
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
- package/src/tigerbeetle/src/ewah.zig +11 -33
- package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
- package/src/tigerbeetle/src/io/linux.zig +1 -1
- package/src/tigerbeetle/src/lsm/README.md +308 -0
- package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +376 -397
- package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
- package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
- package/src/tigerbeetle/src/lsm/forest.zig +21 -447
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +414 -0
- package/src/tigerbeetle/src/lsm/grid.zig +170 -76
- package/src/tigerbeetle/src/lsm/groove.zig +197 -133
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
- package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
- package/src/tigerbeetle/src/lsm/manifest.zig +93 -180
- package/src/tigerbeetle/src/lsm/manifest_level.zig +161 -454
- package/src/tigerbeetle/src/lsm/manifest_log.zig +243 -356
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
- package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +65 -76
- package/src/tigerbeetle/src/lsm/segmented_array.zig +580 -251
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
- package/src/tigerbeetle/src/lsm/table.zig +115 -68
- package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
- package/src/tigerbeetle/src/lsm/table_iterator.zig +27 -17
- package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
- package/src/tigerbeetle/src/lsm/test.zig +61 -56
- package/src/tigerbeetle/src/lsm/tree.zig +450 -407
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +461 -0
- package/src/tigerbeetle/src/main.zig +83 -8
- package/src/tigerbeetle/src/message_bus.zig +20 -9
- package/src/tigerbeetle/src/message_pool.zig +22 -19
- package/src/tigerbeetle/src/ring_buffer.zig +7 -3
- package/src/tigerbeetle/src/simulator.zig +179 -119
- package/src/tigerbeetle/src/state_machine.zig +381 -246
- package/src/tigerbeetle/src/static_allocator.zig +65 -0
- package/src/tigerbeetle/src/storage.zig +3 -7
- package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
- package/src/tigerbeetle/src/test/accounting/workload.zig +823 -0
- package/src/tigerbeetle/src/test/cluster.zig +33 -81
- package/src/tigerbeetle/src/test/conductor.zig +366 -0
- package/src/tigerbeetle/src/test/fuzz.zig +121 -0
- package/src/tigerbeetle/src/test/id.zig +89 -0
- package/src/tigerbeetle/src/test/network.zig +45 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
- package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
- package/src/tigerbeetle/src/test/state_checker.zig +91 -69
- package/src/tigerbeetle/src/test/state_machine.zig +11 -35
- package/src/tigerbeetle/src/test/storage.zig +470 -106
- package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +15 -16
- package/src/tigerbeetle/src/unit_tests.zig +13 -1
- package/src/tigerbeetle/src/util.zig +97 -11
- package/src/tigerbeetle/src/vopr.zig +495 -0
- package/src/tigerbeetle/src/vsr/client.zig +21 -3
- package/src/tigerbeetle/src/vsr/journal.zig +293 -212
- package/src/tigerbeetle/src/vsr/replica.zig +1086 -515
- package/src/tigerbeetle/src/vsr/superblock.zig +382 -637
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +14 -16
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +416 -153
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +62 -12
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
- package/src/tigerbeetle/src/vsr.zig +94 -60
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
- package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
- package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
|
@@ -28,11 +28,20 @@ const log = std.log.scoped(.manifest_log);
|
|
|
28
28
|
|
|
29
29
|
const config = @import("../config.zig");
|
|
30
30
|
const vsr = @import("../vsr.zig");
|
|
31
|
+
const util = @import("../util.zig");
|
|
31
32
|
|
|
32
33
|
const SuperBlockType = vsr.SuperBlockType;
|
|
33
34
|
const GridType = @import("grid.zig").GridType;
|
|
35
|
+
const BlockType = @import("grid.zig").BlockType;
|
|
36
|
+
const tree = @import("tree.zig");
|
|
34
37
|
const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
|
|
35
38
|
|
|
39
|
+
/// ManifestLog block schema:
|
|
40
|
+
/// │ vsr.Header │ operation=BlockType.manifest
|
|
41
|
+
/// │ [entry_count_max]Label │ level index, insert|remove
|
|
42
|
+
/// │ [≤entry_count_max]TableInfo │
|
|
43
|
+
/// │ […]u8{0} │ padding (to end of block)
|
|
44
|
+
/// Label and TableInfo entries correspond.
|
|
36
45
|
pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
37
46
|
return struct {
|
|
38
47
|
const ManifestLog = @This();
|
|
@@ -40,8 +49,10 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
40
49
|
const SuperBlock = SuperBlockType(Storage);
|
|
41
50
|
const Grid = GridType(Storage);
|
|
42
51
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
52
|
+
pub const Block = ManifestLogBlockType(Storage, TableInfo);
|
|
53
|
+
const BlockPtr = Grid.BlockPtr;
|
|
54
|
+
const BlockPtrConst = Grid.BlockPtrConst;
|
|
55
|
+
const Label = Block.Label;
|
|
45
56
|
|
|
46
57
|
pub const Callback = fn (manifest_log: *ManifestLog) void;
|
|
47
58
|
|
|
@@ -51,11 +62,6 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
51
62
|
table: *const TableInfo,
|
|
52
63
|
) void;
|
|
53
64
|
|
|
54
|
-
pub const Label = packed struct {
|
|
55
|
-
level: u7,
|
|
56
|
-
event: enum(u1) { insert, remove },
|
|
57
|
-
};
|
|
58
|
-
|
|
59
65
|
const alignment = 16;
|
|
60
66
|
|
|
61
67
|
comptime {
|
|
@@ -71,72 +77,92 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
71
77
|
// However, we still store Label ahead of TableInfo to save space on the network.
|
|
72
78
|
// This means we store fewer entries per manifest block, to gain less padding,
|
|
73
79
|
// since we must store entry_count_max of whichever array is first in the layout.
|
|
74
|
-
// For a better understanding of this decision, see
|
|
80
|
+
// For a better understanding of this decision, see Block.size() below.
|
|
75
81
|
assert(@sizeOf(TableInfo) % alignment == 0);
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
/// The maximum number of table updates to the manifest by a half-measure of table
|
|
85
|
+
/// compaction (not including manifest log compaction).
|
|
86
|
+
///
|
|
87
|
+
/// Input tables are updated in the manifest (snapshot_max is reduced).
|
|
88
|
+
/// Input tables are removed from the manifest (if not held by a persistent snapshot).
|
|
89
|
+
/// Output tables are inserted into the manifest.
|
|
90
|
+
// TODO If insert-then-remove can update in-memory, then we can only count input tables once.
|
|
91
|
+
pub const compaction_appends_max = tree.compactions_max *
|
|
92
|
+
(tree.compaction_tables_input_max + // Update snapshot_max.
|
|
93
|
+
tree.compaction_tables_input_max + // Remove.
|
|
94
|
+
tree.compaction_tables_output_max);
|
|
95
|
+
|
|
96
|
+
const blocks_count_appends = util.div_ceil(compaction_appends_max, Block.entry_count_max);
|
|
97
|
+
|
|
98
|
+
/// The upper-bound of manifest log blocks we must buffer.
|
|
99
|
+
///
|
|
100
|
+
/// `blocks` must have sufficient capacity for:
|
|
101
|
+
/// - a manifest log compaction (+1 block in the worst case)
|
|
102
|
+
/// - a leftover open block from the previous ops (+1 block)
|
|
103
|
+
/// - table updates from a half bar of compactions
|
|
104
|
+
/// (This is typically +1 block, but may be more when the block size is small).
|
|
105
|
+
/// TODO(Beat compaction): blocks_count_appends only needs enough for 1 beat.
|
|
106
|
+
const blocks_count_max = 1 + 1 + blocks_count_appends;
|
|
82
107
|
|
|
83
108
|
comptime {
|
|
84
|
-
assert(
|
|
85
|
-
assert(
|
|
86
|
-
assert((entry_count_max * @sizeOf(TableInfo)) % alignment == 0);
|
|
109
|
+
assert(blocks_count_max >= 3);
|
|
110
|
+
assert(blocks_count_max == 3 or config.block_size < 64 * 1024);
|
|
87
111
|
}
|
|
88
112
|
|
|
89
113
|
superblock: *SuperBlock,
|
|
90
114
|
grid: *Grid,
|
|
115
|
+
grid_reservation: ?Grid.Reservation = null,
|
|
91
116
|
tree_hash: u128,
|
|
92
117
|
|
|
93
118
|
/// The head block is used to accumulate a full block, to be written at the next flush.
|
|
94
119
|
/// The remaining blocks must accommodate all further appends.
|
|
95
|
-
|
|
96
|
-
blocks: RingBuffer(BlockPtr, 3, .array),
|
|
120
|
+
blocks: RingBuffer(BlockPtr, blocks_count_max, .array),
|
|
97
121
|
|
|
98
122
|
/// The number of blocks that have been appended to, filled up, and then closed.
|
|
99
123
|
blocks_closed: u8 = 0,
|
|
100
124
|
|
|
101
125
|
/// The number of entries in the open block.
|
|
126
|
+
///
|
|
127
|
+
/// Invariants:
|
|
128
|
+
/// - When `entry_count = 0`, there is no open block.
|
|
129
|
+
/// - `entry_count < entry_count_max`. When `entry_count` reaches the maximum, the open
|
|
130
|
+
/// block is closed, and `entry_count` resets to 0.
|
|
102
131
|
entry_count: u32 = 0,
|
|
103
132
|
|
|
104
133
|
opened: bool = false,
|
|
105
134
|
open_event: OpenEvent = undefined,
|
|
106
135
|
open_iterator: SuperBlock.Manifest.IteratorReverse = undefined,
|
|
107
136
|
|
|
137
|
+
/// Set for the duration of `compact`.
|
|
108
138
|
reading: bool = false,
|
|
109
139
|
read: Grid.Read = undefined,
|
|
110
|
-
read_callback: Callback =
|
|
140
|
+
read_callback: ?Callback = null,
|
|
111
141
|
read_block_reference: ?SuperBlock.Manifest.BlockReference = null,
|
|
112
142
|
|
|
143
|
+
/// Set for the duration of `flush` and `checkpoint`.
|
|
113
144
|
writing: bool = false,
|
|
114
145
|
write: Grid.Write = undefined,
|
|
115
|
-
write_callback: Callback =
|
|
146
|
+
write_callback: ?Callback = null,
|
|
116
147
|
|
|
117
148
|
pub fn init(allocator: mem.Allocator, grid: *Grid, tree_hash: u128) !ManifestLog {
|
|
118
149
|
// TODO RingBuffer for .pointer should be extended to take care of alignment:
|
|
119
150
|
|
|
120
|
-
|
|
121
|
-
|
|
151
|
+
var blocks: [blocks_count_max]BlockPtr = undefined;
|
|
152
|
+
for (blocks) |*block, i| {
|
|
153
|
+
errdefer for (blocks[0..i]) |b| allocator.free(b);
|
|
122
154
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
errdefer allocator.free(b);
|
|
155
|
+
const block_slice =
|
|
156
|
+
try allocator.alignedAlloc(u8, config.sector_size, config.block_size);
|
|
157
|
+
block.* = block_slice[0..config.block_size];
|
|
158
|
+
}
|
|
159
|
+
errdefer for (blocks) |b| allocator.free(b);
|
|
128
160
|
|
|
129
161
|
return ManifestLog{
|
|
130
162
|
.superblock = grid.superblock,
|
|
131
163
|
.grid = grid,
|
|
132
164
|
.tree_hash = tree_hash,
|
|
133
|
-
.blocks = .{
|
|
134
|
-
.buffer = .{
|
|
135
|
-
a[0..config.block_size],
|
|
136
|
-
b[0..config.block_size],
|
|
137
|
-
c[0..config.block_size],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
165
|
+
.blocks = .{ .buffer = blocks },
|
|
140
166
|
};
|
|
141
167
|
}
|
|
142
168
|
|
|
@@ -154,6 +180,11 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
154
180
|
assert(!manifest_log.opened);
|
|
155
181
|
assert(!manifest_log.reading);
|
|
156
182
|
assert(!manifest_log.writing);
|
|
183
|
+
assert(manifest_log.read_callback == null);
|
|
184
|
+
|
|
185
|
+
assert(manifest_log.blocks.count == 0);
|
|
186
|
+
assert(manifest_log.blocks_closed == 0);
|
|
187
|
+
assert(manifest_log.entry_count == 0);
|
|
157
188
|
|
|
158
189
|
manifest_log.open_event = event;
|
|
159
190
|
manifest_log.open_iterator = manifest_log.superblock.manifest.iterator_reverse(
|
|
@@ -171,6 +202,10 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
171
202
|
assert(manifest_log.reading);
|
|
172
203
|
assert(!manifest_log.writing);
|
|
173
204
|
|
|
205
|
+
assert(manifest_log.blocks.count == 0);
|
|
206
|
+
assert(manifest_log.blocks_closed == 0);
|
|
207
|
+
assert(manifest_log.entry_count == 0);
|
|
208
|
+
|
|
174
209
|
manifest_log.read_block_reference = manifest_log.open_iterator.next();
|
|
175
210
|
|
|
176
211
|
if (manifest_log.read_block_reference) |block| {
|
|
@@ -182,15 +217,16 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
182
217
|
&manifest_log.read,
|
|
183
218
|
block.address,
|
|
184
219
|
block.checksum,
|
|
220
|
+
.manifest,
|
|
185
221
|
);
|
|
186
222
|
} else {
|
|
187
223
|
manifest_log.opened = true;
|
|
188
224
|
manifest_log.open_event = undefined;
|
|
189
225
|
manifest_log.open_iterator = undefined;
|
|
190
226
|
|
|
191
|
-
const callback = manifest_log.read_callback
|
|
227
|
+
const callback = manifest_log.read_callback.?;
|
|
192
228
|
manifest_log.reading = false;
|
|
193
|
-
manifest_log.read_callback =
|
|
229
|
+
manifest_log.read_callback = null;
|
|
194
230
|
assert(manifest_log.read_block_reference == null);
|
|
195
231
|
|
|
196
232
|
callback(manifest_log);
|
|
@@ -206,9 +242,9 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
206
242
|
const block_reference = manifest_log.read_block_reference.?;
|
|
207
243
|
verify_block(block, block_reference.checksum, block_reference.address);
|
|
208
244
|
|
|
209
|
-
const entry_count =
|
|
210
|
-
const labels_used = labels_const(block)[0..entry_count];
|
|
211
|
-
const tables_used = tables_const(block)[0..entry_count];
|
|
245
|
+
const entry_count = Block.entry_count(block);
|
|
246
|
+
const labels_used = Block.labels_const(block)[0..entry_count];
|
|
247
|
+
const tables_used = Block.tables_const(block)[0..entry_count];
|
|
212
248
|
|
|
213
249
|
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
214
250
|
|
|
@@ -229,6 +265,10 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
229
265
|
}
|
|
230
266
|
}
|
|
231
267
|
|
|
268
|
+
if (Block.entry_count(block) < Block.entry_count_max) {
|
|
269
|
+
manifest.queue_for_compaction(block_reference.address);
|
|
270
|
+
}
|
|
271
|
+
|
|
232
272
|
log.debug("{}: opened: checksum={} address={} entries={}", .{
|
|
233
273
|
manifest_log.tree_hash,
|
|
234
274
|
block_reference.checksum,
|
|
@@ -243,12 +283,14 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
243
283
|
/// A move is only recorded as an insert, there is no remove from the previous level, since
|
|
244
284
|
/// this is safer (no potential to get the event order wrong) and reduces fragmentation.
|
|
245
285
|
pub fn insert(manifest_log: *ManifestLog, level: u7, table: *const TableInfo) void {
|
|
286
|
+
assert(!manifest_log.writing);
|
|
246
287
|
manifest_log.append(.{ .level = level, .event = .insert }, table);
|
|
247
288
|
}
|
|
248
289
|
|
|
249
290
|
/// Appends the removal of a table from a level.
|
|
250
291
|
/// The table must have previously been inserted to the manifest log.
|
|
251
292
|
pub fn remove(manifest_log: *ManifestLog, level: u7, table: *const TableInfo) void {
|
|
293
|
+
assert(!manifest_log.writing);
|
|
252
294
|
manifest_log.append(.{ .level = level, .event = .remove }, table);
|
|
253
295
|
}
|
|
254
296
|
|
|
@@ -259,17 +301,14 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
259
301
|
assert(table.snapshot_min > 0);
|
|
260
302
|
assert(table.snapshot_max > table.snapshot_min);
|
|
261
303
|
|
|
262
|
-
if (manifest_log.
|
|
263
|
-
manifest_log.
|
|
264
|
-
} else if (manifest_log.entry_count == entry_count_max) {
|
|
265
|
-
assert(manifest_log.blocks.count > 0);
|
|
266
|
-
manifest_log.close_block();
|
|
304
|
+
if (manifest_log.entry_count == 0) {
|
|
305
|
+
assert(manifest_log.blocks.count == manifest_log.blocks_closed);
|
|
267
306
|
manifest_log.acquire_block();
|
|
268
307
|
} else if (manifest_log.entry_count > 0) {
|
|
269
308
|
assert(manifest_log.blocks.count > 0);
|
|
270
309
|
}
|
|
271
310
|
|
|
272
|
-
assert(manifest_log.entry_count < entry_count_max);
|
|
311
|
+
assert(manifest_log.entry_count < Block.entry_count_max);
|
|
273
312
|
assert(manifest_log.blocks.count - manifest_log.blocks_closed == 1);
|
|
274
313
|
|
|
275
314
|
log.debug(
|
|
@@ -288,11 +327,11 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
288
327
|
|
|
289
328
|
const block: BlockPtr = manifest_log.blocks.tail().?;
|
|
290
329
|
const entry = manifest_log.entry_count;
|
|
291
|
-
labels(block)[entry] = label;
|
|
292
|
-
tables(block)[entry] = table.*;
|
|
330
|
+
Block.labels(block)[entry] = label;
|
|
331
|
+
Block.tables(block)[entry] = table.*;
|
|
293
332
|
|
|
294
333
|
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
295
|
-
const address =
|
|
334
|
+
const address = Block.address(block);
|
|
296
335
|
if (manifest.update_table_extent(table.address, address, entry)) |previous_block| {
|
|
297
336
|
manifest.queue_for_compaction(previous_block);
|
|
298
337
|
if (label.event == .remove) manifest.queue_for_compaction(address);
|
|
@@ -302,12 +341,17 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
302
341
|
}
|
|
303
342
|
|
|
304
343
|
manifest_log.entry_count += 1;
|
|
344
|
+
if (manifest_log.entry_count == Block.entry_count_max) {
|
|
345
|
+
manifest_log.close_block();
|
|
346
|
+
assert(manifest_log.entry_count == 0);
|
|
347
|
+
}
|
|
305
348
|
}
|
|
306
349
|
|
|
307
|
-
|
|
350
|
+
fn flush(manifest_log: *ManifestLog, callback: Callback) void {
|
|
308
351
|
assert(manifest_log.opened);
|
|
309
352
|
assert(!manifest_log.reading);
|
|
310
353
|
assert(!manifest_log.writing);
|
|
354
|
+
assert(manifest_log.write_callback == null);
|
|
311
355
|
|
|
312
356
|
manifest_log.writing = true;
|
|
313
357
|
manifest_log.write_callback = callback;
|
|
@@ -316,6 +360,26 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
316
360
|
manifest_log.tree_hash,
|
|
317
361
|
manifest_log.blocks_closed,
|
|
318
362
|
});
|
|
363
|
+
|
|
364
|
+
// The manifest is updated synchronously relative to the beginning of compact() and
|
|
365
|
+
// checkpoint() so that the SuperBlock.Manifest.append()s are deterministic relative
|
|
366
|
+
// to other trees' manifest logs.
|
|
367
|
+
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
368
|
+
var i: usize = 0;
|
|
369
|
+
while (i < manifest_log.blocks_closed) : (i += 1) {
|
|
370
|
+
const block = manifest_log.blocks.get_ptr(i).?.*;
|
|
371
|
+
verify_block(block, null, null);
|
|
372
|
+
|
|
373
|
+
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
374
|
+
const address = Block.address(block);
|
|
375
|
+
assert(address > 0);
|
|
376
|
+
|
|
377
|
+
manifest.append(manifest_log.tree_hash, header.checksum, address);
|
|
378
|
+
if (Block.entry_count(block) < Block.entry_count_max) {
|
|
379
|
+
manifest.queue_for_compaction(address);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
319
383
|
manifest_log.write_block();
|
|
320
384
|
}
|
|
321
385
|
|
|
@@ -329,11 +393,11 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
329
393
|
assert(manifest_log.entry_count == 0);
|
|
330
394
|
} else {
|
|
331
395
|
assert(manifest_log.blocks.count == 1);
|
|
332
|
-
assert(manifest_log.entry_count < entry_count_max);
|
|
396
|
+
assert(manifest_log.entry_count < Block.entry_count_max);
|
|
333
397
|
}
|
|
334
398
|
|
|
335
|
-
const callback = manifest_log.write_callback
|
|
336
|
-
manifest_log.write_callback =
|
|
399
|
+
const callback = manifest_log.write_callback.?;
|
|
400
|
+
manifest_log.write_callback = null;
|
|
337
401
|
manifest_log.writing = false;
|
|
338
402
|
|
|
339
403
|
callback(manifest_log);
|
|
@@ -344,15 +408,16 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
344
408
|
verify_block(block, null, null);
|
|
345
409
|
|
|
346
410
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
347
|
-
const address =
|
|
411
|
+
const address = Block.address(block);
|
|
348
412
|
assert(address > 0);
|
|
349
413
|
|
|
350
|
-
const entry_count =
|
|
414
|
+
const entry_count = Block.entry_count(block);
|
|
351
415
|
|
|
352
416
|
if (manifest_log.blocks_closed == 1 and manifest_log.blocks.count == 1) {
|
|
417
|
+
// This might be the last block of a checkpoint, which can be a partial block.
|
|
353
418
|
assert(entry_count > 0);
|
|
354
419
|
} else {
|
|
355
|
-
assert(entry_count == entry_count_max);
|
|
420
|
+
assert(entry_count == Block.entry_count_max);
|
|
356
421
|
}
|
|
357
422
|
|
|
358
423
|
log.debug("{}: write_block: checksum={} address={} entries={}", .{
|
|
@@ -375,18 +440,6 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
375
440
|
assert(manifest_log.opened);
|
|
376
441
|
assert(manifest_log.writing);
|
|
377
442
|
|
|
378
|
-
const block = manifest_log.blocks.head().?;
|
|
379
|
-
verify_block(block, null, null);
|
|
380
|
-
|
|
381
|
-
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
382
|
-
const address = block_address(block);
|
|
383
|
-
assert(address > 0);
|
|
384
|
-
|
|
385
|
-
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
386
|
-
|
|
387
|
-
manifest.append(manifest_log.tree_hash, header.checksum, address);
|
|
388
|
-
if (block_entry_count(block) < entry_count_max) manifest.queue_for_compaction(address);
|
|
389
|
-
|
|
390
443
|
manifest_log.blocks_closed -= 1;
|
|
391
444
|
manifest_log.blocks.advance_head();
|
|
392
445
|
assert(manifest_log.blocks_closed <= manifest_log.blocks.count);
|
|
@@ -394,42 +447,82 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
394
447
|
manifest_log.write_block();
|
|
395
448
|
}
|
|
396
449
|
|
|
450
|
+
pub fn reserve(manifest_log: *ManifestLog) void {
|
|
451
|
+
assert(manifest_log.opened);
|
|
452
|
+
assert(!manifest_log.reading);
|
|
453
|
+
assert(!manifest_log.writing);
|
|
454
|
+
assert(manifest_log.read_callback == null);
|
|
455
|
+
assert(manifest_log.write_callback == null);
|
|
456
|
+
assert(manifest_log.grid_reservation == null);
|
|
457
|
+
// reserve() is called at the start of compaction, so we have:
|
|
458
|
+
// - at most 1 closed block, and
|
|
459
|
+
// - at most 1 open block
|
|
460
|
+
// due to the last log compaction plus a leftover partial block.
|
|
461
|
+
assert(manifest_log.blocks_closed <= 1);
|
|
462
|
+
assert(manifest_log.blocks.count <= manifest_log.blocks_closed + 1);
|
|
463
|
+
|
|
464
|
+
// TODO Make sure this cannot fail — before compaction begins verify that enough free
|
|
465
|
+
// blocks are available for all reservations.
|
|
466
|
+
// +1 for the manifest log block compaction, which acquires at most one block.
|
|
467
|
+
manifest_log.grid_reservation = manifest_log.grid.reserve(1 + blocks_count_appends).?;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/// `compact` does not close a partial block; that is only necessary during `checkpoint`.
|
|
397
471
|
pub fn compact(manifest_log: *ManifestLog, callback: Callback) void {
|
|
472
|
+
assert(manifest_log.opened);
|
|
398
473
|
assert(!manifest_log.reading);
|
|
474
|
+
assert(!manifest_log.writing);
|
|
475
|
+
assert(manifest_log.read_callback == null);
|
|
476
|
+
assert(manifest_log.write_callback == null);
|
|
477
|
+
assert(manifest_log.grid_reservation != null);
|
|
478
|
+
|
|
479
|
+
const free_set = manifest_log.grid.superblock.free_set;
|
|
480
|
+
assert(free_set.count_free_reserved(manifest_log.grid_reservation.?) >= 1);
|
|
481
|
+
|
|
399
482
|
manifest_log.read_callback = callback;
|
|
400
|
-
manifest_log.flush(
|
|
483
|
+
manifest_log.flush(compact_flush_callback);
|
|
401
484
|
}
|
|
402
485
|
|
|
403
|
-
fn
|
|
404
|
-
const callback = manifest_log.read_callback
|
|
405
|
-
manifest_log.read_callback = undefined;
|
|
486
|
+
fn compact_flush_callback(manifest_log: *ManifestLog) void {
|
|
487
|
+
const callback = manifest_log.read_callback.?;
|
|
406
488
|
|
|
407
489
|
assert(manifest_log.opened);
|
|
408
490
|
assert(!manifest_log.reading);
|
|
409
491
|
assert(!manifest_log.writing);
|
|
492
|
+
assert(manifest_log.blocks_closed == 0);
|
|
493
|
+
assert(manifest_log.grid_reservation != null);
|
|
410
494
|
|
|
411
495
|
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
412
496
|
|
|
497
|
+
// Compact a single manifest block — to minimize latency spikes, we want to do the bare
|
|
498
|
+
// minimum of compaction work required.
|
|
499
|
+
// TODO Compact more than 1 block if fragmentation is outstripping the compaction rate.
|
|
500
|
+
// (Make sure to update the grid block reservation to account for this).
|
|
501
|
+
// Or assert that compactions cannot update blocks fast enough to outpace manifest
|
|
502
|
+
// log compaction (relative to the number of updates that fit in a manifest log block).
|
|
413
503
|
if (manifest.oldest_block_queued_for_compaction(manifest_log.tree_hash)) |block| {
|
|
414
504
|
assert(block.tree == manifest_log.tree_hash);
|
|
415
505
|
assert(block.address > 0);
|
|
416
506
|
|
|
417
507
|
manifest_log.reading = true;
|
|
418
|
-
manifest_log.read_callback = callback;
|
|
419
508
|
manifest_log.read_block_reference = block;
|
|
420
509
|
|
|
421
510
|
manifest_log.grid.read_block(
|
|
422
|
-
|
|
511
|
+
compact_read_block_callback,
|
|
423
512
|
&manifest_log.read,
|
|
424
513
|
block.address,
|
|
425
514
|
block.checksum,
|
|
515
|
+
.manifest,
|
|
426
516
|
);
|
|
427
517
|
} else {
|
|
518
|
+
manifest_log.read_callback = null;
|
|
519
|
+
manifest_log.grid.forfeit(manifest_log.grid_reservation.?);
|
|
520
|
+
manifest_log.grid_reservation = null;
|
|
428
521
|
callback(manifest_log);
|
|
429
522
|
}
|
|
430
523
|
}
|
|
431
524
|
|
|
432
|
-
fn
|
|
525
|
+
fn compact_read_block_callback(read: *Grid.Read, block: BlockPtrConst) void {
|
|
433
526
|
const manifest_log = @fieldParentPtr(ManifestLog, "read", read);
|
|
434
527
|
assert(manifest_log.opened);
|
|
435
528
|
assert(manifest_log.reading);
|
|
@@ -438,9 +531,9 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
438
531
|
const block_reference = manifest_log.read_block_reference.?;
|
|
439
532
|
verify_block(block, block_reference.checksum, block_reference.address);
|
|
440
533
|
|
|
441
|
-
const entry_count =
|
|
442
|
-
const labels_used = labels_const(block)[0..entry_count];
|
|
443
|
-
const tables_used = tables_const(block)[0..entry_count];
|
|
534
|
+
const entry_count = Block.entry_count(block);
|
|
535
|
+
const labels_used = Block.labels_const(block)[0..entry_count];
|
|
536
|
+
const tables_used = Block.tables_const(block)[0..entry_count];
|
|
444
537
|
|
|
445
538
|
const manifest: *SuperBlock.Manifest = &manifest_log.superblock.manifest;
|
|
446
539
|
assert(manifest.tables.count() > 0);
|
|
@@ -479,7 +572,7 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
479
572
|
|
|
480
573
|
// Blocks may be compacted if they contain frees, or are not completely full.
|
|
481
574
|
// For example, a partial block may be flushed as part of a checkpoint.
|
|
482
|
-
assert(frees > 0 or entry_count < entry_count_max);
|
|
575
|
+
assert(frees > 0 or entry_count < Block.entry_count_max);
|
|
483
576
|
|
|
484
577
|
assert(manifest.queued_for_compaction(block_reference.address));
|
|
485
578
|
manifest.remove(
|
|
@@ -489,11 +582,13 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
489
582
|
);
|
|
490
583
|
assert(!manifest.queued_for_compaction(block_reference.address));
|
|
491
584
|
|
|
492
|
-
manifest_log.
|
|
585
|
+
manifest_log.grid.release(block_reference.address);
|
|
586
|
+
manifest_log.grid.forfeit(manifest_log.grid_reservation.?);
|
|
587
|
+
manifest_log.grid_reservation = null;
|
|
493
588
|
|
|
494
|
-
const callback = manifest_log.read_callback
|
|
589
|
+
const callback = manifest_log.read_callback.?;
|
|
495
590
|
manifest_log.reading = false;
|
|
496
|
-
manifest_log.read_callback =
|
|
591
|
+
manifest_log.read_callback = null;
|
|
497
592
|
manifest_log.read_block_reference = null;
|
|
498
593
|
|
|
499
594
|
callback(manifest_log);
|
|
@@ -503,9 +598,8 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
503
598
|
assert(manifest_log.opened);
|
|
504
599
|
assert(!manifest_log.reading);
|
|
505
600
|
assert(!manifest_log.writing);
|
|
506
|
-
|
|
507
|
-
manifest_log.
|
|
508
|
-
manifest_log.write_callback = callback;
|
|
601
|
+
assert(manifest_log.write_callback == null);
|
|
602
|
+
assert(manifest_log.grid_reservation == null);
|
|
509
603
|
|
|
510
604
|
if (manifest_log.entry_count > 0) {
|
|
511
605
|
manifest_log.close_block();
|
|
@@ -514,12 +608,13 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
514
608
|
assert(manifest_log.blocks_closed == manifest_log.blocks.count);
|
|
515
609
|
}
|
|
516
610
|
|
|
517
|
-
|
|
518
|
-
manifest_log.write_block();
|
|
611
|
+
manifest_log.flush(callback);
|
|
519
612
|
}
|
|
520
613
|
|
|
521
614
|
fn acquire_block(manifest_log: *ManifestLog) void {
|
|
615
|
+
assert(manifest_log.opened);
|
|
522
616
|
assert(manifest_log.entry_count == 0);
|
|
617
|
+
assert(manifest_log.blocks.count == manifest_log.blocks_closed);
|
|
523
618
|
assert(!manifest_log.blocks.full());
|
|
524
619
|
|
|
525
620
|
manifest_log.blocks.advance_tail();
|
|
@@ -529,27 +624,29 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
529
624
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
530
625
|
header.* = .{
|
|
531
626
|
.cluster = manifest_log.superblock.working.cluster,
|
|
532
|
-
.op = manifest_log.
|
|
627
|
+
.op = manifest_log.grid.acquire(manifest_log.grid_reservation.?),
|
|
533
628
|
.size = undefined,
|
|
534
629
|
.command = .block,
|
|
630
|
+
.operation = BlockType.manifest.operation(),
|
|
535
631
|
};
|
|
536
632
|
}
|
|
537
633
|
|
|
538
634
|
fn close_block(manifest_log: *ManifestLog) void {
|
|
539
|
-
|
|
635
|
+
assert(manifest_log.blocks.count == manifest_log.blocks_closed + 1);
|
|
540
636
|
|
|
637
|
+
const block: BlockPtr = manifest_log.blocks.tail().?;
|
|
541
638
|
const entry_count = manifest_log.entry_count;
|
|
542
639
|
assert(entry_count > 0);
|
|
543
|
-
assert(entry_count <= entry_count_max);
|
|
640
|
+
assert(entry_count <= Block.entry_count_max);
|
|
544
641
|
|
|
545
642
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
546
643
|
assert(header.cluster == manifest_log.superblock.working.cluster);
|
|
547
644
|
assert(header.op > 0);
|
|
548
|
-
header.size = block_size(entry_count);
|
|
549
645
|
assert(header.command == .block);
|
|
646
|
+
header.size = Block.size(entry_count);
|
|
550
647
|
|
|
551
648
|
// Zero unused labels:
|
|
552
|
-
mem.set(u8, mem.sliceAsBytes(labels(block)[entry_count..]), 0);
|
|
649
|
+
mem.set(u8, mem.sliceAsBytes(Block.labels(block)[entry_count..]), 0);
|
|
553
650
|
|
|
554
651
|
// Zero unused tables, and padding:
|
|
555
652
|
mem.set(u8, block[header.size..], 0);
|
|
@@ -558,21 +655,23 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
558
655
|
header.set_checksum();
|
|
559
656
|
|
|
560
657
|
verify_block(block, null, null);
|
|
561
|
-
assert(
|
|
658
|
+
assert(Block.entry_count(block) == entry_count);
|
|
562
659
|
|
|
563
660
|
log.debug("{}: close_block: checksum={} address={} entries={}", .{
|
|
564
661
|
manifest_log.tree_hash,
|
|
565
662
|
header.checksum,
|
|
566
|
-
|
|
663
|
+
Block.address(block),
|
|
567
664
|
entry_count,
|
|
568
665
|
});
|
|
569
666
|
|
|
570
667
|
manifest_log.blocks_closed += 1;
|
|
571
668
|
manifest_log.entry_count = 0;
|
|
669
|
+
assert(manifest_log.blocks.count == manifest_log.blocks_closed);
|
|
572
670
|
}
|
|
573
671
|
|
|
574
672
|
fn verify_block(block: BlockPtrConst, checksum: ?u128, address: ?u64) void {
|
|
575
673
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
674
|
+
assert(BlockType.from(header.operation) == .manifest);
|
|
576
675
|
|
|
577
676
|
if (config.verify) {
|
|
578
677
|
assert(header.valid_checksum());
|
|
@@ -581,63 +680,92 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
581
680
|
|
|
582
681
|
assert(checksum == null or header.checksum == checksum.?);
|
|
583
682
|
|
|
584
|
-
assert(
|
|
585
|
-
assert(address == null or
|
|
683
|
+
assert(Block.address(block) > 0);
|
|
684
|
+
assert(address == null or Block.address(block) == address.?);
|
|
586
685
|
|
|
587
|
-
const entry_count =
|
|
686
|
+
const entry_count = Block.entry_count(block);
|
|
588
687
|
assert(entry_count > 0);
|
|
589
688
|
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
fn ManifestLogBlockType(comptime Storage: type, comptime TableInfo: type) type {
|
|
693
|
+
return struct {
|
|
694
|
+
const Grid = GridType(Storage);
|
|
695
|
+
const BlockPtr = Grid.BlockPtr;
|
|
696
|
+
const BlockPtrConst = Grid.BlockPtrConst;
|
|
697
|
+
|
|
698
|
+
const block_body_size = config.block_size - @sizeOf(vsr.Header);
|
|
699
|
+
const entry_size = @sizeOf(Label) + @sizeOf(TableInfo);
|
|
700
|
+
const entry_count_max_unaligned = @divFloor(block_body_size, entry_size);
|
|
701
|
+
pub const entry_count_max = @divFloor(
|
|
702
|
+
entry_count_max_unaligned,
|
|
703
|
+
@alignOf(TableInfo),
|
|
704
|
+
) * @alignOf(TableInfo);
|
|
590
705
|
|
|
591
|
-
|
|
706
|
+
comptime {
|
|
707
|
+
assert(entry_count_max > 0);
|
|
708
|
+
assert((entry_count_max * @sizeOf(Label)) % @alignOf(TableInfo) == 0);
|
|
709
|
+
assert((entry_count_max * @sizeOf(TableInfo)) % @alignOf(TableInfo) == 0);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
pub const Label = packed struct {
|
|
713
|
+
level: u7,
|
|
714
|
+
event: enum(u1) { insert, remove },
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
pub fn address(block: BlockPtrConst) u64 {
|
|
592
718
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
593
719
|
assert(header.command == .block);
|
|
594
720
|
|
|
595
|
-
const
|
|
596
|
-
assert(
|
|
597
|
-
return
|
|
721
|
+
const block_address = header.op;
|
|
722
|
+
assert(block_address > 0);
|
|
723
|
+
return block_address;
|
|
598
724
|
}
|
|
599
725
|
|
|
600
|
-
fn
|
|
726
|
+
pub fn checksum(block: BlockPtrConst) u128 {
|
|
601
727
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
602
728
|
assert(header.command == .block);
|
|
603
729
|
|
|
604
730
|
return header.checksum;
|
|
605
731
|
}
|
|
606
732
|
|
|
607
|
-
fn
|
|
733
|
+
pub fn entry_count(block: BlockPtrConst) u32 {
|
|
608
734
|
const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
|
|
609
735
|
assert(header.command == .block);
|
|
610
736
|
|
|
611
737
|
const labels_size = entry_count_max * @sizeOf(Label);
|
|
612
738
|
const tables_size = header.size - @sizeOf(vsr.Header) - labels_size;
|
|
613
739
|
|
|
614
|
-
const
|
|
615
|
-
assert(
|
|
616
|
-
assert(
|
|
617
|
-
return
|
|
740
|
+
const entry_count_ = @intCast(u32, @divExact(tables_size, @sizeOf(TableInfo)));
|
|
741
|
+
assert(entry_count_ > 0);
|
|
742
|
+
assert(entry_count_ <= entry_count_max);
|
|
743
|
+
return entry_count_;
|
|
618
744
|
}
|
|
619
745
|
|
|
620
|
-
fn
|
|
621
|
-
assert(
|
|
622
|
-
assert(
|
|
746
|
+
pub fn size(entry_count_: u32) u32 {
|
|
747
|
+
assert(entry_count_ > 0);
|
|
748
|
+
assert(entry_count_ <= entry_count_max);
|
|
623
749
|
|
|
624
750
|
// Encode the smaller type first because this will be multiplied by entry_count_max.
|
|
625
751
|
const labels_size = entry_count_max * @sizeOf(Label);
|
|
626
|
-
|
|
752
|
+
assert(labels_size == labels_size_max);
|
|
753
|
+
assert((@sizeOf(vsr.Header) + labels_size) % @alignOf(TableInfo) == 0);
|
|
754
|
+
const tables_size = entry_count_ * @sizeOf(TableInfo);
|
|
627
755
|
|
|
628
756
|
return @sizeOf(vsr.Header) + labels_size + tables_size;
|
|
629
757
|
}
|
|
630
758
|
|
|
631
759
|
const labels_size_max = entry_count_max * @sizeOf(Label);
|
|
632
760
|
|
|
633
|
-
fn labels(block: BlockPtr) *[entry_count_max]Label {
|
|
761
|
+
pub fn labels(block: BlockPtr) *[entry_count_max]Label {
|
|
634
762
|
return mem.bytesAsSlice(
|
|
635
763
|
Label,
|
|
636
764
|
block[@sizeOf(vsr.Header)..][0..labels_size_max],
|
|
637
765
|
)[0..entry_count_max];
|
|
638
766
|
}
|
|
639
767
|
|
|
640
|
-
fn labels_const(block: BlockPtrConst) *const [entry_count_max]Label {
|
|
768
|
+
pub fn labels_const(block: BlockPtrConst) *const [entry_count_max]Label {
|
|
641
769
|
return mem.bytesAsSlice(
|
|
642
770
|
Label,
|
|
643
771
|
block[@sizeOf(vsr.Header)..][0..labels_size_max],
|
|
@@ -646,259 +774,18 @@ pub fn ManifestLogType(comptime Storage: type, comptime TableInfo: type) type {
|
|
|
646
774
|
|
|
647
775
|
const tables_size_max = entry_count_max * @sizeOf(TableInfo);
|
|
648
776
|
|
|
649
|
-
fn tables(block: BlockPtr) *[entry_count_max]TableInfo {
|
|
777
|
+
pub fn tables(block: BlockPtr) *[entry_count_max]TableInfo {
|
|
650
778
|
return mem.bytesAsSlice(
|
|
651
779
|
TableInfo,
|
|
652
|
-
block[@sizeOf(vsr.Header) +
|
|
780
|
+
block[@sizeOf(vsr.Header) + labels_size_max ..][0..tables_size_max],
|
|
653
781
|
)[0..entry_count_max];
|
|
654
782
|
}
|
|
655
783
|
|
|
656
|
-
fn tables_const(block: BlockPtrConst) *const [entry_count_max]TableInfo {
|
|
784
|
+
pub fn tables_const(block: BlockPtrConst) *const [entry_count_max]TableInfo {
|
|
657
785
|
return mem.bytesAsSlice(
|
|
658
786
|
TableInfo,
|
|
659
|
-
block[@sizeOf(vsr.Header) +
|
|
787
|
+
block[@sizeOf(vsr.Header) + labels_size_max ..][0..tables_size_max],
|
|
660
788
|
)[0..entry_count_max];
|
|
661
789
|
}
|
|
662
790
|
};
|
|
663
791
|
}
|
|
664
|
-
|
|
665
|
-
// TODO This is a manual runner to be replaced with a fuzz test.
|
|
666
|
-
fn ManifestLogTestType(
|
|
667
|
-
comptime Storage: type,
|
|
668
|
-
comptime TableInfo: type,
|
|
669
|
-
) type {
|
|
670
|
-
return struct {
|
|
671
|
-
const ManifestLogTest = @This();
|
|
672
|
-
const ManifestLog = ManifestLogType(Storage, TableInfo);
|
|
673
|
-
|
|
674
|
-
const SuperBlock = SuperBlockType(Storage);
|
|
675
|
-
const Grid = GridType(Storage);
|
|
676
|
-
|
|
677
|
-
superblock: *SuperBlock,
|
|
678
|
-
superblock_context: SuperBlock.Context = undefined,
|
|
679
|
-
manifest_log: ManifestLog,
|
|
680
|
-
pending: usize = 0,
|
|
681
|
-
|
|
682
|
-
fn init(allocator: mem.Allocator, grid: *Grid) !ManifestLogTest {
|
|
683
|
-
const tree_hash: u128 = std.math.maxInt(u128);
|
|
684
|
-
|
|
685
|
-
var manifest_log = try ManifestLog.init(allocator, grid, tree_hash);
|
|
686
|
-
errdefer manifest_log.deinit(allocator);
|
|
687
|
-
|
|
688
|
-
return ManifestLogTest{
|
|
689
|
-
.superblock = grid.superblock,
|
|
690
|
-
.manifest_log = manifest_log,
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
fn deinit(t: *ManifestLogTest, allocator: mem.Allocator) void {
|
|
695
|
-
t.manifest_log.deinit(allocator);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
fn format_superblock(t: *ManifestLogTest) void {
|
|
699
|
-
t.pending += 1;
|
|
700
|
-
t.superblock.format(format_superblock_callback, &t.superblock_context, .{
|
|
701
|
-
.cluster = 10,
|
|
702
|
-
.replica = 0,
|
|
703
|
-
.size_max = 512 * 1024 * 1024,
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
fn format_superblock_callback(context: *SuperBlock.Context) void {
|
|
708
|
-
const t = @fieldParentPtr(ManifestLogTest, "superblock_context", context);
|
|
709
|
-
t.pending -= 1;
|
|
710
|
-
t.open_superblock();
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
fn open_superblock(t: *ManifestLogTest) void {
|
|
714
|
-
t.pending += 1;
|
|
715
|
-
t.superblock.open(open_superblock_callback, &t.superblock_context);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
fn open_superblock_callback(context: *SuperBlock.Context) void {
|
|
719
|
-
const t = @fieldParentPtr(ManifestLogTest, "superblock_context", context);
|
|
720
|
-
t.pending -= 1;
|
|
721
|
-
|
|
722
|
-
t.open();
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
fn open(t: *ManifestLogTest) void {
|
|
726
|
-
t.pending += 1;
|
|
727
|
-
t.manifest_log.open(open_event, open_callback);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
fn open_event(manifest_log: *ManifestLog, level: u7, table: *const TableInfo) void {
|
|
731
|
-
log.debug(
|
|
732
|
-
"{}: open_event: level={} checksum={} address={} flags={} snapshot={}..{}",
|
|
733
|
-
.{
|
|
734
|
-
manifest_log.tree_hash,
|
|
735
|
-
level,
|
|
736
|
-
table.checksum,
|
|
737
|
-
table.address,
|
|
738
|
-
table.flags,
|
|
739
|
-
table.snapshot_min,
|
|
740
|
-
table.snapshot_max,
|
|
741
|
-
},
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
fn open_callback(manifest_log: *ManifestLog) void {
|
|
746
|
-
const t = @fieldParentPtr(ManifestLogTest, "manifest_log", manifest_log);
|
|
747
|
-
t.pending -= 1;
|
|
748
|
-
|
|
749
|
-
t.manifest_log.insert(2, &TableInfo{
|
|
750
|
-
.checksum = 123,
|
|
751
|
-
.address = 7,
|
|
752
|
-
.flags = 0,
|
|
753
|
-
.snapshot_min = 42,
|
|
754
|
-
.key_min = 50,
|
|
755
|
-
.key_max = 100,
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
t.flush();
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
fn flush(t: *ManifestLogTest) void {
|
|
762
|
-
t.pending += 1;
|
|
763
|
-
t.manifest_log.flush(flush_callback);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
fn flush_callback(manifest_log: *ManifestLog) void {
|
|
767
|
-
const t = @fieldParentPtr(ManifestLogTest, "manifest_log", manifest_log);
|
|
768
|
-
t.pending -= 1;
|
|
769
|
-
t.checkpoint();
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
fn checkpoint(t: *ManifestLogTest) void {
|
|
773
|
-
t.pending += 1;
|
|
774
|
-
t.manifest_log.checkpoint(checkpoint_callback);
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
fn checkpoint_callback(manifest_log: *ManifestLog) void {
|
|
778
|
-
const t = @fieldParentPtr(ManifestLogTest, "manifest_log", manifest_log);
|
|
779
|
-
t.pending -= 1;
|
|
780
|
-
|
|
781
|
-
t.manifest_log.insert(2, &TableInfo{
|
|
782
|
-
.checksum = 123,
|
|
783
|
-
.address = 7,
|
|
784
|
-
.flags = 0,
|
|
785
|
-
.snapshot_min = 42,
|
|
786
|
-
.snapshot_max = 50,
|
|
787
|
-
.key_min = 50,
|
|
788
|
-
.key_max = 100,
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
t.manifest_log.remove(2, &TableInfo{
|
|
792
|
-
.checksum = 123,
|
|
793
|
-
.address = 7,
|
|
794
|
-
.flags = 0,
|
|
795
|
-
.snapshot_min = 42,
|
|
796
|
-
.snapshot_max = 50,
|
|
797
|
-
.key_min = 50,
|
|
798
|
-
.key_max = 100,
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
t.checkpoint_again();
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
fn checkpoint_again(t: *ManifestLogTest) void {
|
|
805
|
-
t.pending += 1;
|
|
806
|
-
t.manifest_log.checkpoint(checkpoint_again_callback);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
fn checkpoint_again_callback(manifest_log: *ManifestLog) void {
|
|
810
|
-
const t = @fieldParentPtr(ManifestLogTest, "manifest_log", manifest_log);
|
|
811
|
-
t.pending -= 1;
|
|
812
|
-
t.compact();
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
fn compact(t: *ManifestLogTest) void {
|
|
816
|
-
t.pending += 1;
|
|
817
|
-
t.manifest_log.compact(compact_callback);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
fn compact_callback(manifest_log: *ManifestLog) void {
|
|
821
|
-
const t = @fieldParentPtr(ManifestLogTest, "manifest_log", manifest_log);
|
|
822
|
-
t.pending -= 1;
|
|
823
|
-
|
|
824
|
-
const tree = t.manifest_log.tree_hash;
|
|
825
|
-
if (t.manifest_log.superblock.manifest.oldest_block_queued_for_compaction(tree)) |_| {
|
|
826
|
-
t.compact();
|
|
827
|
-
} else {
|
|
828
|
-
t.checkpoint_superblock();
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
fn checkpoint_superblock(t: *ManifestLogTest) void {
|
|
833
|
-
t.pending += 1;
|
|
834
|
-
t.superblock.checkpoint(checkpoint_superblock_callback, &t.superblock_context);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
fn checkpoint_superblock_callback(context: *SuperBlock.Context) void {
|
|
838
|
-
const t = @fieldParentPtr(ManifestLogTest, "superblock_context", context);
|
|
839
|
-
t.pending -= 1;
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
pub fn main() !void {
|
|
845
|
-
const testing = std.testing;
|
|
846
|
-
const allocator = testing.allocator;
|
|
847
|
-
|
|
848
|
-
testing.log_level = .debug;
|
|
849
|
-
|
|
850
|
-
const os = std.os;
|
|
851
|
-
const IO = @import("../io.zig").IO;
|
|
852
|
-
const Storage = @import("../storage.zig").Storage;
|
|
853
|
-
const SuperBlock = SuperBlockType(Storage);
|
|
854
|
-
const Grid = @import("grid.zig").GridType(Storage);
|
|
855
|
-
|
|
856
|
-
const dir_path = ".";
|
|
857
|
-
const dir_fd = os.openZ(dir_path, os.O.CLOEXEC | os.O.RDONLY, 0) catch |err| {
|
|
858
|
-
std.debug.print("failed to open directory '{s}': {}", .{ dir_path, err });
|
|
859
|
-
return;
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
const size_max = 2 * 1024 * 1024 * 1024;
|
|
863
|
-
const storage_fd = try IO.open_file(dir_fd, "test_manifest_log", size_max, true);
|
|
864
|
-
defer std.fs.cwd().deleteFile("test_manifest_log") catch {};
|
|
865
|
-
|
|
866
|
-
var io = try IO.init(128, 0);
|
|
867
|
-
defer io.deinit();
|
|
868
|
-
|
|
869
|
-
var storage = try Storage.init(&io, storage_fd);
|
|
870
|
-
defer storage.deinit();
|
|
871
|
-
|
|
872
|
-
var superblock = try SuperBlock.init(allocator, &storage);
|
|
873
|
-
defer superblock.deinit(allocator);
|
|
874
|
-
|
|
875
|
-
var grid = try Grid.init(allocator, &superblock);
|
|
876
|
-
defer grid.deinit(allocator);
|
|
877
|
-
|
|
878
|
-
const TableInfo = extern struct {
|
|
879
|
-
checksum: u128,
|
|
880
|
-
address: u64,
|
|
881
|
-
flags: u64 = 0,
|
|
882
|
-
|
|
883
|
-
/// The minimum snapshot that can see this table (with exclusive bounds).
|
|
884
|
-
/// This value is set to the current snapshot tick on table creation.
|
|
885
|
-
snapshot_min: u64,
|
|
886
|
-
|
|
887
|
-
/// The maximum snapshot that can see this table (with exclusive bounds).
|
|
888
|
-
/// This value is set to the current snapshot tick on table deletion.
|
|
889
|
-
snapshot_max: u64 = math.maxInt(u64),
|
|
890
|
-
|
|
891
|
-
key_min: u128,
|
|
892
|
-
key_max: u128,
|
|
893
|
-
};
|
|
894
|
-
assert(@sizeOf(TableInfo) == 48 + 16 * 2);
|
|
895
|
-
assert(@alignOf(TableInfo) == 16);
|
|
896
|
-
|
|
897
|
-
const ManifestLogTest = ManifestLogTestType(Storage, TableInfo);
|
|
898
|
-
|
|
899
|
-
var t = try ManifestLogTest.init(allocator, &grid);
|
|
900
|
-
defer t.deinit(allocator);
|
|
901
|
-
|
|
902
|
-
t.format_superblock();
|
|
903
|
-
while (t.pending > 0) try io.run_for_ns(100);
|
|
904
|
-
}
|