tigerbeetle-node 0.4.2 → 0.5.2

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 (58) hide show
  1. package/README.md +19 -5
  2. package/dist/benchmark.js.map +1 -1
  3. package/dist/index.d.ts +18 -16
  4. package/dist/index.js +35 -13
  5. package/dist/index.js.map +1 -1
  6. package/dist/test.js +12 -0
  7. package/dist/test.js.map +1 -1
  8. package/package.json +2 -2
  9. package/scripts/postinstall.sh +2 -2
  10. package/src/benchmark.ts +2 -2
  11. package/src/index.ts +29 -4
  12. package/src/node.zig +120 -17
  13. package/src/test.ts +14 -0
  14. package/src/tigerbeetle/scripts/install.sh +1 -1
  15. package/src/tigerbeetle/scripts/install_zig.bat +109 -0
  16. package/src/tigerbeetle/scripts/install_zig.sh +4 -2
  17. package/src/tigerbeetle/scripts/lint.zig +8 -2
  18. package/src/tigerbeetle/scripts/vopr.bat +48 -0
  19. package/src/tigerbeetle/src/benchmark.zig +10 -8
  20. package/src/tigerbeetle/src/cli.zig +6 -4
  21. package/src/tigerbeetle/src/config.zig +2 -2
  22. package/src/tigerbeetle/src/demo.zig +119 -89
  23. package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
  24. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
  25. package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
  26. package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
  27. package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
  28. package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
  29. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +7 -0
  30. package/src/tigerbeetle/src/io/benchmark.zig +238 -0
  31. package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +89 -124
  32. package/src/tigerbeetle/src/io/linux.zig +933 -0
  33. package/src/tigerbeetle/src/io/test.zig +621 -0
  34. package/src/tigerbeetle/src/io.zig +7 -1328
  35. package/src/tigerbeetle/src/main.zig +18 -10
  36. package/src/tigerbeetle/src/message_bus.zig +43 -60
  37. package/src/tigerbeetle/src/message_pool.zig +3 -2
  38. package/src/tigerbeetle/src/ring_buffer.zig +135 -68
  39. package/src/tigerbeetle/src/simulator.zig +41 -37
  40. package/src/tigerbeetle/src/state_machine.zig +851 -26
  41. package/src/tigerbeetle/src/storage.zig +49 -46
  42. package/src/tigerbeetle/src/test/cluster.zig +2 -2
  43. package/src/tigerbeetle/src/test/message_bus.zig +6 -6
  44. package/src/tigerbeetle/src/test/network.zig +3 -3
  45. package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
  46. package/src/tigerbeetle/src/test/state_checker.zig +2 -2
  47. package/src/tigerbeetle/src/test/state_machine.zig +4 -0
  48. package/src/tigerbeetle/src/test/storage.zig +39 -19
  49. package/src/tigerbeetle/src/test/time.zig +2 -2
  50. package/src/tigerbeetle/src/tigerbeetle.zig +6 -129
  51. package/src/tigerbeetle/src/time.zig +6 -5
  52. package/src/tigerbeetle/src/vsr/client.zig +11 -11
  53. package/src/tigerbeetle/src/vsr/clock.zig +26 -43
  54. package/src/tigerbeetle/src/vsr/journal.zig +7 -6
  55. package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
  56. package/src/tigerbeetle/src/vsr/replica.zig +51 -48
  57. package/src/tigerbeetle/src/vsr.zig +24 -20
  58. package/src/translate.zig +55 -55
@@ -1,11 +1,12 @@
1
1
  const std = @import("std");
2
+ const builtin = @import("builtin");
2
3
  const os = std.os;
3
4
  const Allocator = std.mem.Allocator;
4
5
  const assert = std.debug.assert;
5
6
  const log = std.log.scoped(.storage);
6
7
 
7
8
  const IO = @import("io.zig").IO;
8
- const is_darwin = std.Target.current.isDarwin();
9
+ const is_darwin = builtin.target.isDarwin();
9
10
 
10
11
  const config = @import("config.zig");
11
12
  const vsr = @import("vsr.zig");
