tigerbeetle-node 0.5.0 → 0.5.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/package.json +2 -2
- package/scripts/postinstall.sh +2 -2
- package/src/node.zig +17 -15
- package/src/tigerbeetle/scripts/install.sh +1 -1
- package/src/tigerbeetle/scripts/install_zig.bat +3 -3
- package/src/tigerbeetle/scripts/install_zig.sh +4 -2
- package/src/tigerbeetle/scripts/lint.zig +8 -2
- package/src/tigerbeetle/src/benchmark.zig +8 -6
- package/src/tigerbeetle/src/cli.zig +6 -4
- package/src/tigerbeetle/src/config.zig +2 -2
- package/src/tigerbeetle/src/demo.zig +119 -97
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
- package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +2 -3
- package/src/tigerbeetle/src/io/benchmark.zig +238 -0
- package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +88 -127
- package/src/tigerbeetle/src/io/linux.zig +933 -0
- package/src/tigerbeetle/src/io/test.zig +621 -0
- package/src/tigerbeetle/src/io.zig +7 -1328
- package/src/tigerbeetle/src/main.zig +18 -10
- package/src/tigerbeetle/src/message_bus.zig +43 -54
- package/src/tigerbeetle/src/message_pool.zig +3 -2
- package/src/tigerbeetle/src/simulator.zig +41 -37
- package/src/tigerbeetle/src/state_machine.zig +58 -27
- package/src/tigerbeetle/src/storage.zig +49 -46
- package/src/tigerbeetle/src/test/cluster.zig +2 -2
- package/src/tigerbeetle/src/test/message_bus.zig +6 -6
- package/src/tigerbeetle/src/test/network.zig +3 -3
- package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
- package/src/tigerbeetle/src/test/state_checker.zig +1 -1
- package/src/tigerbeetle/src/test/state_machine.zig +4 -0
- package/src/tigerbeetle/src/test/storage.zig +23 -19
- package/src/tigerbeetle/src/test/time.zig +2 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +6 -128
- package/src/tigerbeetle/src/time.zig +6 -5
- package/src/tigerbeetle/src/vsr/client.zig +7 -7
- package/src/tigerbeetle/src/vsr/clock.zig +26 -43
- package/src/tigerbeetle/src/vsr/journal.zig +7 -6
- package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
- package/src/tigerbeetle/src/vsr/replica.zig +49 -46
- package/src/tigerbeetle/src/vsr.zig +24 -20
- package/src/translate.zig +55 -55
|
@@ -20,12 +20,20 @@ const vsr = @import("vsr.zig");
|
|
|
20
20
|
const Replica = vsr.Replica(StateMachine, MessageBus, Storage, Time);
|
|
21
21
|
|
|
22
22
|
pub fn main() !void {
|
|
23
|
-
var
|
|
24
|
-
|
|
23
|
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
24
|
+
defer arena.deinit();
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const allocator = arena.allocator();
|
|
27
|
+
|
|
28
|
+
switch (cli.parse_args(allocator)) {
|
|
29
|
+
.init => |args| try init(args.cluster, args.replica, args.dir_fd),
|
|
30
|
+
.start => |args| try start(
|
|
31
|
+
allocator,
|
|
32
|
+
args.cluster,
|
|
33
|
+
args.replica,
|
|
34
|
+
args.addresses,
|
|
35
|
+
args.dir_fd,
|
|
36
|
+
),
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
@@ -34,7 +42,7 @@ const filename_fmt = "cluster_{d:0>10}_replica_{d:0>3}.tigerbeetle";
|
|
|
34
42
|
const filename_len = fmt.count(filename_fmt, .{ 0, 0 });
|
|
35
43
|
|
|
36
44
|
/// Create a .tigerbeetle data file for the given args and exit
|
|
37
|
-
fn init(
|
|
45
|
+
fn init(cluster: u32, replica: u8, dir_fd: os.fd_t) !void {
|
|
38
46
|
// Add 1 for the terminating null byte
|
|
39
47
|
var buffer: [filename_len + 1]u8 = undefined;
|
|
40
48
|
const filename = fmt.bufPrintZ(&buffer, filename_fmt, .{ cluster, replica }) catch unreachable;
|
|
@@ -53,7 +61,7 @@ fn init(arena: *mem.Allocator, cluster: u32, replica: u8, dir_fd: os.fd_t) !void
|
|
|
53
61
|
|
|
54
62
|
/// Run as a replica server defined by the given args
|
|
55
63
|
fn start(
|
|
56
|
-
|
|
64
|
+
allocator: mem.Allocator,
|
|
57
65
|
cluster: u32,
|
|
58
66
|
replica_index: u8,
|
|
59
67
|
addresses: []std.net.Address,
|
|
@@ -75,14 +83,14 @@ fn start(
|
|
|
75
83
|
);
|
|
76
84
|
var io = try IO.init(128, 0);
|
|
77
85
|
var state_machine = try StateMachine.init(
|
|
78
|
-
|
|
86
|
+
allocator,
|
|
79
87
|
config.accounts_max,
|
|
80
88
|
config.transfers_max,
|
|
81
89
|
config.commits_max,
|
|
82
90
|
);
|
|
83
91
|
var storage = try Storage.init(config.journal_size_max, storage_fd, &io);
|
|
84
92
|
var message_bus = try MessageBus.init(
|
|
85
|
-
|
|
93
|
+
allocator,
|
|
86
94
|
cluster,
|
|
87
95
|
addresses,
|
|
88
96
|
replica_index,
|
|
@@ -90,7 +98,7 @@ fn start(
|
|
|
90
98
|
);
|
|
91
99
|
var time: Time = .{};
|
|
92
100
|
var replica = try Replica.init(
|
|
93
|
-
|
|
101
|
+
allocator,
|
|
94
102
|
cluster,
|
|
95
103
|
@intCast(u8, addresses.len),
|
|
96
104
|
replica_index,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
const assert = std.debug.assert;
|
|
3
4
|
const mem = std.mem;
|
|
4
5
|
const os = std.os;
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const sock_flags = os.SOCK_CLOEXEC | (if (is_darwin) os.SOCK_NONBLOCK else 0);
|
|
7
|
+
const is_linux = builtin.target.os.tag == .linux;
|
|
8
8
|
|
|
9
9
|
const config = @import("config.zig");
|
|
10
10
|
const log = std.log.scoped(.message_bus);
|
|
@@ -54,8 +54,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
54
54
|
|
|
55
55
|
/// The callback to be called when a message is received. Use set_on_message() to set
|
|
56
56
|
/// with type safety for the context pointer.
|
|
57
|
-
on_message_callback: ?fn (context: ?*
|
|
58
|
-
on_message_context: ?*
|
|
57
|
+
on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
|
|
58
|
+
on_message_context: ?*anyopaque = null,
|
|
59
59
|
|
|
60
60
|
/// This slice is allocated with a fixed size in the init function and never reallocated.
|
|
61
61
|
connections: []Connection,
|
|
@@ -75,7 +75,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
75
75
|
|
|
76
76
|
/// Initialize the MessageBus for the given cluster, configuration and replica/client process.
|
|
77
77
|
pub fn init(
|
|
78
|
-
allocator:
|
|
78
|
+
allocator: mem.Allocator,
|
|
79
79
|
cluster: u32,
|
|
80
80
|
configuration: []std.net.Address,
|
|
81
81
|
process: switch (process_type) {
|
|
@@ -124,7 +124,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
124
124
|
|
|
125
125
|
// Pre-allocate enough memory to hold all possible connections in the client map.
|
|
126
126
|
if (process_type == .replica) {
|
|
127
|
-
try bus.process.clients.
|
|
127
|
+
try bus.process.clients.ensureTotalCapacity(allocator, config.connections_max);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return bus;
|
|
@@ -140,7 +140,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
140
140
|
assert(bus.on_message_context == null);
|
|
141
141
|
|
|
142
142
|
bus.on_message_callback = struct {
|
|
143
|
-
fn wrapper(_context: ?*
|
|
143
|
+
fn wrapper(_context: ?*anyopaque, message: *Message) void {
|
|
144
144
|
on_message(@intToPtr(Context, @ptrToInt(_context)), message);
|
|
145
145
|
}
|
|
146
146
|
}.wrapper;
|
|
@@ -148,13 +148,13 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/// TODO This is required by the Client.
|
|
151
|
-
pub fn deinit(
|
|
151
|
+
pub fn deinit(_: *Self) void {}
|
|
152
152
|
|
|
153
153
|
fn init_tcp(address: std.net.Address) !os.socket_t {
|
|
154
|
-
const fd = try
|
|
154
|
+
const fd = try IO.openSocket(
|
|
155
155
|
address.any.family,
|
|
156
|
-
os.
|
|
157
|
-
os.
|
|
156
|
+
os.SOCK.STREAM,
|
|
157
|
+
os.IPPROTO.TCP,
|
|
158
158
|
);
|
|
159
159
|
errdefer os.close(fd);
|
|
160
160
|
|
|
@@ -164,71 +164,61 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
164
164
|
}
|
|
165
165
|
}.set;
|
|
166
166
|
|
|
167
|
-
// darwin doesn't support os.MSG_NOSIGNAL, but instead a socket option to avoid SIGPIPE.
|
|
168
|
-
if (is_darwin) {
|
|
169
|
-
try set(fd, os.SOL_SOCKET, os.SO_NOSIGPIPE, 1);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
167
|
// Set tcp recv buffer size
|
|
173
168
|
if (config.tcp_rcvbuf > 0) rcvbuf: {
|
|
174
|
-
if (
|
|
169
|
+
if (is_linux) {
|
|
175
170
|
// Requires CAP_NET_ADMIN privilege (settle for SO_RCVBUF in case of an EPERM):
|
|
176
|
-
if (set(fd, os.
|
|
171
|
+
if (set(fd, os.SOL.SOCKET, os.SO.RCVBUFFORCE, config.tcp_rcvbuf)) |_| {
|
|
177
172
|
break :rcvbuf;
|
|
178
173
|
} else |err| switch (err) {
|
|
179
174
|
error.PermissionDenied => {},
|
|
180
175
|
else => |e| return e,
|
|
181
176
|
}
|
|
182
177
|
}
|
|
183
|
-
try set(fd, os.
|
|
178
|
+
try set(fd, os.SOL.SOCKET, os.SO.RCVBUF, config.tcp_rcvbuf);
|
|
184
179
|
}
|
|
185
180
|
|
|
186
181
|
// Set tcp send buffer size
|
|
187
182
|
if (config.tcp_sndbuf > 0) sndbuf: {
|
|
188
|
-
if (
|
|
183
|
+
if (is_linux) {
|
|
189
184
|
// Requires CAP_NET_ADMIN privilege (settle for SO_SNDBUF in case of an EPERM):
|
|
190
|
-
if (set(fd, os.
|
|
185
|
+
if (set(fd, os.SOL.SOCKET, os.SO.SNDBUFFORCE, config.tcp_sndbuf)) |_| {
|
|
191
186
|
break :sndbuf;
|
|
192
187
|
} else |err| switch (err) {
|
|
193
188
|
error.PermissionDenied => {},
|
|
194
189
|
else => |e| return e,
|
|
195
190
|
}
|
|
196
191
|
}
|
|
197
|
-
try set(fd, os.
|
|
192
|
+
try set(fd, os.SOL.SOCKET, os.SO.SNDBUF, config.tcp_sndbuf);
|
|
198
193
|
}
|
|
199
194
|
|
|
200
195
|
// Set tcp keep alive
|
|
201
196
|
if (config.tcp_keepalive) {
|
|
202
|
-
try set(fd, os.
|
|
203
|
-
if (
|
|
204
|
-
try set(fd, os.
|
|
205
|
-
try set(fd, os.
|
|
206
|
-
try set(fd, os.
|
|
197
|
+
try set(fd, os.SOL.SOCKET, os.SO.KEEPALIVE, 1);
|
|
198
|
+
if (is_linux) {
|
|
199
|
+
try set(fd, os.IPPROTO.TCP, os.TCP_KEEPIDLE, config.tcp_keepidle);
|
|
200
|
+
try set(fd, os.IPPROTO.TCP, os.TCP_KEEPINTVL, config.tcp_keepintvl);
|
|
201
|
+
try set(fd, os.IPPROTO.TCP, os.TCP_KEEPCNT, config.tcp_keepcnt);
|
|
207
202
|
}
|
|
208
203
|
}
|
|
209
204
|
|
|
210
205
|
// Set tcp user timeout
|
|
211
206
|
if (config.tcp_user_timeout > 0) {
|
|
212
|
-
if (
|
|
213
|
-
try set(fd, os.
|
|
207
|
+
if (is_linux) {
|
|
208
|
+
try set(fd, os.IPPROTO.TCP, os.TCP_USER_TIMEOUT, config.tcp_user_timeout);
|
|
214
209
|
}
|
|
215
210
|
}
|
|
216
211
|
|
|
217
212
|
// Set tcp no-delay
|
|
218
213
|
if (config.tcp_nodelay) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
null;
|
|
225
|
-
|
|
226
|
-
if (TCP_NODELAY) |tcp_nodelay| {
|
|
227
|
-
try set(fd, os.IPPROTO_TCP, tcp_nodelay, 1);
|
|
228
|
-
}
|
|
214
|
+
// TODO: use the version in the zig standard library when upgrading to 0.9
|
|
215
|
+
// https://github.com/rust-lang/libc/search?q=TCP_NODELAY
|
|
216
|
+
// https://github.com/ziglang/zig/search?q=TCP_NODELAY
|
|
217
|
+
const TCP_NODELAY: u32 = 1;
|
|
218
|
+
try set(fd, os.IPPROTO.TCP, TCP_NODELAY, 1);
|
|
229
219
|
}
|
|
230
220
|
|
|
231
|
-
try set(fd, os.
|
|
221
|
+
try set(fd, os.SOL.SOCKET, os.SO.REUSEADDR, 1);
|
|
232
222
|
try os.bind(fd, &address.any, address.getOsSockLen());
|
|
233
223
|
try os.listen(fd, config.tcp_backlog);
|
|
234
224
|
|
|
@@ -289,7 +279,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
289
279
|
if (connection.state == .terminating) return;
|
|
290
280
|
}
|
|
291
281
|
|
|
292
|
-
log.
|
|
282
|
+
log.info("all connections in use but not all replicas are connected, " ++
|
|
293
283
|
"attempting to disconnect a client", .{});
|
|
294
284
|
for (bus.connections) |*connection| {
|
|
295
285
|
if (connection.peer == .client) {
|
|
@@ -298,7 +288,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
298
288
|
}
|
|
299
289
|
}
|
|
300
290
|
|
|
301
|
-
log.
|
|
291
|
+
log.info("failed to disconnect a client as no peer was a known client, " ++
|
|
302
292
|
"attempting to disconnect an unknown peer.", .{});
|
|
303
293
|
for (bus.connections) |*connection| {
|
|
304
294
|
if (connection.peer == .unknown) {
|
|
@@ -332,7 +322,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
332
322
|
on_accept,
|
|
333
323
|
&bus.process.accept_completion,
|
|
334
324
|
bus.process.accept_fd,
|
|
335
|
-
sock_flags,
|
|
336
325
|
);
|
|
337
326
|
}
|
|
338
327
|
|
|
@@ -341,6 +330,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
341
330
|
completion: *IO.Completion,
|
|
342
331
|
result: IO.AcceptError!os.socket_t,
|
|
343
332
|
) void {
|
|
333
|
+
_ = completion;
|
|
334
|
+
|
|
344
335
|
comptime assert(process_type == .replica);
|
|
345
336
|
assert(bus.process.accept_connection != null);
|
|
346
337
|
defer bus.process.accept_connection = null;
|
|
@@ -459,7 +450,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
459
450
|
// The first replica's network address family determines the
|
|
460
451
|
// family for all other replicas:
|
|
461
452
|
const family = bus.configuration[0].any.family;
|
|
462
|
-
connection.fd =
|
|
453
|
+
connection.fd = IO.openSocket(family, os.SOCK.STREAM, os.IPPROTO.TCP) catch return;
|
|
463
454
|
connection.peer = .{ .replica = replica };
|
|
464
455
|
connection.state = .connecting;
|
|
465
456
|
bus.connections_used += 1;
|
|
@@ -469,7 +460,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
469
460
|
|
|
470
461
|
var attempts = &bus.replicas_connect_attempts[replica];
|
|
471
462
|
const ms = vsr.exponential_backoff_with_jitter(
|
|
472
|
-
|
|
463
|
+
bus.prng.random(),
|
|
473
464
|
config.connection_delay_min_ms,
|
|
474
465
|
config.connection_delay_max_ms,
|
|
475
466
|
attempts.*,
|
|
@@ -600,7 +591,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
600
591
|
connection.send_queue.push(message.ref()) catch |err| switch (err) {
|
|
601
592
|
error.NoSpaceLeft => {
|
|
602
593
|
bus.unref(message);
|
|
603
|
-
log.
|
|
594
|
+
log.info("message queue for peer {} full, dropping message", .{
|
|
604
595
|
connection.peer,
|
|
605
596
|
});
|
|
606
597
|
return;
|
|
@@ -633,12 +624,12 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
633
624
|
//
|
|
634
625
|
// TODO: Investigate differences between shutdown() on Linux vs Darwin.
|
|
635
626
|
// Especially how this interacts with our assumptions around pending I/O.
|
|
636
|
-
const rc = os.system.shutdown(connection.fd, os.
|
|
627
|
+
const rc = os.system.shutdown(connection.fd, os.SHUT.RDWR);
|
|
637
628
|
switch (os.errno(rc)) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
629
|
+
.SUCCESS => {},
|
|
630
|
+
.BADF => unreachable,
|
|
631
|
+
.INVAL => unreachable,
|
|
632
|
+
.NOTCONN => {
|
|
642
633
|
// This should only happen if we for some reason decide to terminate
|
|
643
634
|
// a connection while a connect operation is in progress.
|
|
644
635
|
// This is fine though, we simply continue with the logic below and
|
|
@@ -653,7 +644,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
653
644
|
//assert(connection.recv_submitted);
|
|
654
645
|
//assert(!connection.send_submitted);
|
|
655
646
|
},
|
|
656
|
-
|
|
647
|
+
.NOTSOCK => unreachable,
|
|
657
648
|
else => |err| os.unexpectedErrno(err) catch {},
|
|
658
649
|
}
|
|
659
650
|
},
|
|
@@ -886,7 +877,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
886
877
|
&connection.recv_completion,
|
|
887
878
|
connection.fd,
|
|
888
879
|
connection.recv_message.?.buffer[connection.recv_progress..config.message_size_max],
|
|
889
|
-
if (is_darwin) 0 else os.MSG_NOSIGNAL,
|
|
890
880
|
);
|
|
891
881
|
}
|
|
892
882
|
|
|
@@ -929,7 +919,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
929
919
|
&connection.send_completion,
|
|
930
920
|
connection.fd,
|
|
931
921
|
message.buffer[connection.send_progress..message.header.size],
|
|
932
|
-
if (is_darwin) 0 else os.MSG_NOSIGNAL,
|
|
933
922
|
);
|
|
934
923
|
}
|
|
935
924
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
const assert = std.debug.assert;
|
|
3
4
|
const mem = std.mem;
|
|
4
5
|
|
|
@@ -51,7 +52,7 @@ pub const MessagePool = struct {
|
|
|
51
52
|
/// List of currently usused header-sized messages
|
|
52
53
|
header_only_free_list: ?*Message,
|
|
53
54
|
|
|
54
|
-
pub fn init(allocator:
|
|
55
|
+
pub fn init(allocator: mem.Allocator) error{OutOfMemory}!MessagePool {
|
|
55
56
|
var ret: MessagePool = .{
|
|
56
57
|
.free_list = null,
|
|
57
58
|
.header_only_free_list = null,
|
|
@@ -119,7 +120,7 @@ pub const MessagePool = struct {
|
|
|
119
120
|
pub fn unref(pool: *MessagePool, message: *Message) void {
|
|
120
121
|
message.references -= 1;
|
|
121
122
|
if (message.references == 0) {
|
|
122
|
-
if (
|
|
123
|
+
if (builtin.mode == .Debug) mem.set(u8, message.buffer, undefined);
|
|
123
124
|
if (message.header_only()) {
|
|
124
125
|
message.next = pool.header_only_free_list;
|
|
125
126
|
pool.header_only_free_list = message;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
const assert = std.debug.assert;
|
|
3
4
|
const mem = std.mem;
|
|
4
5
|
|
|
@@ -17,7 +18,7 @@ const output = std.log.scoped(.state_checker);
|
|
|
17
18
|
|
|
18
19
|
/// Set this to `false` if you want to see how literally everything works.
|
|
19
20
|
/// This will run much slower but will trace all logic across the cluster.
|
|
20
|
-
const log_state_transitions_only =
|
|
21
|
+
const log_state_transitions_only = builtin.mode != .Debug;
|
|
21
22
|
|
|
22
23
|
/// You can fine tune your log levels even further (debug/info/notice/warn/err/crit/alert/emerg):
|
|
23
24
|
pub const log_level: std.log.Level = if (log_state_transitions_only) .info else .debug;
|
|
@@ -40,13 +41,13 @@ pub fn main() !void {
|
|
|
40
41
|
break :seed_from_arg parse_seed(arg_two);
|
|
41
42
|
};
|
|
42
43
|
|
|
43
|
-
if (
|
|
44
|
+
if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
|
|
44
45
|
// We do not support ReleaseFast or ReleaseSmall because they disable assertions.
|
|
45
46
|
@panic("the simulator must be run with -OReleaseSafe");
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
if (seed == seed_random) {
|
|
49
|
-
if (
|
|
50
|
+
if (builtin.mode != .ReleaseSafe) {
|
|
50
51
|
// If no seed is provided, than Debug is too slow and ReleaseSafe is much faster.
|
|
51
52
|
@panic("no seed provided: the simulator must be run with -OReleaseSafe");
|
|
52
53
|
}
|
|
@@ -56,53 +57,53 @@ pub fn main() !void {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
var prng = std.rand.DefaultPrng.init(seed);
|
|
59
|
-
const random =
|
|
60
|
+
const random = prng.random();
|
|
60
61
|
|
|
61
|
-
const replica_count = 1 +
|
|
62
|
-
const client_count = 1 +
|
|
62
|
+
const replica_count = 1 + random.uintLessThan(u8, config.replicas_max);
|
|
63
|
+
const client_count = 1 + random.uintLessThan(u8, config.clients_max);
|
|
63
64
|
const node_count = replica_count + client_count;
|
|
64
65
|
|
|
65
66
|
const ticks_max = 100_000_000;
|
|
66
67
|
const transitions_max = config.journal_size_max / config.message_size_max;
|
|
67
|
-
const request_probability = 1 +
|
|
68
|
-
const idle_on_probability =
|
|
69
|
-
const idle_off_probability = 10 +
|
|
68
|
+
const request_probability = 1 + random.uintLessThan(u8, 99);
|
|
69
|
+
const idle_on_probability = random.uintLessThan(u8, 20);
|
|
70
|
+
const idle_off_probability = 10 + random.uintLessThan(u8, 10);
|
|
70
71
|
|
|
71
|
-
cluster = try Cluster.create(allocator,
|
|
72
|
+
cluster = try Cluster.create(allocator, random, .{
|
|
72
73
|
.cluster = 0,
|
|
73
74
|
.replica_count = replica_count,
|
|
74
75
|
.client_count = client_count,
|
|
75
|
-
.seed =
|
|
76
|
+
.seed = random.int(u64),
|
|
76
77
|
.network_options = .{
|
|
77
78
|
.packet_simulator_options = .{
|
|
78
79
|
.replica_count = replica_count,
|
|
79
80
|
.client_count = client_count,
|
|
80
81
|
.node_count = node_count,
|
|
81
82
|
|
|
82
|
-
.seed =
|
|
83
|
-
.one_way_delay_mean = 3 +
|
|
84
|
-
.one_way_delay_min =
|
|
85
|
-
.packet_loss_probability =
|
|
86
|
-
.path_maximum_capacity = 20 +
|
|
87
|
-
.path_clog_duration_mean =
|
|
88
|
-
.path_clog_probability =
|
|
89
|
-
.packet_replay_probability =
|
|
83
|
+
.seed = random.int(u64),
|
|
84
|
+
.one_way_delay_mean = 3 + random.uintLessThan(u16, 10),
|
|
85
|
+
.one_way_delay_min = random.uintLessThan(u16, 3),
|
|
86
|
+
.packet_loss_probability = random.uintLessThan(u8, 30),
|
|
87
|
+
.path_maximum_capacity = 20 + random.uintLessThan(u8, 20),
|
|
88
|
+
.path_clog_duration_mean = random.uintLessThan(u16, 500),
|
|
89
|
+
.path_clog_probability = random.uintLessThan(u8, 2),
|
|
90
|
+
.packet_replay_probability = random.uintLessThan(u8, 50),
|
|
90
91
|
|
|
91
92
|
.partition_mode = random_partition_mode(random),
|
|
92
|
-
.partition_probability =
|
|
93
|
-
.unpartition_probability = 1 +
|
|
94
|
-
.partition_stability = 100 +
|
|
95
|
-
.unpartition_stability =
|
|
93
|
+
.partition_probability = random.uintLessThan(u8, 3),
|
|
94
|
+
.unpartition_probability = 1 + random.uintLessThan(u8, 10),
|
|
95
|
+
.partition_stability = 100 + random.uintLessThan(u32, 100),
|
|
96
|
+
.unpartition_stability = random.uintLessThan(u32, 20),
|
|
96
97
|
},
|
|
97
98
|
},
|
|
98
99
|
.storage_options = .{
|
|
99
|
-
.seed =
|
|
100
|
-
.read_latency_min =
|
|
101
|
-
.read_latency_mean = 3 +
|
|
102
|
-
.write_latency_min =
|
|
103
|
-
.write_latency_mean = 3 +
|
|
104
|
-
.read_fault_probability =
|
|
105
|
-
.write_fault_probability =
|
|
100
|
+
.seed = random.int(u64),
|
|
101
|
+
.read_latency_min = random.uintLessThan(u16, 3),
|
|
102
|
+
.read_latency_mean = 3 + random.uintLessThan(u16, 10),
|
|
103
|
+
.write_latency_min = random.uintLessThan(u16, 3),
|
|
104
|
+
.write_latency_mean = 3 + random.uintLessThan(u16, 10),
|
|
105
|
+
.read_fault_probability = random.uintLessThan(u8, 10),
|
|
106
|
+
.write_fault_probability = random.uintLessThan(u8, 10),
|
|
106
107
|
},
|
|
107
108
|
});
|
|
108
109
|
defer cluster.destroy();
|
|
@@ -206,7 +207,7 @@ pub fn main() !void {
|
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
if (cluster.state_checker.transitions < transitions_max) {
|
|
209
|
-
output.
|
|
210
|
+
output.err("you can reproduce this failure with seed={}", .{seed});
|
|
210
211
|
@panic("unable to complete transitions_max before ticks_max");
|
|
211
212
|
}
|
|
212
213
|
|
|
@@ -216,13 +217,13 @@ pub fn main() !void {
|
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
/// Returns true, `p` percent of the time, else false.
|
|
219
|
-
fn chance(random:
|
|
220
|
+
fn chance(random: std.rand.Random, p: u8) bool {
|
|
220
221
|
assert(p <= 100);
|
|
221
222
|
return random.uintLessThan(u8, 100) < p;
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
/// Returns the next argument for the simulator or null (if none available)
|
|
225
|
-
fn args_next(args: *std.process.ArgIterator, allocator:
|
|
226
|
+
fn args_next(args: *std.process.ArgIterator, allocator: std.mem.Allocator) ?[:0]const u8 {
|
|
226
227
|
const err_or_bytes = args.next(allocator) orelse return null;
|
|
227
228
|
return err_or_bytes catch @panic("Unable to extract next value from args");
|
|
228
229
|
}
|
|
@@ -232,7 +233,7 @@ fn on_change_replica(replica: *Replica) void {
|
|
|
232
233
|
cluster.state_checker.check_state(replica.replica);
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
fn send_request(random:
|
|
236
|
+
fn send_request(random: std.rand.Random) bool {
|
|
236
237
|
const client_index = random.uintLessThan(u8, cluster.options.client_count);
|
|
237
238
|
|
|
238
239
|
const client = &cluster.clients[client_index];
|
|
@@ -280,11 +281,14 @@ fn client_callback(
|
|
|
280
281
|
operation: StateMachine.Operation,
|
|
281
282
|
results: Client.Error![]const u8,
|
|
282
283
|
) void {
|
|
284
|
+
_ = operation;
|
|
285
|
+
_ = results catch unreachable;
|
|
286
|
+
|
|
283
287
|
assert(user_data == 0);
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
/// Returns a random partitioning mode, excluding .custom
|
|
287
|
-
fn random_partition_mode(random:
|
|
291
|
+
fn random_partition_mode(random: std.rand.Random) PartitionMode {
|
|
288
292
|
const typeInfo = @typeInfo(PartitionMode).Enum;
|
|
289
293
|
var enumAsInt = random.uintAtMost(typeInfo.tag_type, typeInfo.fields.len - 2);
|
|
290
294
|
if (enumAsInt >= @enumToInt(PartitionMode.custom)) enumAsInt += 1;
|
|
@@ -310,8 +314,8 @@ pub fn log(
|
|
|
310
314
|
const prefix = if (log_state_transitions_only) "" else prefix_default;
|
|
311
315
|
|
|
312
316
|
// Print the message to stdout, silently ignoring any errors
|
|
313
|
-
const held = std.debug.getStderrMutex().acquire();
|
|
314
|
-
defer held.release();
|
|
315
317
|
const stderr = std.io.getStdErr().writer();
|
|
318
|
+
std.debug.getStderrMutex().lock();
|
|
319
|
+
defer std.debug.getStderrMutex().unlock();
|
|
316
320
|
nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
|
|
317
321
|
}
|