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.
Files changed (83) hide show
  1. package/README.md +3 -2
  2. package/dist/index.d.ts +66 -61
  3. package/dist/index.js +66 -61
  4. package/dist/index.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/index.ts +5 -0
  7. package/src/node.zig +17 -18
  8. package/src/tigerbeetle/scripts/benchmark.bat +4 -3
  9. package/src/tigerbeetle/scripts/benchmark.sh +25 -10
  10. package/src/tigerbeetle/scripts/install.sh +2 -1
  11. package/src/tigerbeetle/scripts/install_zig.sh +14 -18
  12. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +12 -3
  13. package/src/tigerbeetle/scripts/vopr.sh +5 -5
  14. package/src/tigerbeetle/src/benchmark.zig +17 -9
  15. package/src/tigerbeetle/src/benchmark_array_search.zig +317 -0
  16. package/src/tigerbeetle/src/benchmarks/perf.zig +299 -0
  17. package/src/tigerbeetle/src/c/tb_client/context.zig +103 -0
  18. package/src/tigerbeetle/src/c/tb_client/packet.zig +80 -0
  19. package/src/tigerbeetle/src/c/tb_client/signal.zig +288 -0
  20. package/src/tigerbeetle/src/c/tb_client/thread.zig +329 -0
  21. package/src/tigerbeetle/src/c/tb_client.h +201 -0
  22. package/src/tigerbeetle/src/c/tb_client.zig +101 -0
  23. package/src/tigerbeetle/src/c/test.zig +1 -0
  24. package/src/tigerbeetle/src/cli.zig +142 -83
  25. package/src/tigerbeetle/src/config.zig +119 -10
  26. package/src/tigerbeetle/src/demo.zig +12 -8
  27. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +2 -2
  28. package/src/tigerbeetle/src/ewah.zig +318 -0
  29. package/src/tigerbeetle/src/ewah_benchmark.zig +121 -0
  30. package/src/tigerbeetle/src/eytzinger_benchmark.zig +317 -0
  31. package/src/tigerbeetle/src/fifo.zig +17 -1
  32. package/src/tigerbeetle/src/io/darwin.zig +12 -10
  33. package/src/tigerbeetle/src/io/linux.zig +25 -9
  34. package/src/tigerbeetle/src/io/windows.zig +13 -9
  35. package/src/tigerbeetle/src/iops.zig +101 -0
  36. package/src/tigerbeetle/src/lsm/binary_search.zig +214 -0
  37. package/src/tigerbeetle/src/lsm/bloom_filter.zig +82 -0
  38. package/src/tigerbeetle/src/lsm/compaction.zig +603 -0
  39. package/src/tigerbeetle/src/lsm/composite_key.zig +75 -0
  40. package/src/tigerbeetle/src/lsm/direction.zig +11 -0
  41. package/src/tigerbeetle/src/lsm/eytzinger.zig +587 -0
  42. package/src/tigerbeetle/src/lsm/forest.zig +630 -0
  43. package/src/tigerbeetle/src/lsm/grid.zig +473 -0
  44. package/src/tigerbeetle/src/lsm/groove.zig +939 -0
  45. package/src/tigerbeetle/src/lsm/k_way_merge.zig +452 -0
  46. package/src/tigerbeetle/src/lsm/level_iterator.zig +296 -0
  47. package/src/tigerbeetle/src/lsm/manifest.zig +680 -0
  48. package/src/tigerbeetle/src/lsm/manifest_level.zig +1169 -0
  49. package/src/tigerbeetle/src/lsm/manifest_log.zig +904 -0
  50. package/src/tigerbeetle/src/lsm/node_pool.zig +231 -0
  51. package/src/tigerbeetle/src/lsm/posted_groove.zig +399 -0
  52. package/src/tigerbeetle/src/lsm/segmented_array.zig +998 -0
  53. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +844 -0
  54. package/src/tigerbeetle/src/lsm/table.zig +932 -0
  55. package/src/tigerbeetle/src/lsm/table_immutable.zig +196 -0
  56. package/src/tigerbeetle/src/lsm/table_iterator.zig +295 -0
  57. package/src/tigerbeetle/src/lsm/table_mutable.zig +123 -0
  58. package/src/tigerbeetle/src/lsm/test.zig +429 -0
  59. package/src/tigerbeetle/src/lsm/tree.zig +1085 -0
  60. package/src/tigerbeetle/src/main.zig +119 -109
  61. package/src/tigerbeetle/src/message_bus.zig +49 -48
  62. package/src/tigerbeetle/src/message_pool.zig +15 -2
  63. package/src/tigerbeetle/src/ring_buffer.zig +126 -30
  64. package/src/tigerbeetle/src/simulator.zig +76 -44
  65. package/src/tigerbeetle/src/state_machine.zig +1022 -585
  66. package/src/tigerbeetle/src/storage.zig +46 -16
  67. package/src/tigerbeetle/src/test/cluster.zig +109 -63
  68. package/src/tigerbeetle/src/test/message_bus.zig +15 -24
  69. package/src/tigerbeetle/src/test/network.zig +26 -17
  70. package/src/tigerbeetle/src/test/state_checker.zig +7 -5
  71. package/src/tigerbeetle/src/test/state_machine.zig +159 -69
  72. package/src/tigerbeetle/src/test/storage.zig +57 -28
  73. package/src/tigerbeetle/src/tigerbeetle.zig +5 -0
  74. package/src/tigerbeetle/src/unit_tests.zig +8 -0
  75. package/src/tigerbeetle/src/util.zig +51 -0
  76. package/src/tigerbeetle/src/vsr/client.zig +21 -7
  77. package/src/tigerbeetle/src/vsr/journal.zig +154 -167
  78. package/src/tigerbeetle/src/vsr/replica.zig +744 -226
  79. package/src/tigerbeetle/src/vsr/superblock.zig +1743 -0
  80. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +258 -0
  81. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +644 -0
  82. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +546 -0
  83. 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(comptime T: type, comptime count_max: usize) type {
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: [count_max]T = undefined,
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
- // TODO add doc comments to these functions:
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 and unsafe if the ring buffer is modified.
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
- test "RingBuffer: low level interface" {
160
- const Ring = RingBuffer(u32, 2);
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
- var ring = Ring{};
163
- try test_iterator(Ring, &ring, &[_]u32{});
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, &ring, &[_]u32{0});
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, &ring, &[_]u32{ 0, 1 });
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, &ring, &[_]u32{1});
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, &ring, &[_]u32{ 1, 2 });
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, &ring, &[_]u32{1002});
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, &ring, &[_]u32{ 1002, 3 });
295
+ try test_iterator(Ring, ring, &[_]u32{ 2, 3 });
211
296
 