@@ -90,7 +91,7 @@ pub const Storage = struct {
90
91
  buffer: []u8,
91
92
  offset: u64,
92
93
  ) void {
93
- self.assert_alignment(buffer, offset);
94
+ assert_alignment(buffer, offset);
94
95
 
95
96
  read.* = .{
96
97
  .completion = undefined,
@@ -189,7 +190,7 @@ pub const Storage = struct {
189
190
  error.Unseekable,
190
191
  error.Unexpected,
191
192
  => {
192
- log.emerg(
193
+ log.err(
193
194
  "impossible read: offset={} buffer.len={} error={s}",
194
195
  .{ read.offset, read.buffer.len, @errorName(err) },
195
196
  );
@@ -201,7 +202,7 @@ pub const Storage = struct {
201
202
  // We tried to read more than there really is available to read.
202
203
  // In other words, we thought we could read beyond the end of the file descriptor.
203
204
  // This can happen if the data file inode `size` was truncated or corrupted.
204
- log.emerg(
205
+ log.err(
205
206
  "short read: buffer.len={} offset={} bytes_read={}",
206
207
  .{ read.offset, read.buffer.len, bytes_read },
207
208
  );
@@ -227,7 +228,7 @@ pub const Storage = struct {
227
228
  buffer: []const u8,
228
229
  offset: u64,
229
230
  ) void {
230
- self.assert_alignment(buffer, offset);
231
+ assert_alignment(buffer, offset);
231
232
 
232
233
  write.* = .{
233
234
  .completion = undefined,
@@ -262,7 +263,7 @@ pub const Storage = struct {
262
263
  // TODO: It seems like it might be possible for some filesystems to return ETIMEDOUT
263
264
  // here. Consider handling this without panicking.
264
265
  else => {
265
- log.emerg(
266
+ log.err(
266
267
  "impossible write: offset={} buffer.len={} error={s}",
267
268
  .{ write.offset, write.buffer.len, @errorName(err) },
268
269
  );
@@ -295,7 +296,7 @@ pub const Storage = struct {
295
296
  /// If this is not the case, then the underlying syscall will return EINVAL.
296
297
  /// We check this only at the start of a read or write because the physical sector size may be
297
298
  /// less than our logical sector size so that partial IOs then leave us no longer aligned.
298
- fn assert_alignment(self: *Storage, buffer: []const u8, offset: u64) void {
299
+ fn assert_alignment(buffer: []const u8, offset: u64) void {
299
300
  assert(@ptrToInt(buffer.ptr) % config.sector_size == 0);
300
301
  assert(buffer.len % config.sector_size == 0);
301
302
  assert(offset % config.sector_size == 0);
@@ -330,17 +331,17 @@ pub const Storage = struct {
330
331
  // TODO Use O_EXCL when opening as a block device to obtain a mandatory exclusive lock.
331
332
  // This is much stronger than an advisory exclusive lock, and is required on some platforms.
332
333
 
333
- var flags: u32 = os.O_CLOEXEC | os.O_RDWR | os.O_DSYNC;
334
+ var flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.DSYNC;
334
335
  var mode: os.mode_t = 0;
335
336
 
336
337
  // TODO Document this and investigate whether this is in fact correct to set here.
337
- if (@hasDecl(os, "O_LARGEFILE")) flags |= os.O_LARGEFILE;
338
+ if (@hasDecl(os, "O_LARGEFILE")) flags |= os.O.LARGEFILE;
338
339
 
339
340
  var direct_io_supported = false;
340
341
  if (config.direct_io) {
341
342
  direct_io_supported = try Storage.fs_supports_direct_io(dir_fd);
342
343
  if (direct_io_supported) {
343
- if (!is_darwin) flags |= os.O_DIRECT;
344
+ if (!is_darwin) flags |= os.O.DIRECT;
344
345
  } else if (config.deployment_environment == .development) {
345
346
  log.warn("file system does not support Direct I/O", .{});
346
347
  } else {
@@ -352,15 +353,15 @@ pub const Storage = struct {
352
353
 
353
354
  if (must_create) {
354
355
  log.info("creating \"{s}\"...", .{relative_path});
355
- flags |= os.O_CREAT;
356
- flags |= os.O_EXCL;
356
+ flags |= os.O.CREAT;
357
+ flags |= os.O.EXCL;
357
358
  mode = 0o666;
358
359
  } else {
359
360
  log.info("opening \"{s}\"...", .{relative_path});
360
361
  }
361
362
 
362
363
  // This is critical as we rely on O_DSYNC for fsync() whenever we write to the file:
363
- assert((flags & os.O_DSYNC) > 0);
364
+ assert((flags & os.O.DSYNC) > 0);
364
365
 
365
366
  // Be careful with openat(2): "If pathname is absolute, then dirfd is ignored." (man page)
366
367
  assert(!std.fs.path.isAbsolute(relative_path));
@@ -372,12 +373,12 @@ pub const Storage = struct {
372
373
 
373
374
  // On darwin, use F_NOCACHE on direct_io to disable the page cache as O_DIRECT doesn't exit.
374
375
  if (is_darwin and config.direct_io and direct_io_supported) {
375
- _ = try os.fcntl(fd, os.F_NOCACHE, 1);
376
+ _ = try os.fcntl(fd, os.F.NOCACHE, 1);
376
377
  }
377
378
 
378
379
  // Obtain an advisory exclusive lock that works only if all processes actually use flock().
379
380
  // LOCK_NB means that we want to fail the lock without waiting if another process has it.
380
- os.flock(fd, os.LOCK_EX | os.LOCK_NB) catch |err| switch (err) {
381
+ os.flock(fd, os.LOCK.EX | os.LOCK.NB) catch |err| switch (err) {
381
382
  error.WouldBlock => @panic("another process holds the data file lock"),
382
383
  else => return err,
383
384
  };
@@ -411,7 +412,7 @@ pub const Storage = struct {
411
412
  Storage.fallocate(fd, 0, 0, @intCast(i64, size)) catch |err| switch (err) {
412
413
  error.OperationNotSupported => {
413
414
  log.warn("file system does not support fallocate(), an ENOSPC will panic", .{});
414
- log.notice("allocating by writing to the last sector of the file instead...", .{});
415
+ log.info("allocating by writing to the last sector of the file instead...", .{});
415
416
 
416
417
  const sector_size = config.sector_size;
417
418
  const sector: [sector_size]u8 align(sector_size) = [_]u8{0} ** sector_size;
@@ -436,6 +437,8 @@ pub const Storage = struct {
436
437
  const F_ALLOCATEALL = 0x4; // allocate all or nothing
437
438
  const F_PEOFPOSMODE = 3; // use relative offset from the seek pos mode
438
439
  const F_VOLPOSMODE = 4; // use the specified volume offset
440
+ _ = F_VOLPOSMODE;
441
+
439
442
  const fstore_t = extern struct {
440
443
  fst_flags: c_uint,
441
444
  fst_posmode: c_int,
@@ -453,24 +456,24 @@ pub const Storage = struct {
453
456
  };
454
457
 
455
458
  // try to pre-allocate contiguous space and fall back to default non-continugous
456
- var res = os.system.fcntl(fd, os.F_PREALLOCATE, @ptrToInt(&store));
457
- if (os.errno(res) != 0) {
459
+ var res = os.system.fcntl(fd, os.F.PREALLOCATE, @ptrToInt(&store));
460
+ if (os.errno(res) != .SUCCESS) {
458
461
  store.fst_flags = F_ALLOCATEALL;
459
- res = os.system.fcntl(fd, os.F_PREALLOCATE, @ptrToInt(&store));
462
+ res = os.system.fcntl(fd, os.F.PREALLOCATE, @ptrToInt(&store));
460
463
  }
461
464
 
462
465
  switch (os.errno(res)) {
463
- 0 => {},
464
- os.EACCES => unreachable, // F_SETLK or F_SETSIZE of F_WRITEBOOTSTRAP
465
- os.EBADF => return error.FileDescriptorInvalid,
466
- os.EDEADLK => unreachable, // F_SETLKW
467
- os.EINTR => unreachable, // F_SETLKW
468
- os.EINVAL => return error.ArgumentsInvalid, // for F_PREALLOCATE (offset invalid)
469
- os.EMFILE => unreachable, // F_DUPFD or F_DUPED
470
- os.ENOLCK => unreachable, // F_SETLK or F_SETLKW
471
- os.EOVERFLOW => return error.FileTooBig,
472
- os.ESRCH => unreachable, // F_SETOWN
473
- os.EOPNOTSUPP => return error.OperationNotSupported, // not reported but need same error union
466
+ .SUCCESS => {},
467
+ .ACCES => unreachable, // F_SETLK or F_SETSIZE of F_WRITEBOOTSTRAP
468
+ .BADF => return error.FileDescriptorInvalid,
469
+ .DEADLK => unreachable, // F_SETLKW
470
+ .INTR => unreachable, // F_SETLKW
471
+ .INVAL => return error.ArgumentsInvalid, // for F_PREALLOCATE (offset invalid)
472
+ .MFILE => unreachable, // F_DUPFD or F_DUPED
473
+ .NOLCK => unreachable, // F_SETLK or F_SETLKW
474
+ .OVERFLOW => return error.FileTooBig,
475
+ .SRCH => unreachable, // F_SETOWN
476
+ .OPNOTSUPP => return error.OperationNotSupported, // not reported but need same error union
474
477
  else => |errno| return os.unexpectedErrno(errno),
475
478
  }
476
479
 
@@ -484,19 +487,19 @@ pub const Storage = struct {
484
487
  while (true) {
485
488
  const rc = os.linux.fallocate(fd, mode, offset, length);
486
489
  switch (os.linux.getErrno(rc)) {
487
- 0 => return,
488
- os.linux.EBADF => return error.FileDescriptorInvalid,
489
- os.linux.EFBIG => return error.FileTooBig,
490
- os.linux.EINTR => continue,
491
- os.linux.EINVAL => return error.ArgumentsInvalid,
492
- os.linux.EIO => return error.InputOutput,
493
- os.linux.ENODEV => return error.NoDevice,
494
- os.linux.ENOSPC => return error.NoSpaceLeft,
495
- os.linux.ENOSYS => return error.SystemOutdated,
496
- os.linux.EOPNOTSUPP => return error.OperationNotSupported,
497
- os.linux.EPERM => return error.PermissionDenied,
498
- os.linux.ESPIPE => return error.Unseekable,
499
- os.linux.ETXTBSY => return error.FileBusy,
490
+ .SUCCESS => return,
491
+ .BADF => return error.FileDescriptorInvalid,
492
+ .FBIG => return error.FileTooBig,
493
+ .INTR => continue,
494
+ .INVAL => return error.ArgumentsInvalid,
495
+ .IO => return error.InputOutput,
496
+ .NODEV => return error.NoDevice,
497
+ .NOSPC => return error.NoSpaceLeft,
498
+ .NOSYS => return error.SystemOutdated,
499
+ .OPNOTSUPP => return error.OperationNotSupported,
500
+ .PERM => return error.PermissionDenied,
501
+ .SPIPE => return error.Unseekable,
502
+ .TXTBSY => return error.FileBusy,
500
503
  else => |errno| return os.unexpectedErrno(errno),
501
504
  }
502
505
  }
@@ -509,18 +512,18 @@ pub const Storage = struct {
509
512
 
510
513
  const path = "fs_supports_direct_io";
511
514
  const dir = std.fs.Dir{ .fd = dir_fd };
512
- const fd = try os.openatZ(dir_fd, path, os.O_CLOEXEC | os.O_CREAT | os.O_TRUNC, 0o666);
515
+ const fd = try os.openatZ(dir_fd, path, os.O.CLOEXEC | os.O.CREAT | os.O.TRUNC, 0o666);
513
516
  defer os.close(fd);
514
517
  defer dir.deleteFile(path) catch {};
515
518
 
516
519
  // F_NOCACHE on darwin is the most similar option to O_DIRECT on linux.
517
520
  if (is_darwin) {
518
- _ = os.fcntl(fd, os.F_NOCACHE, 1) catch return false;
521
+ _ = os.fcntl(fd, os.F.NOCACHE, 1) catch return false;
519
522
  return true;
520
523
  }
521
524
 
522
525
  while (true) {
523
- const res = os.system.openat(dir_fd, path, os.O_CLOEXEC | os.O_RDONLY | os.O_DIRECT, 0);
526
+ const res = os.system.openat(dir_fd, path, os.O.CLOEXEC | os.O.RDONLY | os.O.DIRECT, 0);
524
527
  switch (os.linux.getErrno(res)) {
525
528
  0 => {
526
529
  os.close(@intCast(os.fd_t, res));
@@ -34,7 +34,7 @@ pub const ClusterOptions = struct {
34
34
  };
35
35
 
36
36
  pub const Cluster = struct {
37
- allocator: *mem.Allocator,
37
+ allocator: mem.Allocator,
38
38
  options: ClusterOptions,
39
39
 
40
40
  state_machines: []StateMachine,
@@ -49,7 +49,7 @@ pub const Cluster = struct {
49
49
  state_checker: StateChecker = undefined,
50
50
  on_change_state: fn (replica: *Replica) void = undefined,
51
51
 
52
- pub fn create(allocator: *mem.Allocator, prng: *std.rand.Random, options: ClusterOptions) !*Cluster {
52
+ pub fn create(allocator: mem.Allocator, prng: std.rand.Random, options: ClusterOptions) !*Cluster {
53
53
  const cluster = try allocator.create(Cluster);
54
54
  errdefer allocator.destroy(cluster);
55
55
 
@@ -25,11 +25,11 @@ pub const MessageBus = struct {
25
25
 
26
26
  /// The callback to be called when a message is received. Use set_on_message() to set
27
27
  /// with type safety for the context pointer.
28
- on_message_callback: ?fn (context: ?*c_void, message: *Message) void = null,
29
- on_message_context: ?*c_void = null,
28
+ on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
29
+ on_message_context: ?*anyopaque = null,
30
30
 
31
31
  pub fn init(
32
- allocator: *std.mem.Allocator,
32
+ allocator: std.mem.Allocator,
33
33
  cluster: u32,
34
34
  process: Process,
35
35
  network: *Network,
@@ -43,7 +43,7 @@ pub const MessageBus = struct {
43
43
  }
44
44
 
45
45
  /// TODO
46
- pub fn deinit(bus: *MessageBus) void {}
46
+ pub fn deinit(_: *MessageBus) void {}
47
47
 
48
48
  pub fn set_on_message(
49
49
  bus: *MessageBus,
@@ -52,14 +52,14 @@ pub const MessageBus = struct {
52
52
  comptime on_message: fn (context: Context, message: *Message) void,
53
53
  ) void {
54
54
  bus.on_message_callback = struct {
55
- fn wrapper(_context: ?*c_void, message: *Message) void {
55
+ fn wrapper(_context: ?*anyopaque, message: *Message) void {
56
56
  on_message(@intToPtr(Context, @ptrToInt(_context)), message);
57
57
  }
58
58
  }.wrapper;
59
59
  bus.on_message_context = context;
60
60
  }
61
61
 
62
- pub fn tick(self: *MessageBus) void {}
62
+ pub fn tick(_: *MessageBus) void {}
63
63
 
64
64
  pub fn get_message(bus: *MessageBus) ?*Message {
65
65
  return bus.pool.get_message();
@@ -38,7 +38,7 @@ pub const Network = struct {
38
38
  target: Process,
39
39
  };
40
40
 
41
- allocator: *std.mem.Allocator,
41
+ allocator: std.mem.Allocator,
42
42
 
43
43
  options: NetworkOptions,
44
44
  packet_simulator: PacketSimulator(Packet),
@@ -47,7 +47,7 @@ pub const Network = struct {
47
47
  processes: std.ArrayListUnmanaged(u128),
48
48
 
49
49
  pub fn init(
50
- allocator: *std.mem.Allocator,
50
+ allocator: std.mem.Allocator,
51
51
  replica_count: u8,
52
52
  client_count: u8,
53
53
  options: NetworkOptions,
@@ -136,7 +136,7 @@ pub const Network = struct {
136
136
 
137
137
  fn deliver_message(packet: Packet, path: PacketSimulatorPath) void {
138
138
  const network = packet.network;
139
- const source_bus = &network.busses.items[path.source];
139
+
140
140
  const target_bus = &network.busses.items[path.target];
141
141
 
142
142
  const message = target_bus.get_message() orelse {
@@ -88,7 +88,7 @@ pub fn PacketSimulator(comptime Packet: type) type {
88
88
 
89
89
  /// A send and receive path between each node in the network. We use the `path` function to
90
90
  /// index it.
91
- paths: []std.PriorityQueue(Data),
91
+ paths: []std.PriorityQueue(Data, void, Self.order_packets),
92
92
 
93
93
  /// We can arbitrary clog a path until a tick.
94
94
  path_clogged_till: []u64,
@@ -103,11 +103,11 @@ pub fn PacketSimulator(comptime Packet: type) type {
103
103
  replicas: []u8,
104
104
  stability: u32,
105
105
 
106
- pub fn init(allocator: *std.mem.Allocator, options: PacketSimulatorOptions) !Self {
106
+ pub fn init(allocator: std.mem.Allocator, options: PacketSimulatorOptions) !Self {
107
107
  assert(options.one_way_delay_mean >= options.one_way_delay_min);
108
108
  var self = Self{
109
109
  .paths = try allocator.alloc(
110
- std.PriorityQueue(Data),
110
+ std.PriorityQueue(Data, void, Self.order_packets),
111
111
  @as(usize, options.node_count) * options.node_count,
112
112
  ),
113
113
  .path_clogged_till = try allocator.alloc(
@@ -128,8 +128,8 @@ pub fn PacketSimulator(comptime Packet: type) type {
128
128
  }
129
129
 
130
130
  for (self.paths) |*queue| {
131
- queue.* = std.PriorityQueue(Data).init(allocator, Self.order_packets);
132
- try queue.ensureCapacity(options.path_maximum_capacity);
131
+ queue.* = std.PriorityQueue(Data, void, Self.order_packets).init(allocator, {});
132
+ try queue.ensureTotalCapacity(options.path_maximum_capacity);
133
133
  }
134
134
 
135
135
  for (self.path_clogged_till) |*clogged_till| {
@@ -139,7 +139,7 @@ pub fn PacketSimulator(comptime Packet: type) type {
139
139
  return self;
140
140
  }
141
141
 
142
- pub fn deinit(self: *Self, allocator: *std.mem.Allocator) void {
142
+ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
143
143
  for (self.paths) |*queue| {
144
144
  while (queue.popOrNull()) |*data| data.packet.deinit();
145
145
  queue.deinit();
@@ -147,12 +147,14 @@ pub fn PacketSimulator(comptime Packet: type) type {
147
147
  allocator.free(self.paths);
148
148
  }
149
149
 
150
- fn order_packets(a: Data, b: Data) math.Order {
150
+ fn order_packets(context: void, a: Data, b: Data) math.Order {
151
+ _ = context;
152
+
151
153
  return math.order(a.expiry, b.expiry);
152
154
  }
153
155
 
154
156
  fn should_drop(self: *Self) bool {
155
- return self.prng.random.uintAtMost(u8, 100) < self.options.packet_loss_probability;
157
+ return self.prng.random().uintAtMost(u8, 100) < self.options.packet_loss_probability;
156
158
  }
157
159
 
158
160
  fn path_index(self: *Self, path: Path) usize {
@@ -161,9 +163,8 @@ pub fn PacketSimulator(comptime Packet: type) type {
161
163
  return @as(usize, path.source) * self.options.node_count + path.target;
162
164
  }
163
165
 
164
- pub fn path_queue(self: *Self, path: Path) *std.PriorityQueue(Data) {
165
- var index = self.path_index(path);
166
- return &self.paths[@as(usize, path.source) * self.options.node_count + path.target];
166
+ pub fn path_queue(self: *Self, path: Path) *std.PriorityQueue(Data, void, Self.order_packets) {
167
+ return &self.paths[self.path_index(path)];
167
168
  }
168
169
 
169
170
  fn is_clogged(self: *Self, path: Path) bool {
@@ -171,7 +172,9 @@ pub fn PacketSimulator(comptime Packet: type) type {
171
172
  }
172
173
 
173
174
  fn should_clog(self: *Self, path: Path) bool {
174
- return self.prng.random.uintAtMost(u8, 100) < self.options.path_clog_probability;
175
+ _ = path;
176
+
177
+ return self.prng.random().uintAtMost(u8, 100) < self.options.path_clog_probability;
175
178
  }
176
179
 
177
180
  fn clog_for(self: *Self, path: Path, ticks: u64) void {
@@ -185,15 +188,15 @@ pub fn PacketSimulator(comptime Packet: type) type {
185
188
  }
186
189
 
187
190
  fn should_replay(self: *Self) bool {
188
- return self.prng.random.uintAtMost(u8, 100) < self.options.packet_replay_probability;
191
+ return self.prng.random().uintAtMost(u8, 100) < self.options.packet_replay_probability;
189
192
  }
190
193
 
191
194
  fn should_partition(self: *Self) bool {
192
- return self.prng.random.uintAtMost(u8, 100) < self.options.partition_probability;
195
+ return self.prng.random().uintAtMost(u8, 100) < self.options.partition_probability;
193
196
  }
194
197
 
195
198
  fn should_unpartition(self: *Self) bool {
196
- return self.prng.random.uintAtMost(u8, 100) < self.options.unpartition_probability;
199
+ return self.prng.random().uintAtMost(u8, 100) < self.options.unpartition_probability;
197
200
  }
198
201
 
199
202
  /// Return a value produced using an exponential distribution with
@@ -201,7 +204,7 @@ pub fn PacketSimulator(comptime Packet: type) type {
201
204
  fn one_way_delay(self: *Self) u64 {
202
205
  const min = self.options.one_way_delay_min;
203
206
  const mean = self.options.one_way_delay_mean;
204
- return min + @floatToInt(u64, @intToFloat(f64, mean - min) * self.prng.random.floatExp(f64));
207
+ return min + @floatToInt(u64, @intToFloat(f64, mean - min) * self.prng.random().floatExp(f64));
205
208
  }
206
209
 
207
210
  /// Partitions the network. Guaranteed to isolate at least one replica.
@@ -217,8 +220,8 @@ pub fn PacketSimulator(comptime Packet: type) type {
217
220
  .uniform_size => {
218
221
  // Exclude cases sz == 0 and sz == replica_count
219
222
  const sz =
220
- 1 + self.prng.random.uintAtMost(u8, self.options.replica_count - 2);
221
- self.prng.random.shuffle(u8, self.replicas);
223
+ 1 + self.prng.random().uintAtMost(u8, self.options.replica_count - 2);
224
+ self.prng.random().shuffle(u8, self.replicas);
222
225
  for (self.replicas) |r, i| {
223
226
  self.partition[r] = i < sz;
224
227
  }
@@ -226,18 +229,18 @@ pub fn PacketSimulator(comptime Packet: type) type {
226
229
  .uniform_partition => {
227
230
  var only_same = true;
228
231
  self.partition[0] =
229
- self.prng.random.uintLessThan(u8, 2) == 1;
232
+ self.prng.random().uintLessThan(u8, 2) == 1;
230
233
 
231
234
  var i: usize = 1;
232
235
  while (i < self.options.replica_count) : (i += 1) {
233
236
  self.partition[i] =
234
- self.prng.random.uintLessThan(u8, 2) == 1;
237
+ self.prng.random().uintLessThan(u8, 2) == 1;
235
238
  only_same =
236
239
  only_same and (self.partition[i - 1] == self.partition[i]);
237
240
  }
238
241
 
239
242
  if (only_same) {
240
- const n = self.prng.random.uintLessThan(u8, self.options.replica_count);
243
+ const n = self.prng.random().uintLessThan(u8, self.options.replica_count);
241
244
  self.partition[n] = true;
242
245
  }
243
246
  },
@@ -245,7 +248,7 @@ pub fn PacketSimulator(comptime Packet: type) type {
245
248
  for (self.replicas) |_, i| {
246
249
  self.partition[i] = false;
247
250
  }
248
- const n = self.prng.random.uintLessThan(u8, self.options.replica_count);
251
+ const n = self.prng.random().uintLessThan(u8, self.options.replica_count);
249
252
  self.partition[n] = true;
250
253
  },
251
254
  // Put your own partitioning logic here.
@@ -279,12 +282,12 @@ pub fn PacketSimulator(comptime Packet: type) type {
279
282
  if (self.is_partitioned) {
280
283
  if (self.should_unpartition()) {
281
284
  self.unpartition_network();
282
- log.alert("unpartitioned network: partition={d}", .{self.partition});
285
+ log.err("unpartitioned network: partition={d}", .{self.partition});
283
286
  }
284
287
  } else {
285
288
  if (self.options.replica_count > 1 and self.should_partition()) {
286
289
  self.partition_network();
287
- log.alert("partitioned network: partition={d}", .{self.partition});
290
+ log.err("partitioned network: partition={d}", .{self.partition});
288
291
  }
289
292
  }
290
293
  }
@@ -305,14 +308,14 @@ pub fn PacketSimulator(comptime Packet: type) type {
305
308
  self.replicas_are_in_different_partitions(from, to))
306
309
  {
307
310
  self.stats[@enumToInt(PacketStatistics.dropped_due_to_partition)] += 1;
308
- log.alert("dropped packet (different partitions): from={} to={}", .{ from, to });
311
+ log.err("dropped packet (different partitions): from={} to={}", .{ from, to });
309
312
  data.packet.deinit(path);
310
313
  continue;
311
314
  }
312
315
 
313
316
  if (self.should_drop()) {
314
317
  self.stats[@enumToInt(PacketStatistics.dropped)] += 1;
315
- log.alert("dropped packet from={} to={}.", .{ from, to });
318
+ log.err("dropped packet from={} to={}.", .{ from, to });
316
319
  data.packet.deinit(path);
317
320
  continue;
318
321
  }
@@ -336,7 +339,7 @@ pub fn PacketSimulator(comptime Packet: type) type {
336
339
  if (self.should_clog(reverse_path)) {
337
340
  log.debug("reverse path clogged", .{});
338
341
  const mean = @intToFloat(f64, self.options.path_clog_duration_mean);
339
- const ticks = @floatToInt(u64, mean * self.prng.random.floatExp(f64));
342
+ const ticks = @floatToInt(u64, mean * self.prng.random().floatExp(f64));
340
343
  self.clog_for(reverse_path, ticks);
341
344
  }
342
345
  }
@@ -352,10 +355,10 @@ pub fn PacketSimulator(comptime Packet: type) type {
352
355
  const queue = self.path_queue(path);
353
356
  var queue_length = queue.count();
354
357
  if (queue_length + 1 > queue.capacity()) {
355
- const index = self.prng.random.uintLessThanBiased(u64, queue_length);
358
+ const index = self.prng.random().uintLessThanBiased(u64, queue_length);
356
359
  const data = queue.removeIndex(index);
357
360
  data.packet.deinit(path);
358
- log.alert("submit_packet: {} reached capacity, dropped packet={}", .{
361
+ log.err("submit_packet: {} reached capacity, dropped packet={}", .{
359
362
  path,
360
363
  index,
361
364
  });
@@ -34,7 +34,7 @@ pub const StateChecker = struct {
34
34
  /// The number of times the cannonical state has been advanced.
35
35
  transitions: u64 = 0,
36
36
 
37
- pub fn init(allocator: *mem.Allocator, cluster: *Cluster) !StateChecker {
37
+ pub fn init(allocator: mem.Allocator, cluster: *Cluster) !StateChecker {
38
38
  const state = cluster.state_machines[0].state;
39
39
 
40
40
  var state_machine_states: [config.replicas_max]u128 = undefined;
@@ -86,7 +86,7 @@ pub const StateChecker = struct {
86
86
  // The replica has transitioned to state `b` that is not yet in the history.
87
87
  // Check if this is a valid new state based on all currently inflight client requests.
88
88
  for (state_checker.client_requests) |*queue| {
89
- if (queue.peek_ptr()) |input| {
89
+ if (queue.head_ptr()) |input| {
90
90
  if (b == StateMachine.hash(state_checker.state, std.mem.asBytes(input))) {
91
91
  const transitions_executed = state_checker.history.get(a).?;
92
92
  if (transitions_executed < state_checker.transitions) {
@@ -24,6 +24,10 @@ pub const StateMachine = struct {
24
24
  operation: Operation,
25
25
  input: []u8,
26
26
  ) void {
27
+ _ = state_machine;
28
+ _ = realtime;
29
+ _ = operation;
30
+ _ = input;
27
31
  // TODO: use realtime in some way to test the system
28
32
  }
29
33