tigerbeetle-node 0.11.2 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.client.node.sha256 +1 -1
- package/package.json +1 -1
- package/src/tigerbeetle/src/config.zig +6 -0
- package/src/tigerbeetle/src/ewah.zig +18 -29
- package/src/tigerbeetle/src/ewah_fuzz.zig +128 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +49 -1
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +5 -0
- package/src/tigerbeetle/src/lsm/tree.zig +19 -2
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +6 -1
- package/src/tigerbeetle/src/main.zig +4 -0
- package/src/tigerbeetle/src/simulator.zig +1 -1
- package/src/tigerbeetle/src/state_machine.zig +45 -0
- package/src/tigerbeetle/src/test/state_checker.zig +1 -1
- package/src/tigerbeetle/src/test/storage.zig +9 -0
- package/src/tigerbeetle/src/tracer.zig +319 -0
- package/src/tigerbeetle/src/unit_tests.zig +1 -0
- package/src/tigerbeetle/src/vsr/journal.zig +40 -109
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +109 -0
- package/src/tigerbeetle/src/vsr/replica.zig +45 -156
- package/src/tigerbeetle/src/vsr/replica_format.zig +216 -0
- package/src/tigerbeetle/src/vsr.zig +1 -1
package/dist/.client.node.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cd58e7f1a46beef96d550cfb8423c01542980ff7f9832127e73ad2e5cd0e3a63 dist/client.node
|
package/package.json
CHANGED
|
@@ -2,6 +2,8 @@ const std = @import("std");
|
|
|
2
2
|
const assert = std.debug.assert;
|
|
3
3
|
const vsr = @import("vsr.zig");
|
|
4
4
|
|
|
5
|
+
const build_options = @import("tigerbeetle_build_options");
|
|
6
|
+
|
|
5
7
|
const Environment = enum {
|
|
6
8
|
development,
|
|
7
9
|
production,
|
|
@@ -330,6 +332,10 @@ pub const verify = true;
|
|
|
330
332
|
pub const journal_size_headers = journal_slot_count * @sizeOf(vsr.Header);
|
|
331
333
|
pub const journal_size_prepares = journal_slot_count * message_size_max;
|
|
332
334
|
|
|
335
|
+
// Which backend to use for ./tracer.zig.
|
|
336
|
+
// Default is `.none`.
|
|
337
|
+
pub const tracer_backend = build_options.tracer_backend;
|
|
338
|
+
|
|
333
339
|
// TODO Move these into a separate `config_valid.zig` which we import here:
|
|
334
340
|
comptime {
|
|
335
341
|
// vsr.parse_address assumes that config.address/config.port are valid.
|
|
@@ -160,6 +160,24 @@ pub fn ewah(comptime Word: type) type {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
test "ewah encode→decode cycle" {
|
|
164
|
+
const fuzz = @import("./ewah_fuzz.zig");
|
|
165
|
+
var prng = std.rand.DefaultPrng.init(123);
|
|
166
|
+
|
|
167
|
+
inline for (.{ u8, u16, u32, u64, usize }) |Word| {
|
|
168
|
+
var decoded: [4096]Word = undefined;
|
|
169
|
+
|
|
170
|
+
std.mem.set(Word, &decoded, 0);
|
|
171
|
+
try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
|
|
172
|
+
|
|
173
|
+
std.mem.set(Word, &decoded, std.math.maxInt(Word));
|
|
174
|
+
try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
|
|
175
|
+
|
|
176
|
+
prng.random().bytes(std.mem.asBytes(&decoded));
|
|
177
|
+
try fuzz.fuzz_encode_decode(Word, std.testing.allocator, &decoded);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
163
181
|
test "ewah Word=u8 decode→encode→decode" {
|
|
164
182
|
try test_decode_with_word(u8);
|
|
165
183
|
|
|
@@ -179,35 +197,6 @@ test "ewah Word=u8 decode→encode→decode" {
|
|
|
179
197
|
}
|
|
180
198
|
}
|
|
181
199
|
|
|
182
|
-
test "ewah Word=u8 encode→decode→encode" {
|
|
183
|
-
const codec = ewah(u8);
|
|
184
|
-
|
|
185
|
-
var seed: u64 = undefined;
|
|
186
|
-
try std.os.getrandom(mem.asBytes(&seed));
|
|
187
|
-
|
|
188
|
-
var prng = std.rand.DefaultPrng.init(seed);
|
|
189
|
-
const random = prng.random();
|
|
190
|
-
|
|
191
|
-
var decoded_expect: [4096]u8 = undefined;
|
|
192
|
-
var decoded_actual: [4096]u8 = undefined;
|
|
193
|
-
|
|
194
|
-
const encoded_actual = try std.testing.allocator.alignedAlloc(
|
|
195
|
-
u8,
|
|
196
|
-
@alignOf(u8),
|
|
197
|
-
codec.encode_size_max(decoded_expect.len),
|
|
198
|
-
);
|
|
199
|
-
defer std.testing.allocator.free(encoded_actual);
|
|
200
|
-
|
|
201
|
-
var t: usize = 0;
|
|
202
|
-
while (t < 100) : (t += 1) {
|
|
203
|
-
random.bytes(decoded_expect[0..]);
|
|
204
|
-
_ = codec.encode(decoded_expect[0..], encoded_actual);
|
|
205
|
-
const decoded_actual_length = codec.decode(encoded_actual[0..], decoded_actual[0..]);
|
|
206
|
-
try std.testing.expectEqual(decoded_expect.len, decoded_actual_length);
|
|
207
|
-
try std.testing.expectEqual(decoded_expect, decoded_actual);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
200
|
test "ewah Word=u16" {
|
|
212
201
|
try test_decode_with_word(u16);
|
|
213
202
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
//! Fuzz EWAH encode/decode cycle.
|
|
2
|
+
const std = @import("std");
|
|
3
|
+
const assert = std.debug.assert;
|
|
4
|
+
const log = std.log.scoped(.fuzz_ewah);
|
|
5
|
+
|
|
6
|
+
const ewah = @import("./ewah.zig");
|
|
7
|
+
const fuzz = @import("./test/fuzz.zig");
|
|
8
|
+
|
|
9
|
+
pub fn main() !void {
|
|
10
|
+
const allocator = std.testing.allocator;
|
|
11
|
+
const args = try fuzz.parse_fuzz_args(allocator);
|
|
12
|
+
|
|
13
|
+
inline for (.{ u8, u16, u32, u64, usize }) |Word| {
|
|
14
|
+
var prng = std.rand.DefaultPrng.init(args.seed);
|
|
15
|
+
const random = prng.random();
|
|
16
|
+
|
|
17
|
+
const decoded_size_max = @divExact(1024 * 1024, @sizeOf(Word));
|
|
18
|
+
const decoded_size = 1 + random.uintLessThan(usize, decoded_size_max);
|
|
19
|
+
const decoded = try allocator.alloc(Word, decoded_size);
|
|
20
|
+
defer allocator.free(decoded);
|
|
21
|
+
|
|
22
|
+
const decoded_bits_total = decoded_size * @bitSizeOf(Word);
|
|
23
|
+
const decoded_bits = random.uintLessThan(usize, decoded_bits_total);
|
|
24
|
+
generate_bits(random, std.mem.sliceAsBytes(decoded[0..decoded_size]), decoded_bits);
|
|
25
|
+
|
|
26
|
+
var context = try ContextType(Word).init(allocator, decoded.len);
|
|
27
|
+
defer context.deinit(allocator);
|
|
28
|
+
|
|
29
|
+
const encoded_size = try context.test_encode_decode(decoded);
|
|
30
|
+
|
|
31
|
+
log.info("word={} decoded={} encoded={} compression_ratio={d:.2} set={d:.2}", .{
|
|
32
|
+
Word,
|
|
33
|
+
decoded_size,
|
|
34
|
+
encoded_size,
|
|
35
|
+
@intToFloat(f64, decoded_size) / @intToFloat(f64, encoded_size),
|
|
36
|
+
@intToFloat(f64, decoded_bits) / @intToFloat(f64, decoded_bits_total),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn fuzz_encode_decode(
|
|
42
|
+
comptime Word: type,
|
|
43
|
+
allocator: std.mem.Allocator,
|
|
44
|
+
decoded: []const Word,
|
|
45
|
+
) !void {
|
|
46
|
+
var context = try ContextType(Word).init(allocator, decoded.len);
|
|
47
|
+
defer context.deinit(allocator);
|
|
48
|
+
|
|
49
|
+
_ = try context.test_encode_decode(decoded);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Modify `data` such that it has exactly `bits_set_total` randomly-chosen bits set,
|
|
53
|
+
/// with the remaining bits unset.
|
|
54
|
+
fn generate_bits(random: std.rand.Random, data: []u8, bits_set_total: usize) void {
|
|
55
|
+
const bits_total = data.len * @bitSizeOf(u8);
|
|
56
|
+
assert(bits_set_total <= bits_total);
|
|
57
|
+
|
|
58
|
+
// Start off full or empty to save some work.
|
|
59
|
+
const init_empty = bits_set_total < @divExact(bits_total, 2);
|
|
60
|
+
std.mem.set(u8, data, if (init_empty) @as(u8, 0) else std.math.maxInt(u8));
|
|
61
|
+
|
|
62
|
+
var bits_set = if (init_empty) 0 else bits_total;
|
|
63
|
+
while (bits_set != bits_set_total) {
|
|
64
|
+
const bit = random.uintLessThan(usize, bits_total);
|
|
65
|
+
const word = @divFloor(bit, @bitSizeOf(u8));
|
|
66
|
+
const mask = @as(u8, 1) << @intCast(std.math.Log2Int(u8), bit % @bitSizeOf(u8));
|
|
67
|
+
|
|
68
|
+
if (init_empty) {
|
|
69
|
+
if (data[word] & mask != 0) continue;
|
|
70
|
+
data[word] |= mask;
|
|
71
|
+
bits_set += 1;
|
|
72
|
+
} else {
|
|
73
|
+
if (data[word] & mask == 0) continue;
|
|
74
|
+
data[word] &= ~mask;
|
|
75
|
+
bits_set -= 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn ContextType(comptime Word: type) type {
|
|
81
|
+
return struct {
|
|
82
|
+
const Self = @This();
|
|
83
|
+
const Codec = ewah.ewah(Word);
|
|
84
|
+
|
|
85
|
+
decoded_actual: []Word,
|
|
86
|
+
encoded_actual: []align(@alignOf(Word)) u8,
|
|
87
|
+
|
|
88
|
+
fn init(allocator: std.mem.Allocator, size_max: usize) !Self {
|
|
89
|
+
const decoded_actual = try allocator.alloc(Word, size_max);
|
|
90
|
+
errdefer allocator.free(decoded_actual);
|
|
91
|
+
|
|
92
|
+
const encoded_actual = try allocator.alignedAlloc(
|
|
93
|
+
u8,
|
|
94
|
+
@alignOf(Word),
|
|
95
|
+
Codec.encode_size_max(size_max),
|
|
96
|
+
);
|
|
97
|
+
errdefer allocator.free(encoded_actual);
|
|
98
|
+
|
|
99
|
+
return Self{
|
|
100
|
+
.decoded_actual = decoded_actual,
|
|
101
|
+
.encoded_actual = encoded_actual,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn deinit(context: *Self, allocator: std.mem.Allocator) void {
|
|
106
|
+
allocator.free(context.decoded_actual);
|
|
107
|
+
allocator.free(context.encoded_actual);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn test_encode_decode(context: Self, decoded_expect: []const Word) !usize {
|
|
111
|
+
assert(decoded_expect.len > 0);
|
|
112
|
+
|
|
113
|
+
const encoded_size = Codec.encode(decoded_expect, context.encoded_actual);
|
|
114
|
+
const decoded_actual_size = Codec.decode(
|
|
115
|
+
context.encoded_actual[0..encoded_size],
|
|
116
|
+
context.decoded_actual[0..],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
try std.testing.expectEqual(decoded_expect.len, decoded_actual_size);
|
|
120
|
+
try std.testing.expectEqualSlices(
|
|
121
|
+
Word,
|
|
122
|
+
decoded_expect,
|
|
123
|
+
context.decoded_actual[0..decoded_actual_size],
|
|
124
|
+
);
|
|
125
|
+
return encoded_size;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -36,6 +36,8 @@ const math = std.math;
|
|
|
36
36
|
const assert = std.debug.assert;
|
|
37
37
|
|
|
38
38
|
const log = std.log.scoped(.compaction);
|
|
39
|
+
const tracer = @import("../tracer.zig");
|
|
40
|
+
|
|
39
41
|
const config = @import("../config.zig");
|
|
40
42
|
|
|
41
43
|
const GridType = @import("grid.zig").GridType;
|
|
@@ -120,6 +122,8 @@ pub fn CompactionType(
|
|
|
120
122
|
done,
|
|
121
123
|
};
|
|
122
124
|
|
|
125
|
+
tree_name: []const u8,
|
|
126
|
+
|
|
123
127
|
grid: *Grid,
|
|
124
128
|
grid_reservation: Grid.Reservation,
|
|
125
129
|
range: Manifest.CompactionRange,
|
|
@@ -155,7 +159,9 @@ pub fn CompactionType(
|
|
|
155
159
|
|
|
156
160
|
tables_output_count: usize = 0,
|
|
157
161
|
|
|
158
|
-
|
|
162
|
+
tracer_slot: ?tracer.SpanStart = null,
|
|
163
|
+
|
|
164
|
+
pub fn init(allocator: mem.Allocator, tree_name: []const u8) !Compaction {
|
|
159
165
|
var iterator_a = try IteratorA.init(allocator);
|
|
160
166
|
errdefer iterator_a.deinit(allocator);
|
|
161
167
|
|
|
@@ -166,6 +172,8 @@ pub fn CompactionType(
|
|
|
166
172
|
errdefer table_builder.deinit(allocator);
|
|
167
173
|
|
|
168
174
|
return Compaction{
|
|
175
|
+
.tree_name = tree_name,
|
|
176
|
+
|
|
169
177
|
// Assigned by start()
|
|
170
178
|
.grid = undefined,
|
|
171
179
|
.grid_reservation = undefined,
|
|
@@ -216,6 +224,7 @@ pub fn CompactionType(
|
|
|
216
224
|
assert(compaction.callback == null);
|
|
217
225
|
assert(compaction.io_pending == 0);
|
|
218
226
|
assert(!compaction.merge_done and compaction.merge_iterator == null);
|
|
227
|
+
assert(compaction.tracer_slot == null);
|
|
219
228
|
|
|
220
229
|
assert(op_min % @divExact(config.lsm_batch_multiple, 2) == 0);
|
|
221
230
|
assert(range.table_count > 0);
|
|
@@ -230,6 +239,8 @@ pub fn CompactionType(
|
|
|
230
239
|
assert(drop_tombstones or level_b < config.lsm_levels - 1);
|
|
231
240
|
|
|
232
241
|
compaction.* = .{
|
|
242
|
+
.tree_name = compaction.tree_name,
|
|
243
|
+
|
|
233
244
|
.grid = grid,
|
|
234
245
|
// Reserve enough blocks to write our output tables in the worst case, where:
|
|
235
246
|
// - no tombstones are dropped,
|
|
@@ -337,6 +348,15 @@ pub fn CompactionType(
|
|
|
337
348
|
|
|
338
349
|
compaction.callback = callback;
|
|
339
350
|
|
|
351
|
+
tracer.start(
|
|
352
|
+
&compaction.tracer_slot,
|
|
353
|
+
.{ .tree = .{ .tree_name = compaction.tree_name } },
|
|
354
|
+
.{ .tree_compaction_tick = .{
|
|
355
|
+
.tree_name = compaction.tree_name,
|
|
356
|
+
.level_b = compaction.level_b,
|
|
357
|
+
} },
|
|
358
|
+
);
|
|
359
|
+
|
|
340
360
|
// Generate fake IO to make sure io_pending doesn't reach zero multiple times from
|
|
341
361
|
// IO being completed inline down below.
|
|
342
362
|
// The fake IO is immediately resolved and triggers the cpu_merge_start if all
|
|
@@ -405,6 +425,16 @@ pub fn CompactionType(
|
|
|
405
425
|
assert(compaction.io_pending == 0);
|
|
406
426
|
assert(!compaction.merge_done);
|
|
407
427
|
|
|
428
|
+
var tracer_slot: ?tracer.SpanStart = null;
|
|
429
|
+
tracer.start(
|
|
430
|
+
&tracer_slot,
|
|
431
|
+
.{ .tree = .{ .tree_name = compaction.tree_name } },
|
|
432
|
+
.{ .tree_compaction_merge = .{
|
|
433
|
+
.tree_name = compaction.tree_name,
|
|
434
|
+
.level_b = compaction.level_b,
|
|
435
|
+
} },
|
|
436
|
+
);
|
|
437
|
+
|
|
408
438
|
// Create the merge iterator only when we can peek() from the read iterators.
|
|
409
439
|
// This happens after IO for the first reads complete.
|
|
410
440
|
if (compaction.merge_iterator == null) {
|
|
@@ -422,6 +452,23 @@ pub fn CompactionType(
|
|
|
422
452
|
compaction.cpu_merge_finish();
|
|
423
453
|
}
|
|
424
454
|
|
|
455
|
+
tracer.end(
|
|
456
|
+
&tracer_slot,
|
|
457
|
+
.{ .tree = .{ .tree_name = compaction.tree_name } },
|
|
458
|
+
.{ .tree_compaction_merge = .{
|
|
459
|
+
.tree_name = compaction.tree_name,
|
|
460
|
+
.level_b = compaction.level_b,
|
|
461
|
+
} },
|
|
462
|
+
);
|
|
463
|
+
tracer.end(
|
|
464
|
+
&compaction.tracer_slot,
|
|
465
|
+
.{ .tree = .{ .tree_name = compaction.tree_name } },
|
|
466
|
+
.{ .tree_compaction_tick = .{
|
|
467
|
+
.tree_name = compaction.tree_name,
|
|
468
|
+
.level_b = compaction.level_b,
|
|
469
|
+
} },
|
|
470
|
+
);
|
|
471
|
+
|
|
425
472
|
// TODO Implement pacing here by deciding if we should do another compact_tick()
|
|
426
473
|
// instead of invoking the callback, using compaction.range.table_count as the heuristic.
|
|
427
474
|
|
|
@@ -560,6 +607,7 @@ pub fn CompactionType(
|
|
|
560
607
|
assert(compaction.callback == null);
|
|
561
608
|
assert(compaction.io_pending == 0);
|
|
562
609
|
assert(compaction.merge_done);
|
|
610
|
+
assert(compaction.tracer_slot == null);
|
|
563
611
|
|
|
564
612
|
// TODO(Beat Pacing) This should really be where the compaction callback is invoked,
|
|
565
613
|
// but currently that can occur multiple times per beat.
|
|
@@ -6,7 +6,9 @@ const assert = std.debug.assert;
|
|
|
6
6
|
const config = @import("../config.zig");
|
|
7
7
|
const fuzz = @import("../test/fuzz.zig");
|
|
8
8
|
const vsr = @import("../vsr.zig");
|
|
9
|
+
|
|
9
10
|
const log = std.log.scoped(.lsm_forest_fuzz);
|
|
11
|
+
const tracer = @import("../tracer.zig");
|
|
10
12
|
|
|
11
13
|
const MessagePool = @import("../message_pool.zig").MessagePool;
|
|
12
14
|
const Transfer = @import("../tigerbeetle.zig").Transfer;
|
|
@@ -395,6 +397,9 @@ pub fn generate_fuzz_ops(random: std.rand.Random) ![]const FuzzOp {
|
|
|
395
397
|
}
|
|
396
398
|
|
|
397
399
|
pub fn main() !void {
|
|
400
|
+
try tracer.init(allocator);
|
|
401
|
+
defer tracer.deinit(allocator);
|
|
402
|
+
|
|
398
403
|
const fuzz_args = try fuzz.parse_fuzz_args(allocator);
|
|
399
404
|
var rng = std.rand.DefaultPrng.init(fuzz_args.seed);
|
|
400
405
|
const random = rng.random();
|
|
@@ -7,6 +7,7 @@ const mem = std.mem;
|
|
|
7
7
|
const os = std.os;
|
|
8
8
|
|
|
9
9
|
const log = std.log.scoped(.tree);
|
|
10
|
+
const tracer = @import("../tracer.zig");
|
|
10
11
|
|
|
11
12
|
const config = @import("../config.zig");
|
|
12
13
|
const div_ceil = @import("../util.zig").div_ceil;
|
|
@@ -144,6 +145,8 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
|
|
|
144
145
|
checkpoint_callback: ?fn (*Tree) void,
|
|
145
146
|
open_callback: ?fn (*Tree) void,
|
|
146
147
|
|
|
148
|
+
tracer_slot: ?tracer.SpanStart = null,
|
|
149
|
+
|
|
147
150
|
pub const Options = struct {
|
|
148
151
|
/// The maximum number of keys that may be committed per batch.
|
|
149
152
|
///
|
|
@@ -188,13 +191,13 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
|
|
|
188
191
|
var manifest = try Manifest.init(allocator, node_pool, grid, tree_hash);
|
|
189
192
|
errdefer manifest.deinit(allocator);
|
|
190
193
|
|
|
191
|
-
var compaction_table_immutable = try CompactionTableImmutable.init(allocator);
|
|
194
|
+
var compaction_table_immutable = try CompactionTableImmutable.init(allocator, tree_name);
|
|
192
195
|
errdefer compaction_table_immutable.deinit(allocator);
|
|
193
196
|
|
|
194
197
|
var compaction_table: [@divFloor(config.lsm_levels, 2)]CompactionTable = undefined;
|
|
195
198
|
for (compaction_table) |*compaction, i| {
|
|
196
199
|
errdefer for (compaction_table[0..i]) |*c| c.deinit(allocator);
|
|
197
|
-
compaction.* = try CompactionTable.init(allocator);
|
|
200
|
+
compaction.* = try CompactionTable.init(allocator, tree_name);
|
|
198
201
|
}
|
|
199
202
|
errdefer for (compaction_table) |*c| c.deinit(allocator);
|
|
200
203
|
|
|
@@ -221,6 +224,8 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
|
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
pub fn deinit(tree: *Tree, allocator: mem.Allocator) void {
|
|
227
|
+
assert(tree.tracer_slot == null);
|
|
228
|
+
|
|
224
229
|
tree.compaction_table_immutable.deinit(allocator);
|
|
225
230
|
for (tree.compaction_table) |*compaction| compaction.deinit(allocator);
|
|
226
231
|
|
|
@@ -548,6 +553,12 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
|
|
|
548
553
|
assert(tree.compaction_io_pending == 0);
|
|
549
554
|
assert(tree.compaction_callback == null);
|
|
550
555
|
|
|
556
|
+
tracer.start(
|
|
557
|
+
&tree.tracer_slot,
|
|
558
|
+
.{ .tree = .{ .tree_name = tree_name } },
|
|
559
|
+
.{ .tree_compaction_beat = .{ .tree_name = tree_name } },
|
|
560
|
+
);
|
|
561
|
+
|
|
551
562
|
tree.compaction_callback = callback;
|
|
552
563
|
|
|
553
564
|
const compaction_beat = tree.compaction_op % config.lsm_batch_multiple;
|
|
@@ -917,6 +928,12 @@ pub fn TreeType(comptime TreeTable: type, comptime Storage: type, comptime tree_
|
|
|
917
928
|
assert(tree.compaction_io_pending == 0);
|
|
918
929
|
assert(tree.table_mutable.can_commit_batch(tree.options.commit_entries_max));
|
|
919
930
|
|
|
931
|
+
tracer.end(
|
|
932
|
+
&tree.tracer_slot,
|
|
933
|
+
.{ .tree = .{ .tree_name = tree_name } },
|
|
934
|
+
.{ .tree_compaction_beat = .{ .tree_name = tree_name } },
|
|
935
|
+
);
|
|
936
|
+
|
|
920
937
|
// Invoke the compact() callback after the manifest compacts at the end of the beat.
|
|
921
938
|
const callback = tree.compaction_callback.?;
|
|
922
939
|
tree.compaction_callback = null;
|
|
@@ -6,7 +6,9 @@ const assert = std.debug.assert;
|
|
|
6
6
|
const config = @import("../config.zig");
|
|
7
7
|
const fuzz = @import("../test/fuzz.zig");
|
|
8
8
|
const vsr = @import("../vsr.zig");
|
|
9
|
+
|
|
9
10
|
const log = std.log.scoped(.lsm_tree_fuzz);
|
|
11
|
+
const tracer = @import("../tracer.zig");
|
|
10
12
|
|
|
11
13
|
const MessagePool = @import("../message_pool.zig").MessagePool;
|
|
12
14
|
const Transfer = @import("../tigerbeetle.zig").Transfer;
|
|
@@ -27,7 +29,7 @@ const Table = @import("table.zig").TableType(
|
|
|
27
29
|
Key.tombstone,
|
|
28
30
|
Key.tombstone_from_key,
|
|
29
31
|
);
|
|
30
|
-
const Tree = @import("tree.zig").TreeType(Table, Storage,
|
|
32
|
+
const Tree = @import("tree.zig").TreeType(Table, Storage, "Key.Value");
|
|
31
33
|
|
|
32
34
|
const Grid = GridType(Storage);
|
|
33
35
|
const SuperBlock = vsr.SuperBlockType(Storage);
|
|
@@ -440,6 +442,9 @@ pub fn generate_fuzz_ops(random: std.rand.Random) ![]const FuzzOp {
|
|
|
440
442
|
}
|
|
441
443
|
|
|
442
444
|
pub fn main() !void {
|
|
445
|
+
try tracer.init(allocator);
|
|
446
|
+
defer tracer.deinit(allocator);
|
|
447
|
+
|
|
443
448
|
const fuzz_args = try fuzz.parse_fuzz_args(allocator);
|
|
444
449
|
var rng = std.rand.DefaultPrng.init(fuzz_args.seed);
|
|
445
450
|
const random = rng.random();
|
|
@@ -9,6 +9,7 @@ const log = std.log.scoped(.main);
|
|
|
9
9
|
const build_options = @import("tigerbeetle_build_options");
|
|
10
10
|
const config = @import("config.zig");
|
|
11
11
|
pub const log_level: std.log.Level = @intToEnum(std.log.Level, config.log_level);
|
|
12
|
+
const tracer = @import("tracer.zig");
|
|
12
13
|
|
|
13
14
|
const cli = @import("cli.zig");
|
|
14
15
|
const fatal = cli.fatal;
|
|
@@ -130,6 +131,9 @@ const Command = struct {
|
|
|
130
131
|
) !void {
|
|
131
132
|
const allocator = arena.allocator();
|
|
132
133
|
|
|
134
|
+
try tracer.init(allocator);
|
|
135
|
+
defer tracer.deinit(allocator);
|
|
136
|
+
|
|
133
137
|
var command: Command = undefined;
|
|
134
138
|
try command.init(allocator, path, false);
|
|
135
139
|
defer command.deinit(allocator);
|
|
@@ -333,7 +333,7 @@ pub fn main() !void {
|
|
|
333
333
|
var crashes = cluster.replica_normal_count() -| replica_normal_min;
|
|
334
334
|
|
|
335
335
|
for (cluster.storages) |*storage, replica| {
|
|
336
|
-
if (cluster.replicas[replica].journal.recovered) {
|
|
336
|
+
if (cluster.replicas[replica].journal.status == .recovered) {
|
|
337
337
|
// TODO Remove this workaround when VSR recovery protocol is disabled.
|
|
338
338
|
// When only the minimum number of replicas are healthy (no more crashes allowed),
|
|
339
339
|
// disable storage faults on all healthy replicas.
|
|
@@ -2,7 +2,9 @@ const std = @import("std");
|
|
|
2
2
|
const assert = std.debug.assert;
|
|
3
3
|
const math = std.math;
|
|
4
4
|
const mem = std.mem;
|
|
5
|
+
|
|
5
6
|
const log = std.log.scoped(.state_machine);
|
|
7
|
+
const tracer = @import("tracer.zig");
|
|
6
8
|
|
|
7
9
|
const tb = @import("tigerbeetle.zig");
|
|
8
10
|
const snapshot_latest = @import("lsm/tree.zig").snapshot_latest;
|
|
@@ -114,6 +116,8 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
114
116
|
compact_callback: ?fn (*StateMachine) void = null,
|
|
115
117
|
checkpoint_callback: ?fn (*StateMachine) void = null,
|
|
116
118
|
|
|
119
|
+
tracer_slot: ?tracer.SpanStart,
|
|
120
|
+
|
|
117
121
|
pub fn init(allocator: mem.Allocator, grid: *Grid, options: Options) !StateMachine {
|
|
118
122
|
var forest = try Forest.init(
|
|
119
123
|
allocator,
|
|
@@ -127,10 +131,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
127
131
|
.prepare_timestamp = 0,
|
|
128
132
|
.commit_timestamp = 0,
|
|
129
133
|
.forest = forest,
|
|
134
|
+
.tracer_slot = null,
|
|
130
135
|
};
|
|
131
136
|
}
|
|
132
137
|
|
|
133
138
|
pub fn deinit(self: *StateMachine, allocator: mem.Allocator) void {
|
|
139
|
+
assert(self.tracer_slot == null);
|
|
140
|
+
|
|
134
141
|
self.forest.deinit(allocator);
|
|
135
142
|
}
|
|
136
143
|
|
|
@@ -218,6 +225,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
218
225
|
return;
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
tracer.start(
|
|
229
|
+
&self.tracer_slot,
|
|
230
|
+
.main,
|
|
231
|
+
.state_machine_prefetch,
|
|
232
|
+
);
|
|
233
|
+
|
|
221
234
|
self.prefetch_input = input;
|
|
222
235
|
self.prefetch_callback = callback;
|
|
223
236
|
|
|
@@ -248,6 +261,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
248
261
|
const callback = self.prefetch_callback.?;
|
|
249
262
|
self.prefetch_input = null;
|
|
250
263
|
self.prefetch_callback = null;
|
|
264
|
+
|
|
265
|
+
tracer.end(
|
|
266
|
+
&self.tracer_slot,
|
|
267
|
+
.main,
|
|
268
|
+
.state_machine_prefetch,
|
|
269
|
+
);
|
|
270
|
+
|
|
251
271
|
callback(self);
|
|
252
272
|
}
|
|
253
273
|
|
|
@@ -367,6 +387,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
367
387
|
_ = client;
|
|
368
388
|
assert(op != 0);
|
|
369
389
|
|
|
390
|
+
tracer.start(
|
|
391
|
+
&self.tracer_slot,
|
|
392
|
+
.main,
|
|
393
|
+
.state_machine_commit,
|
|
394
|
+
);
|
|
395
|
+
|
|
370
396
|
const result = switch (operation) {
|
|
371
397
|
.root => unreachable,
|
|
372
398
|
.register => 0,
|
|
@@ -377,6 +403,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
377
403
|
else => unreachable,
|
|
378
404
|
};
|
|
379
405
|
|
|
406
|
+
tracer.end(
|
|
407
|
+
&self.tracer_slot,
|
|
408
|
+
.main,
|
|
409
|
+
.state_machine_commit,
|
|
410
|
+
);
|
|
411
|
+
|
|
380
412
|
return result;
|
|
381
413
|
}
|
|
382
414
|
|
|
@@ -384,6 +416,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
384
416
|
assert(self.compact_callback == null);
|
|
385
417
|
assert(self.checkpoint_callback == null);
|
|
386
418
|
|
|
419
|
+
tracer.start(
|
|
420
|
+
&self.tracer_slot,
|
|
421
|
+
.main,
|
|
422
|
+
.state_machine_compact,
|
|
423
|
+
);
|
|
424
|
+
|
|
387
425
|
self.compact_callback = callback;
|
|
388
426
|
self.forest.compact(compact_finish, op);
|
|
389
427
|
}
|
|
@@ -392,6 +430,13 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
|
|
|
392
430
|
const self = @fieldParentPtr(StateMachine, "forest", forest);
|
|
393
431
|
const callback = self.compact_callback.?;
|
|
394
432
|
self.compact_callback = null;
|
|
433
|
+
|
|
434
|
+
tracer.end(
|
|
435
|
+
&self.tracer_slot,
|
|
436
|
+
.main,
|
|
437
|
+
.state_machine_compact,
|
|
438
|
+
);
|
|
439
|
+
|
|
395
440
|
callback(self);
|
|
396
441
|
}
|
|
397
442
|
|
|
@@ -72,7 +72,7 @@ pub const StateChecker = struct {
|
|
|
72
72
|
pub fn check_state(state_checker: *StateChecker, replica_index: u8) !void {
|
|
73
73
|
const replica = state_checker.replicas[replica_index];
|
|
74
74
|
const commit_header = header: {
|
|
75
|
-
if (replica.journal.recovered) {
|
|
75
|
+
if (replica.journal.status == .recovered) {
|
|
76
76
|
const commit_header = replica.journal.header_with_op(replica.commit_min);
|
|
77
77
|
assert(commit_header != null or replica.commit_min == replica.op_checkpoint);
|
|
78
78
|
break :header replica.journal.header_with_op(replica.commit_min);
|
|
@@ -751,6 +751,15 @@ pub const Storage = struct {
|
|
|
751
751
|
}
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
+
pub fn superblock_sector(
|
|
755
|
+
storage: *const Storage,
|
|
756
|
+
copy_: u8,
|
|
757
|
+
) *const superblock.SuperBlockSector {
|
|
758
|
+
const offset = vsr.Zone.superblock.offset(superblock.Layout.offset_sector(copy_));
|
|
759
|
+
const bytes = storage.memory[offset..][0..@sizeOf(superblock.SuperBlockSector)];
|
|
760
|
+
return mem.bytesAsValue(superblock.SuperBlockSector, bytes);
|
|
761
|
+
}
|
|
762
|
+
|
|
754
763
|
pub fn wal_headers(storage: *const Storage) []const vsr.Header {
|
|
755
764
|
const offset = vsr.Zone.wal_headers.offset(0);
|
|
756
765
|
const size = vsr.Zone.wal_headers.size().?;
|