tigerbeetle-node 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.d.ts +66 -61
- package/dist/index.js +66 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +5 -0
- package/src/node.zig +17 -18
- package/src/tigerbeetle/scripts/benchmark.bat +4 -3
- package/src/tigerbeetle/scripts/benchmark.sh +25 -10
- package/src/tigerbeetle/scripts/install.sh +2 -1
- package/src/tigerbeetle/scripts/install_zig.sh +14 -18
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +12 -3
- package/src/tigerbeetle/scripts/vopr.sh +5 -5
- package/src/tigerbeetle/src/benchmark.zig +17 -9
- package/src/tigerbeetle/src/benchmark_array_search.zig +317 -0
- package/src/tigerbeetle/src/benchmarks/perf.zig +299 -0
- package/src/tigerbeetle/src/c/tb_client/context.zig +103 -0
- package/src/tigerbeetle/src/c/tb_client/packet.zig +80 -0
- package/src/tigerbeetle/src/c/tb_client/signal.zig +288 -0
- package/src/tigerbeetle/src/c/tb_client/thread.zig +329 -0
- package/src/tigerbeetle/src/c/tb_client.h +201 -0
- package/src/tigerbeetle/src/c/tb_client.zig +101 -0
- package/src/tigerbeetle/src/c/test.zig +1 -0
- package/src/tigerbeetle/src/cli.zig +142 -83
- package/src/tigerbeetle/src/config.zig +119 -10
- package/src/tigerbeetle/src/demo.zig +12 -8
- package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +2 -2
- package/src/tigerbeetle/src/ewah.zig +318 -0
- package/src/tigerbeetle/src/ewah_benchmark.zig +121 -0
- package/src/tigerbeetle/src/eytzinger_benchmark.zig +317 -0
- package/src/tigerbeetle/src/fifo.zig +17 -1
- package/src/tigerbeetle/src/io/darwin.zig +12 -10
- package/src/tigerbeetle/src/io/linux.zig +25 -9
- package/src/tigerbeetle/src/io/windows.zig +13 -9
- package/src/tigerbeetle/src/iops.zig +101 -0
- package/src/tigerbeetle/src/lsm/binary_search.zig +214 -0
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +82 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +603 -0
- package/src/tigerbeetle/src/lsm/composite_key.zig +75 -0
- package/src/tigerbeetle/src/lsm/direction.zig +11 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +587 -0
- package/src/tigerbeetle/src/lsm/forest.zig +630 -0
- package/src/tigerbeetle/src/lsm/grid.zig +473 -0
- package/src/tigerbeetle/src/lsm/groove.zig +939 -0
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +452 -0
- package/src/tigerbeetle/src/lsm/level_iterator.zig +296 -0
- package/src/tigerbeetle/src/lsm/manifest.zig +680 -0
- package/src/tigerbeetle/src/lsm/manifest_level.zig +1169 -0
- package/src/tigerbeetle/src/lsm/manifest_log.zig +904 -0
- package/src/tigerbeetle/src/lsm/node_pool.zig +231 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +399 -0
- package/src/tigerbeetle/src/lsm/segmented_array.zig +998 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +844 -0
- package/src/tigerbeetle/src/lsm/table.zig +932 -0
- package/src/tigerbeetle/src/lsm/table_immutable.zig +196 -0
- package/src/tigerbeetle/src/lsm/table_iterator.zig +295 -0
- package/src/tigerbeetle/src/lsm/table_mutable.zig +123 -0
- package/src/tigerbeetle/src/lsm/test.zig +429 -0
- package/src/tigerbeetle/src/lsm/tree.zig +1085 -0
- package/src/tigerbeetle/src/main.zig +119 -109
- package/src/tigerbeetle/src/message_bus.zig +49 -48
- package/src/tigerbeetle/src/message_pool.zig +15 -2
- package/src/tigerbeetle/src/ring_buffer.zig +126 -30
- package/src/tigerbeetle/src/simulator.zig +76 -44
- package/src/tigerbeetle/src/state_machine.zig +1022 -585
- package/src/tigerbeetle/src/storage.zig +46 -16
- package/src/tigerbeetle/src/test/cluster.zig +109 -63
- package/src/tigerbeetle/src/test/message_bus.zig +15 -24
- package/src/tigerbeetle/src/test/network.zig +26 -17
- package/src/tigerbeetle/src/test/state_checker.zig +7 -5
- package/src/tigerbeetle/src/test/state_machine.zig +159 -69
- package/src/tigerbeetle/src/test/storage.zig +57 -28
- package/src/tigerbeetle/src/tigerbeetle.zig +5 -0
- package/src/tigerbeetle/src/unit_tests.zig +8 -0
- package/src/tigerbeetle/src/util.zig +51 -0
- package/src/tigerbeetle/src/vsr/client.zig +21 -7
- package/src/tigerbeetle/src/vsr/journal.zig +154 -167
- package/src/tigerbeetle/src/vsr/replica.zig +744 -226
- package/src/tigerbeetle/src/vsr/superblock.zig +1743 -0
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +258 -0
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +644 -0
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +546 -0
- package/src/tigerbeetle/src/vsr.zig +43 -115
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const assert = std.debug.assert;
|
|
3
|
+
const math = std.math;
|
|
4
|
+
const mem = std.mem;
|
|
3
5
|
|
|
4
6
|
/// A First In, First Out ring buffer holding at most `count_max` elements.
|
|
5
|
-
pub fn RingBuffer(
|
|
7
|
+
pub fn RingBuffer(
|
|
8
|
+
comptime T: type,
|
|
9
|
+
comptime count_max: usize,
|
|
10
|
+
comptime buffer_type: enum { array, pointer },
|
|
11
|
+
) type {
|
|
6
12
|
return struct {
|
|
7
13
|
const Self = @This();
|
|
8
14
|
|
|
9
|
-
buffer:
|
|
15
|
+
buffer: switch (buffer_type) {
|
|
16
|
+
.array => [count_max]T,
|
|
17
|
+
.pointer => *[count_max]T,
|
|
18
|
+
} = switch (buffer_type) {
|
|
19
|
+
.array => undefined,
|
|
20
|
+
.pointer => @compileError("init() must be used if buffer_type is .pointer!"),
|
|
21
|
+
},
|
|
10
22
|
|
|
11
23
|
/// The index of the slot with the first item, if any.
|
|
12
24
|
index: usize = 0,
|
|
@@ -14,7 +26,22 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
14
26
|
/// The number of items in the buffer.
|
|
15
27
|
count: usize = 0,
|
|
16
28
|
|
|
17
|
-
|
|
29
|
+
pub usingnamespace switch (buffer_type) {
|
|
30
|
+
.array => struct {},
|
|
31
|
+
.pointer => struct {
|
|
32
|
+
pub fn init(allocator: mem.Allocator) !Self {
|
|
33
|
+
const buffer = try allocator.create([count_max]T);
|
|
34
|
+
errdefer allocator.destroy(buffer);
|
|
35
|
+
return Self{ .buffer = buffer };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn deinit(self: *Self, allocator: mem.Allocator) void {
|
|
39
|
+
allocator.destroy(self.buffer);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// TODO Add doc comments to these functions:
|
|
18
45
|
pub inline fn head(self: Self) ?T {
|
|
19
46
|
if (self.empty()) return null;
|
|
20
47
|
return self.buffer[self.index];
|
|
@@ -25,6 +52,11 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
25
52
|
return &self.buffer[self.index];
|
|
26
53
|
}
|
|
27
54
|
|
|
55
|
+
pub inline fn head_ptr_const(self: *const Self) ?*const T {
|
|
56
|
+
if (self.empty()) return null;
|
|
57
|
+
return &self.buffer[self.index];
|
|
58
|
+
}
|
|
59
|
+
|
|
28
60
|
pub inline fn tail(self: Self) ?T {
|
|
29
61
|
if (self.empty()) return null;
|
|
30
62
|
return self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
@@ -35,6 +67,11 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
35
67
|
return &self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
36
68
|
}
|
|
37
69
|
|
|
70
|
+
pub inline fn tail_ptr_const(self: *const Self) ?*const T {
|
|
71
|
+
if (self.empty()) return null;
|
|
72
|
+
return &self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
73
|
+
}
|
|
74
|
+
|
|
38
75
|
pub inline fn get_ptr(self: *Self, index: usize) ?*T {
|
|
39
76
|
if (index < self.count) {
|
|
40
77
|
return &self.buffer[(self.index + index) % self.buffer.len];
|
|
@@ -54,6 +91,11 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
54
91
|
return &self.buffer[(self.index + self.count) % self.buffer.len];
|
|
55
92
|
}
|
|
56
93
|
|
|
94
|
+
pub inline fn next_tail_ptr_const(self: *const Self) ?*const T {
|
|
95
|
+
if (self.full()) return null;
|
|
96
|
+
return &self.buffer[(self.index + self.count) % self.buffer.len];
|
|
97
|
+
}
|
|
98
|
+
|
|
57
99
|
pub inline fn advance_head(self: *Self) void {
|
|
58
100
|
self.index += 1;
|
|
59
101
|
self.index %= self.buffer.len;
|
|
@@ -96,6 +138,19 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
96
138
|
};
|
|
97
139
|
}
|
|
98
140
|
|
|
141
|
+
pub fn push_slice(self: *Self, items: []const T) error{NoSpaceLeft}!void {
|
|
142
|
+
if (self.count + items.len > self.buffer.len) return error.NoSpaceLeft;
|
|
143
|
+
|
|
144
|
+
const pre_wrap_start = (self.index + self.count) % self.buffer.len;
|
|
145
|
+
const pre_wrap_count = math.min(items.len, self.buffer.len - pre_wrap_start);
|
|
146
|
+
const post_wrap_count = items.len - pre_wrap_count;
|
|
147
|
+
|
|
148
|
+
mem.copy(T, self.buffer[pre_wrap_start..], items[0..pre_wrap_count]);
|
|
149
|
+
mem.copy(T, self.buffer[0..post_wrap_count], items[pre_wrap_count..]);
|
|
150
|
+
|
|
151
|
+
self.count += items.len;
|
|
152
|
+
}
|
|
153
|
+
|
|
99
154
|
/// Remove and return the next item, if any.
|
|
100
155
|
pub fn pop(self: *Self) ?T {
|
|
101
156
|
const result = self.head() orelse return null;
|
|
@@ -111,17 +166,18 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
111
166
|
}
|
|
112
167
|
|
|
113
168
|
pub const Iterator = struct {
|
|
114
|
-
ring: *Self,
|
|
169
|
+
ring: *const Self,
|
|
115
170
|
count: usize = 0,
|
|
116
171
|
|
|
117
172
|
pub fn next(it: *Iterator) ?T {
|
|
173
|
+
// TODO Use next_ptr() internally to avoid duplicating this code.
|
|
118
174
|
assert(it.count <= it.ring.count);
|
|
119
175
|
if (it.count == it.ring.count) return null;
|
|
120
176
|
defer it.count += 1;
|
|
121
177
|
return it.ring.buffer[(it.ring.index + it.count) % it.ring.buffer.len];
|
|
122
178
|
}
|
|
123
179
|
|
|
124
|
-
pub fn next_ptr(it: *Iterator) ?*T {
|
|
180
|
+
pub fn next_ptr(it: *Iterator) ?*const T {
|
|
125
181
|
assert(it.count <= it.ring.count);
|
|
126
182
|
if (it.count == it.ring.count) return null;
|
|
127
183
|
defer it.count += 1;
|
|
@@ -130,8 +186,25 @@ pub fn RingBuffer(comptime T: type, comptime count_max: usize) type {
|
|
|
130
186
|
};
|
|
131
187
|
|
|
132
188
|
/// Returns an iterator to iterate through all `count` items in the ring buffer.
|
|
133
|
-
/// The iterator is invalidated
|
|
134
|
-
pub fn iterator(self: *Self) Iterator {
|
|
189
|
+
/// The iterator is invalidated if the ring buffer is advanced.
|
|
190
|
+
pub fn iterator(self: *const Self) Iterator {
|
|
191
|
+
return .{ .ring = self };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
pub const IteratorMutable = struct {
|
|
195
|
+
ring: *Self,
|
|
196
|
+
count: usize = 0,
|
|
197
|
+
|
|
198
|
+
pub fn next_ptr(it: *IteratorMutable) ?*T {
|
|
199
|
+
assert(it.count <= it.ring.count);
|
|
200
|
+
if (it.count == it.ring.count) return null;
|
|
201
|
+
defer it.count += 1;
|
|
202
|
+
return &it.ring.buffer[(it.ring.index + it.count) % it.ring.buffer.len];
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// TODO Add to tests.
|
|
207
|
+
pub fn iterator_mutable(self: *Self) IteratorMutable {
|
|
135
208
|
return .{ .ring = self };
|
|
136
209
|
}
|
|
137
210
|
};
|
|
@@ -156,11 +229,30 @@ fn test_iterator(comptime T: type, ring: *T, values: []const u32) !void {
|
|
|
156
229
|
try testing.expectEqual(ring_index, ring.index);
|
|
157
230
|
}
|
|
158
231
|
|
|
159
|
-
|
|
160
|
-
|
|
232
|
+
fn test_low_level_interface(comptime Ring: type, ring: *Ring) !void {
|
|
233
|
+
try ring.push_slice(&[_]u32{});
|
|
234
|
+
try test_iterator(Ring, ring, &[_]u32{});
|
|
235
|
+
|
|
236
|
+
try testing.expectError(error.NoSpaceLeft, ring.push_slice(&[_]u32{ 1, 2, 3 }));
|
|
161
237
|
|
|
162
|
-
|
|
163
|
-
try
|
|
238
|
+
try ring.push_slice(&[_]u32{1});
|
|
239
|
+
try testing.expectEqual(@as(?u32, 1), ring.tail());
|
|
240
|
+
try testing.expectEqual(@as(u32, 1), ring.tail_ptr().?.*);
|
|
241
|
+
ring.advance_head();
|
|
242
|
+
|
|
243
|
+
try testing.expectEqual(@as(usize, 1), ring.index);
|
|
244
|
+
try testing.expectEqual(@as(usize, 0), ring.count);
|
|
245
|
+
try ring.push_slice(&[_]u32{ 1, 2 });
|
|
246
|
+
try test_iterator(Ring, ring, &[_]u32{ 1, 2 });
|
|
247
|
+
ring.advance_head();
|
|
248
|
+
ring.advance_head();
|
|
249
|
+
|
|
250
|
+
try testing.expectEqual(@as(usize, 1), ring.index);
|
|
251
|
+
try testing.expectEqual(@as(usize, 0), ring.count);
|
|
252
|
+
try ring.push_slice(&[_]u32{1});
|
|
253
|
+
try testing.expectEqual(@as(?u32, 1), ring.tail());
|
|
254
|
+
try testing.expectEqual(@as(u32, 1), ring.tail_ptr().?.*);
|
|
255
|
+
ring.advance_head();
|
|
164
256
|
|
|
165
257
|
try testing.expectEqual(@as(?u32, null), ring.head());
|
|
166
258
|
try testing.expectEqual(@as(?*u32, null), ring.head_ptr());
|
|
@@ -171,13 +263,13 @@ test "RingBuffer: low level interface" {
|
|
|
171
263
|
ring.advance_tail();
|
|
172
264
|
try testing.expectEqual(@as(?u32, 0), ring.tail());
|
|
173
265
|
try testing.expectEqual(@as(u32, 0), ring.tail_ptr().?.*);
|
|
174
|
-
try test_iterator(Ring,
|
|
266
|
+
try test_iterator(Ring, ring, &[_]u32{0});
|
|
175
267
|
|
|
176
268
|
ring.next_tail_ptr().?.* = 1;
|
|
177
269
|
ring.advance_tail();
|
|
178
270
|
try testing.expectEqual(@as(?u32, 1), ring.tail());
|
|
179
271
|
try testing.expectEqual(@as(u32, 1), ring.tail_ptr().?.*);
|
|
180
|
-
try test_iterator(Ring,
|
|
272
|
+
try test_iterator(Ring, ring, &[_]u32{ 0, 1 });
|
|
181
273
|
|
|
182
274
|
try testing.expectEqual(@as(?u32, null), ring.next_tail());
|
|
183
275
|
try testing.expectEqual(@as(?*u32, null), ring.next_tail_ptr());
|
|
@@ -185,39 +277,32 @@ test "RingBuffer: low level interface" {
|
|
|
185
277
|
try testing.expectEqual(@as(?u32, 0), ring.head());
|
|
186
278
|
try testing.expectEqual(@as(u32, 0), ring.head_ptr().?.*);
|
|
187
279
|
ring.advance_head();
|
|
188
|
-
try test_iterator(Ring,
|
|
280
|
+
try test_iterator(Ring, ring, &[_]u32{1});
|
|
189
281
|
|
|
190
282
|
ring.next_tail_ptr().?.* = 2;
|
|
191
283
|
ring.advance_tail();
|
|
192
284
|
try testing.expectEqual(@as(?u32, 2), ring.tail());
|
|
193
285
|
try testing.expectEqual(@as(u32, 2), ring.tail_ptr().?.*);
|
|
194
|
-
try test_iterator(Ring,
|
|
195
|
-
|
|
196
|
-
var iterator = ring.iterator();
|
|
197
|
-
while (iterator.next_ptr()) |item_ptr| {
|
|
198
|
-
item_ptr.* += 1000;
|
|
199
|
-
}
|
|
286
|
+
try test_iterator(Ring, ring, &[_]u32{ 1, 2 });
|
|
200
287
|
|
|
201
|
-
try testing.expectEqual(@as(?u32, 1001), ring.head());
|
|
202
|
-
try testing.expectEqual(@as(u32, 1001), ring.head_ptr().?.*);
|
|
203
288
|
ring.advance_head();
|
|
204
|
-
try test_iterator(Ring,
|
|
289
|
+
try test_iterator(Ring, ring, &[_]u32{2});
|
|
205
290
|
|
|
206
291
|
ring.next_tail_ptr().?.* = 3;
|
|
207
292
|
ring.advance_tail();
|
|
208
293
|
try testing.expectEqual(@as(?u32, 3), ring.tail());
|
|
209
294
|
try testing.expectEqual(@as(u32, 3), ring.tail_ptr().?.*);
|
|
210
|
-
try test_iterator(Ring,
|
|
295
|
+
try test_iterator(Ring, ring, &[_]u32{ 2, 3 });
|
|
211
296
|
|
|
212
|
-
try testing.expectEqual(@as(?u32,
|
|
213
|
-
try testing.expectEqual(@as(u32,
|
|
297
|
+
try testing.expectEqual(@as(?u32, 2), ring.head());
|
|
298
|
+
try testing.expectEqual(@as(u32, 2), ring.head_ptr().?.*);
|
|
214
299
|
ring.advance_head();
|
|
215
|
-
try test_iterator(Ring,
|
|
300
|
+
try test_iterator(Ring, ring, &[_]u32{3});
|
|
216
301
|
|
|
217
302
|
try testing.expectEqual(@as(?u32, 3), ring.head());
|
|
218
303
|
try testing.expectEqual(@as(u32, 3), ring.head_ptr().?.*);
|
|
219
304
|
ring.advance_head();
|
|
220
|
-
try test_iterator(Ring,
|
|
305
|
+
try test_iterator(Ring, ring, &[_]u32{});
|
|
221
306
|
|
|
222
307
|
try testing.expectEqual(@as(?u32, null), ring.head());
|
|
223
308
|
try testing.expectEqual(@as(?*u32, null), ring.head_ptr());
|
|
@@ -225,8 +310,19 @@ test "RingBuffer: low level interface" {
|
|
|
225
310
|
try testing.expectEqual(@as(?*u32, null), ring.tail_ptr());
|
|
226
311
|
}
|
|
227
312
|
|
|
313
|
+
test "RingBuffer: low level interface" {
|
|
314
|
+
const ArrayRing = RingBuffer(u32, 2, .array);
|
|
315
|
+
var array_ring: ArrayRing = .{};
|
|
316
|
+
try test_low_level_interface(ArrayRing, &array_ring);
|
|
317
|
+
|
|
318
|
+
const PointerRing = RingBuffer(u32, 2, .pointer);
|
|
319
|
+
var pointer_ring = try PointerRing.init(testing.allocator);
|
|
320
|
+
defer pointer_ring.deinit(testing.allocator);
|
|
321
|
+
try test_low_level_interface(PointerRing, &pointer_ring);
|
|
322
|
+
}
|
|
323
|
+
|
|
228
324
|
test "RingBuffer: push/pop high level interface" {
|
|
229
|
-
var fifo = RingBuffer(u32, 3){};
|
|
325
|
+
var fifo = RingBuffer(u32, 3, .array){};
|
|
230
326
|
|
|
231
327
|
try testing.expect(!fifo.full());
|
|
232
328
|
try testing.expect(fifo.empty());
|
|
@@ -273,7 +369,7 @@ test "RingBuffer: push/pop high level interface" {
|
|
|
273
369
|
}
|
|
274
370
|
|
|
275
371
|
test "RingBuffer: pop_tail" {
|
|
276
|
-
var lifo = RingBuffer(u32, 3){};
|
|
372
|
+
var lifo = RingBuffer(u32, 3, .array){};
|
|
277
373
|
try lifo.push(1);
|
|
278
374
|
try lifo.push(2);
|
|
279
375
|
try lifo.push(3);
|
|
@@ -7,6 +7,7 @@ const config = @import("config.zig");
|
|
|
7
7
|
|
|
8
8
|
const Client = @import("test/cluster.zig").Client;
|
|
9
9
|
const Cluster = @import("test/cluster.zig").Cluster;
|
|
10
|
+
const ClusterOptions = @import("test/cluster.zig").ClusterOptions;
|
|
10
11
|
const Header = @import("vsr.zig").Header;
|
|
11
12
|
const Replica = @import("test/cluster.zig").Replica;
|
|
12
13
|
const StateChecker = @import("test/state_checker.zig").StateChecker;
|
|
@@ -21,13 +22,23 @@ const output = std.log.scoped(.state_checker);
|
|
|
21
22
|
const log_state_transitions_only = builtin.mode != .Debug;
|
|
22
23
|
|
|
23
24
|
const log_health = std.log.scoped(.health);
|
|
25
|
+
const log_faults = std.log.scoped(.faults);
|
|
24
26
|
|
|
25
27
|
/// You can fine tune your log levels even further (debug/info/notice/warn/err/crit/alert/emerg):
|
|
26
28
|
pub const log_level: std.log.Level = if (log_state_transitions_only) .info else .debug;
|
|
27
29
|
|
|
30
|
+
/// Modifies compile-time constants on "config.zig".
|
|
31
|
+
pub const deployment_environment = .simulation;
|
|
32
|
+
comptime {
|
|
33
|
+
assert(config.deployment_environment == .simulation);
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
var cluster: *Cluster = undefined;
|
|
29
37
|
|
|
30
38
|
pub fn main() !void {
|
|
39
|
+
// This must be initialized at runtime as stderr is not comptime known on e.g. Windows.
|
|
40
|
+
log_buffer.unbuffered_writer = std.io.getStdErr().writer();
|
|
41
|
+
|
|
31
42
|
// TODO Use std.testing.allocator when all deinit() leaks are fixed.
|
|
32
43
|
const allocator = std.heap.page_allocator;
|
|
33
44
|
|
|
@@ -70,11 +81,12 @@ pub fn main() !void {
|
|
|
70
81
|
const idle_on_probability = random.uintLessThan(u8, 20);
|
|
71
82
|
const idle_off_probability = 10 + random.uintLessThan(u8, 10);
|
|
72
83
|
|
|
73
|
-
|
|
84
|
+
const cluster_options: ClusterOptions = .{
|
|
74
85
|
.cluster = 0,
|
|
75
86
|
.replica_count = replica_count,
|
|
76
87
|
.client_count = client_count,
|
|
77
88
|
.seed = random.int(u64),
|
|
89
|
+
.on_change_state = on_change_replica,
|
|
78
90
|
.network_options = .{
|
|
79
91
|
.packet_simulator_options = .{
|
|
80
92
|
.replica_count = replica_count,
|
|
@@ -112,16 +124,13 @@ pub fn main() !void {
|
|
|
112
124
|
.restart_probability = 0.01,
|
|
113
125
|
.restart_stability = random.uintLessThan(u32, 1_000),
|
|
114
126
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
replica.on_change_state = on_change_replica;
|
|
123
|
-
}
|
|
124
|
-
cluster.on_change_state = on_change_replica;
|
|
127
|
+
.state_machine_options = .{
|
|
128
|
+
.seed = random.int(u64),
|
|
129
|
+
.prefetch_mean = 5 + random.uintLessThan(u64, 10),
|
|
130
|
+
.compact_mean = 5 + random.uintLessThan(u64, 10),
|
|
131
|
+
.checkpoint_mean = 5 + random.uintLessThan(u64, 10),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
125
134
|
|
|
126
135
|
output.info(
|
|
127
136
|
\\
|
|
@@ -154,6 +163,9 @@ pub fn main() !void {
|
|
|
154
163
|
\\ crash_stability={} ticks
|
|
155
164
|
\\ restart_probability={d}%
|
|
156
165
|
\\ restart_stability={} ticks
|
|
166
|
+
\\ prefetch_mean={} ticks
|
|
167
|
+
\\ compact_mean={} ticks
|
|
168
|
+
\\ checkpoint_mean={} ticks
|
|
157
169
|
\\
|
|
158
170
|
, .{
|
|
159
171
|
seed,
|
|
@@ -162,30 +174,39 @@ pub fn main() !void {
|
|
|
162
174
|
request_probability,
|
|
163
175
|
idle_on_probability,
|
|
164
176
|
idle_off_probability,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
cluster_options.network_options.packet_simulator_options.one_way_delay_mean,
|
|
178
|
+
cluster_options.network_options.packet_simulator_options.one_way_delay_min,
|
|
179
|
+
cluster_options.network_options.packet_simulator_options.packet_loss_probability,
|
|
180
|
+
cluster_options.network_options.packet_simulator_options.path_maximum_capacity,
|
|
181
|
+
cluster_options.network_options.packet_simulator_options.path_clog_duration_mean,
|
|
182
|
+
cluster_options.network_options.packet_simulator_options.path_clog_probability,
|
|
183
|
+
cluster_options.network_options.packet_simulator_options.packet_replay_probability,
|
|
184
|
+
cluster_options.network_options.packet_simulator_options.partition_mode,
|
|
185
|
+
cluster_options.network_options.packet_simulator_options.partition_probability,
|
|
186
|
+
cluster_options.network_options.packet_simulator_options.unpartition_probability,
|
|
187
|
+
cluster_options.network_options.packet_simulator_options.partition_stability,
|
|
188
|
+
cluster_options.network_options.packet_simulator_options.unpartition_stability,
|
|
189
|
+
cluster_options.storage_options.read_latency_min,
|
|
190
|
+
cluster_options.storage_options.read_latency_mean,
|
|
191
|
+
cluster_options.storage_options.write_latency_min,
|
|
192
|
+
cluster_options.storage_options.write_latency_mean,
|
|
193
|
+
cluster_options.storage_options.read_fault_probability,
|
|
194
|
+
cluster_options.storage_options.write_fault_probability,
|
|
195
|
+
cluster_options.health_options.crash_probability * 100,
|
|
196
|
+
cluster_options.health_options.crash_stability,
|
|
197
|
+
cluster_options.health_options.restart_probability * 100,
|
|
198
|
+
cluster_options.health_options.restart_stability,
|
|
199
|
+
cluster_options.state_machine_options.prefetch_mean,
|
|
200
|
+
cluster_options.state_machine_options.compact_mean,
|
|
201
|
+
cluster_options.state_machine_options.checkpoint_mean,
|
|
187
202
|
});
|
|
188
203
|
|
|
204
|
+
cluster = try Cluster.create(allocator, random, cluster_options);
|
|
205
|
+
defer cluster.destroy();
|
|
206
|
+
|
|
207
|
+
cluster.state_checker = try StateChecker.init(allocator, cluster);
|
|
208
|
+
defer cluster.state_checker.deinit();
|
|
209
|
+
|
|
189
210
|
var requests_sent: u64 = 0;
|
|
190
211
|
var idle = false;
|
|
191
212
|
|
|
@@ -215,7 +236,6 @@ pub fn main() !void {
|
|
|
215
236
|
|
|
216
237
|
for (cluster.storages) |*storage, replica| {
|
|
217
238
|
if (cluster.replicas[replica].journal.recovered) {
|
|
218
|
-
|
|
219
239
|
// TODO Remove this workaround when VSR recovery protocol is disabled.
|
|
220
240
|
// When only the minimum number of replicas are healthy (no more crashes allowed),
|
|
221
241
|
// disable storage faults on all healthy replicas.
|
|
@@ -226,11 +246,17 @@ pub fn main() !void {
|
|
|
226
246
|
// because two replicas are not enough to nack, and the unhealthy replica cannot
|
|
227
247
|
// complete the VSR recovery protocol either.
|
|
228
248
|
if (cluster.health[replica] == .up and crashes == 0) {
|
|
229
|
-
storage.faulty
|
|
249
|
+
if (storage.faulty) {
|
|
250
|
+
log_faults.debug("{}: disable storage faults", .{replica});
|
|
251
|
+
storage.faulty = false;
|
|
252
|
+
}
|
|
230
253
|
} else {
|
|
231
254
|
// When a journal recovers for the first time, enable its storage faults.
|
|
232
255
|
// Future crashes will recover in the presence of faults.
|
|
233
|
-
storage.faulty
|
|
256
|
+
if (!storage.faulty) {
|
|
257
|
+
log_faults.debug("{}: enable storage faults", .{replica});
|
|
258
|
+
storage.faulty = true;
|
|
259
|
+
}
|
|
234
260
|
}
|
|
235
261
|
}
|
|
236
262
|
storage.tick();
|
|
@@ -252,7 +278,7 @@ pub fn main() !void {
|
|
|
252
278
|
}
|
|
253
279
|
|
|
254
280
|
if (!try cluster.crash_replica(replica.replica)) continue;
|
|
255
|
-
log_health.debug("crash replica
|
|
281
|
+
log_health.debug("{}: crash replica", .{replica.replica});
|
|
256
282
|
crashes -= 1;
|
|
257
283
|
},
|
|
258
284
|
.down => |*ticks| {
|
|
@@ -263,7 +289,7 @@ pub fn main() !void {
|
|
|
263
289
|
assert(replica.status == .recovering);
|
|
264
290
|
if (ticks.* == 0 and chance_f64(random, health_options.restart_probability)) {
|
|
265
291
|
cluster.health[replica.replica] = .{ .up = health_options.restart_stability };
|
|
266
|
-
log_health.debug("restart replica
|
|
292
|
+
log_health.debug("{}: restart replica", .{replica.replica});
|
|
267
293
|
}
|
|
268
294
|
},
|
|
269
295
|
}
|
|
@@ -325,7 +351,6 @@ fn args_next(args: *std.process.ArgIterator, allocator: std.mem.Allocator) ?[:0]
|
|
|
325
351
|
}
|
|
326
352
|
|
|
327
353
|
fn on_change_replica(replica: *Replica) void {
|
|
328
|
-
assert(cluster.state_machines[replica.replica].state == replica.state_machine.state);
|
|
329
354
|
cluster.state_checker.check_state(replica.replica);
|
|
330
355
|
}
|
|
331
356
|
|
|
@@ -398,6 +423,11 @@ fn parse_seed(bytes: []const u8) u64 {
|
|
|
398
423
|
};
|
|
399
424
|
}
|
|
400
425
|
|
|
426
|
+
var log_buffer: std.io.BufferedWriter(4096, std.fs.File.Writer) = .{
|
|
427
|
+
// This is initialized in main(), as std.io.getStdErr() is not comptime known on e.g. Windows.
|
|
428
|
+
.unbuffered_writer = undefined,
|
|
429
|
+
};
|
|
430
|
+
|
|
401
431
|
pub fn log(
|
|
402
432
|
comptime level: std.log.Level,
|
|
403
433
|
comptime scope: @TypeOf(.EnumLiteral),
|
|
@@ -409,9 +439,11 @@ pub fn log(
|
|
|
409
439
|
const prefix_default = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
|
|
410
440
|
const prefix = if (log_state_transitions_only) "" else prefix_default;
|
|
411
441
|
|
|
412
|
-
// Print the message to
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
442
|
+
// Print the message to stderr using a buffer to avoid many small write() syscalls when
|
|
443
|
+
// providing many format arguments. Silently ignore failure.
|
|
444
|
+
log_buffer.writer().print(prefix ++ format ++ "\n", args) catch {};
|
|
445
|
+
|
|
446
|
+
// Flush the buffer before returning to ensure, for example, that a log message
|
|
447
|
+
// immediately before a failing assertion is fully printed.
|
|
448
|
+
log_buffer.flush() catch {};
|
|
417
449
|
}
|