212
- try testing.expectEqual(@as(?u32, 1002), ring.head());
213
- try testing.expectEqual(@as(u32, 1002), ring.head_ptr().?.*);
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, &ring, &[_]u32{3});
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, &ring, &[_]u32{});
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
- cluster = try Cluster.create(allocator, random, .{
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
- defer cluster.destroy();
117
-
118
- cluster.state_checker = try StateChecker.init(allocator, cluster);
119
- defer cluster.state_checker.deinit();
120
-
121
- for (cluster.replicas) |*replica| {
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
- cluster.options.network_options.packet_simulator_options.one_way_delay_mean,
166
- cluster.options.network_options.packet_simulator_options.one_way_delay_min,
167
- cluster.options.network_options.packet_simulator_options.packet_loss_probability,
168
- cluster.options.network_options.packet_simulator_options.path_maximum_capacity,
169
- cluster.options.network_options.packet_simulator_options.path_clog_duration_mean,
170
- cluster.options.network_options.packet_simulator_options.path_clog_probability,
171
- cluster.options.network_options.packet_simulator_options.packet_replay_probability,
172
- cluster.options.network_options.packet_simulator_options.partition_mode,
173
- cluster.options.network_options.packet_simulator_options.partition_probability,
174
- cluster.options.network_options.packet_simulator_options.unpartition_probability,
175
- cluster.options.network_options.packet_simulator_options.partition_stability,
176
- cluster.options.network_options.packet_simulator_options.unpartition_stability,
177
- cluster.options.storage_options.read_latency_min,
178
- cluster.options.storage_options.read_latency_mean,
179
- cluster.options.storage_options.write_latency_min,
180
- cluster.options.storage_options.write_latency_mean,
181
- cluster.options.storage_options.read_fault_probability,
182
- cluster.options.storage_options.write_fault_probability,
183
- cluster.options.health_options.crash_probability * 100,
184
- cluster.options.health_options.crash_stability,
185
- cluster.options.health_options.restart_probability * 100,
186
- cluster.options.health_options.restart_stability,
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 = false;
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 = true;
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={}", .{replica.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={}", .{replica.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 stdout, silently ignoring any errors
413
- const stderr = std.io.getStdErr().writer();
414
- std.debug.getStderrMutex().lock();
415
- defer std.debug.getStderrMutex().unlock();
416
- nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
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
  }