tigerbeetle-node 0.5.0 → 0.6.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 (51) hide show
  1. package/README.md +3 -4
  2. package/package.json +2 -2
  3. package/scripts/postinstall.sh +2 -2
  4. package/src/node.zig +19 -27
  5. package/src/tigerbeetle/scripts/benchmark.bat +46 -0
  6. package/src/tigerbeetle/scripts/install.sh +1 -1
  7. package/src/tigerbeetle/scripts/install_zig.bat +4 -4
  8. package/src/tigerbeetle/scripts/install_zig.sh +4 -2
  9. package/src/tigerbeetle/scripts/lint.zig +8 -2
  10. package/src/tigerbeetle/scripts/vopr.sh +2 -2
  11. package/src/tigerbeetle/src/benchmark.zig +10 -12
  12. package/src/tigerbeetle/src/cli.zig +43 -20
  13. package/src/tigerbeetle/src/config.zig +26 -11
  14. package/src/tigerbeetle/src/demo.zig +119 -97
  15. package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
  16. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
  17. package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
  18. package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
  19. package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
  20. package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
  21. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +2 -3
  22. package/src/tigerbeetle/src/io/benchmark.zig +213 -0
  23. package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +259 -167
  24. package/src/tigerbeetle/src/io/linux.zig +1038 -0
  25. package/src/tigerbeetle/src/io/test.zig +643 -0
  26. package/src/tigerbeetle/src/io/windows.zig +1161 -0
  27. package/src/tigerbeetle/src/io.zig +9 -1328
  28. package/src/tigerbeetle/src/main.zig +28 -15
  29. package/src/tigerbeetle/src/message_bus.zig +78 -107
  30. package/src/tigerbeetle/src/message_pool.zig +65 -58
  31. package/src/tigerbeetle/src/ring_buffer.zig +7 -0
  32. package/src/tigerbeetle/src/simulator.zig +44 -40
  33. package/src/tigerbeetle/src/state_machine.zig +58 -27
  34. package/src/tigerbeetle/src/storage.zig +7 -234
  35. package/src/tigerbeetle/src/test/cluster.zig +5 -8
  36. package/src/tigerbeetle/src/test/message_bus.zig +10 -9
  37. package/src/tigerbeetle/src/test/network.zig +16 -19
  38. package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
  39. package/src/tigerbeetle/src/test/state_checker.zig +4 -3
  40. package/src/tigerbeetle/src/test/state_machine.zig +4 -0
  41. package/src/tigerbeetle/src/test/storage.zig +23 -19
  42. package/src/tigerbeetle/src/test/time.zig +2 -2
  43. package/src/tigerbeetle/src/tigerbeetle.zig +8 -128
  44. package/src/tigerbeetle/src/time.zig +61 -13
  45. package/src/tigerbeetle/src/vsr/client.zig +23 -37
  46. package/src/tigerbeetle/src/vsr/clock.zig +27 -44
  47. package/src/tigerbeetle/src/vsr/journal.zig +9 -12
  48. package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
  49. package/src/tigerbeetle/src/vsr/replica.zig +184 -204
  50. package/src/tigerbeetle/src/vsr.zig +287 -25
  51. package/src/translate.zig +55 -55
@@ -1,12 +1,11 @@
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
-
10
9
  const config = @import("config.zig");
11
10
  const vsr = @import("vsr.zig");
12
11
 
@@ -90,7 +89,7 @@ pub const Storage = struct {
90
89
  buffer: []u8,
91
90
  offset: u64,
92
91
  ) void {
93
- self.assert_alignment(buffer, offset);
92
+ assert_alignment(buffer, offset);
94
93
 
95
94
  read.* = .{
96
95
  .completion = undefined,
@@ -189,7 +188,7 @@ pub const Storage = struct {
189
188
  error.Unseekable,
190
189
  error.Unexpected,
191
190
  => {
192
- log.emerg(
191
+ log.err(
193
192
  "impossible read: offset={} buffer.len={} error={s}",
194
193
  .{ read.offset, read.buffer.len, @errorName(err) },
195
194
  );
@@ -201,7 +200,7 @@ pub const Storage = struct {
201
200
  // We tried to read more than there really is available to read.
202
201
  // In other words, we thought we could read beyond the end of the file descriptor.
203
202
  // This can happen if the data file inode `size` was truncated or corrupted.
204
- log.emerg(
203
+ log.err(
205
204
  "short read: buffer.len={} offset={} bytes_read={}",
206
205
  .{ read.offset, read.buffer.len, bytes_read },
207
206
  );
@@ -227,7 +226,7 @@ pub const Storage = struct {
227
226
  buffer: []const u8,
228
227
  offset: u64,
229
228
  ) void {
230
- self.assert_alignment(buffer, offset);
229
+ assert_alignment(buffer, offset);
231
230
 
232
231
  write.* = .{
233
232
  .completion = undefined,
@@ -262,7 +261,7 @@ pub const Storage = struct {
262
261
  // TODO: It seems like it might be possible for some filesystems to return ETIMEDOUT
263
262
  // here. Consider handling this without panicking.
264
263
  else => {
265
- log.emerg(
264
+ log.err(
266
265
  "impossible write: offset={} buffer.len={} error={s}",
267
266
  .{ write.offset, write.buffer.len, @errorName(err) },
268
267
  );
@@ -295,7 +294,7 @@ pub const Storage = struct {
295
294
  /// If this is not the case, then the underlying syscall will return EINVAL.
296
295
  /// We check this only at the start of a read or write because the physical sector size may be
297
296
  /// 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 {
297
+ fn assert_alignment(buffer: []const u8, offset: u64) void {
299
298
  assert(@ptrToInt(buffer.ptr) % config.sector_size == 0);
300
299
  assert(buffer.len % config.sector_size == 0);
301
300
  assert(offset % config.sector_size == 0);
@@ -306,230 +305,4 @@ pub const Storage = struct {
306
305
  assert(buffer.len > 0);
307
306
  assert(offset + buffer.len <= self.size);
308
307
  }
309
-
310
- // Static helper functions to handle data file creation/opening/allocation:
311
-
312
- /// Opens or creates a journal file:
313
- /// - For reading and writing.
314
- /// - For Direct I/O (if possible in development mode, but required in production mode).
315
- /// - Obtains an advisory exclusive lock to the file descriptor.
316
- /// - Allocates the file contiguously on disk if this is supported by the file system.
317
- /// - Ensures that the file data (and file inode in the parent directory) is durable on disk.
318
- /// The caller is responsible for ensuring that the parent directory inode is durable.
319
- /// - Verifies that the file size matches the expected file size before returning.
320
- pub fn open(
321
- dir_fd: os.fd_t,
322
- relative_path: [:0]const u8,
323
- size: u64,
324
- must_create: bool,
325
- ) !os.fd_t {
326
- assert(relative_path.len > 0);
327
- assert(size >= config.sector_size);
328
- assert(size % config.sector_size == 0);
329
-
330
- // TODO Use O_EXCL when opening as a block device to obtain a mandatory exclusive lock.
331
- // This is much stronger than an advisory exclusive lock, and is required on some platforms.
332
-
333
- var flags: u32 = os.O_CLOEXEC | os.O_RDWR | os.O_DSYNC;
334
- var mode: os.mode_t = 0;
335
-
336
- // 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
-
339
- var direct_io_supported = false;
340
- if (config.direct_io) {
341
- direct_io_supported = try Storage.fs_supports_direct_io(dir_fd);
342
- if (direct_io_supported) {
343
- if (!is_darwin) flags |= os.O_DIRECT;
344
- } else if (config.deployment_environment == .development) {
345
- log.warn("file system does not support Direct I/O", .{});
346
- } else {
347
- // We require Direct I/O for safety to handle fsync failure correctly, and therefore
348
- // panic in production if it is not supported.
349
- @panic("file system does not support Direct I/O");
350
- }
351
- }
352
-
353
- if (must_create) {
354
- log.info("creating \"{s}\"...", .{relative_path});
355
- flags |= os.O_CREAT;
356
- flags |= os.O_EXCL;
357
- mode = 0o666;
358
- } else {
359
- log.info("opening \"{s}\"...", .{relative_path});
360
- }
361
-
362
- // 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
-
365
- // Be careful with openat(2): "If pathname is absolute, then dirfd is ignored." (man page)
366
- assert(!std.fs.path.isAbsolute(relative_path));
367
- const fd = try os.openatZ(dir_fd, relative_path, flags, mode);
368
- // TODO Return a proper error message when the path exists or does not exist (init/start).
369
- errdefer os.close(fd);
370
-
371
- // TODO Check that the file is actually a file.
372
-
373
- // On darwin, use F_NOCACHE on direct_io to disable the page cache as O_DIRECT doesn't exit.
374
- if (is_darwin and config.direct_io and direct_io_supported) {
375
- _ = try os.fcntl(fd, os.F_NOCACHE, 1);
376
- }
377
-
378
- // Obtain an advisory exclusive lock that works only if all processes actually use flock().
379
- // 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
- error.WouldBlock => @panic("another process holds the data file lock"),
382
- else => return err,
383
- };
384
-
385
- // Ask the file system to allocate contiguous sectors for the file (if possible):
386
- // If the file system does not support `fallocate()`, then this could mean more seeks or a
387
- // panic if we run out of disk space (ENOSPC).
388
- if (must_create) try Storage.allocate(fd, size);
389
-
390
- // The best fsync strategy is always to fsync before reading because this prevents us from
391
- // making decisions on data that was never durably written by a previously crashed process.
392
- // We therefore always fsync when we open the path, also to wait for any pending O_DSYNC.
393
- // Thanks to Alex Miller from FoundationDB for diving into our source and pointing this out.
394
- try os.fsync(fd);
395
-
396
- // We fsync the parent directory to ensure that the file inode is durably written.
397
- // The caller is responsible for the parent directory inode stored under the grandparent.
398
- // We always do this when opening because we don't know if this was done before crashing.
399
- try os.fsync(dir_fd);
400
-
401
- const stat = try os.fstat(fd);
402
- if (stat.size != size) @panic("data file inode size was truncated or corrupted");
403
-
404
- return fd;
405
- }
406
-
407
- /// Allocates a file contiguously using fallocate() if supported.
408
- /// Alternatively, writes to the last sector so that at least the file size is correct.
409
- pub fn allocate(fd: os.fd_t, size: u64) !void {
410
- log.info("allocating {}...", .{std.fmt.fmtIntSizeBin(size)});
411
- Storage.fallocate(fd, 0, 0, @intCast(i64, size)) catch |err| switch (err) {
412
- error.OperationNotSupported => {
413
- 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
-
416
- const sector_size = config.sector_size;
417
- const sector: [sector_size]u8 align(sector_size) = [_]u8{0} ** sector_size;
418
-
419
- // Handle partial writes where the physical sector is less than a logical sector:
420
- const offset = size - sector.len;
421
- var written: usize = 0;
422
- while (written < sector.len) {
423
- written += try os.pwrite(fd, sector[written..], offset + written);
424
- }
425
- },
426
- else => return err,
427
- };
428
- }
429
-
430
- fn fallocate(fd: i32, mode: i32, offset: i64, length: i64) !void {
431
- // https://stackoverflow.com/a/11497568
432
- // https://api.kde.org/frameworks/kcoreaddons/html/posix__fallocate__mac_8h_source.html
433
- // http://hg.mozilla.org/mozilla-central/file/3d846420a907/xpcom/glue/FileUtils.cpp#l61
434
- if (is_darwin) {
435
- const F_ALLOCATECONTIG = 0x2; // allocate contiguous space
436
- const F_ALLOCATEALL = 0x4; // allocate all or nothing
437
- const F_PEOFPOSMODE = 3; // use relative offset from the seek pos mode
438
- const F_VOLPOSMODE = 4; // use the specified volume offset
439
- const fstore_t = extern struct {
440
- fst_flags: c_uint,
441
- fst_posmode: c_int,
442
- fst_offset: os.off_t,
443
- fst_length: os.off_t,
444
- fst_bytesalloc: os.off_t,
445
- };
446
-
447
- var store = fstore_t{
448
- .fst_flags = F_ALLOCATECONTIG | F_ALLOCATEALL,
449
- .fst_posmode = F_PEOFPOSMODE,
450
- .fst_offset = 0,
451
- .fst_length = offset + length,
452
- .fst_bytesalloc = 0,
453
- };
454
-
455
- // 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) {
458
- store.fst_flags = F_ALLOCATEALL;
459
- res = os.system.fcntl(fd, os.F_PREALLOCATE, @ptrToInt(&store));
460
- }
461
-
462
- 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
474
- else => |errno| return os.unexpectedErrno(errno),
475
- }
476
-
477
- // now actually perform the allocation
478
- return os.ftruncate(fd, @intCast(u64, length)) catch |err| switch (err) {
479
- error.AccessDenied => error.PermissionDenied,
480
- else => |e| e,
481
- };
482
- }
483
-
484
- while (true) {
485
- const rc = os.linux.fallocate(fd, mode, offset, length);
486
- 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,
500
- else => |errno| return os.unexpectedErrno(errno),
501
- }
502
- }
503
- }
504
-
505
- /// Detects whether the underlying file system for a given directory fd supports Direct I/O.
506
- /// Not all Linux file systems support `O_DIRECT`, e.g. a shared macOS volume.
507
- fn fs_supports_direct_io(dir_fd: std.os.fd_t) !bool {
508
- if (!@hasDecl(std.os, "O_DIRECT") and !is_darwin) return false;
509
-
510
- const path = "fs_supports_direct_io";
511
- 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);
513
- defer os.close(fd);
514
- defer dir.deleteFile(path) catch {};
515
-
516
- // F_NOCACHE on darwin is the most similar option to O_DIRECT on linux.
517
- if (is_darwin) {
518
- _ = os.fcntl(fd, os.F_NOCACHE, 1) catch return false;
519
- return true;
520
- }
521
-
522
- while (true) {
523
- const res = os.system.openat(dir_fd, path, os.O_CLOEXEC | os.O_RDONLY | os.O_DIRECT, 0);
524
- switch (os.linux.getErrno(res)) {
525
- 0 => {
526
- os.close(@intCast(os.fd_t, res));
527
- return true;
528
- },
529
- os.linux.EINTR => continue,
530
- os.linux.EINVAL => return false,
531
- else => |err| return os.unexpectedErrno(err),
532
- }
533
- }
534
- }
535
308
  };
@@ -6,7 +6,8 @@ const config = @import("../config.zig");
6
6
 
7
7
  const StateChecker = @import("state_checker.zig").StateChecker;
8
8
 
9
- const MessagePool = @import("../message_pool.zig").MessagePool;
9
+ const message_pool = @import("../message_pool.zig");
10
+ const MessagePool = message_pool.MessagePool;
10
11
  const Message = MessagePool.Message;
11
12
 
12
13
  const Network = @import("network.zig").Network;
@@ -34,7 +35,7 @@ pub const ClusterOptions = struct {
34
35
  };
35
36
 
36
37
  pub const Cluster = struct {
37
- allocator: *mem.Allocator,
38
+ allocator: mem.Allocator,
38
39
  options: ClusterOptions,
39
40
 
40
41
  state_machines: []StateMachine,
@@ -49,7 +50,7 @@ pub const Cluster = struct {
49
50
  state_checker: StateChecker = undefined,
50
51
  on_change_state: fn (replica: *Replica) void = undefined,
51
52
 
52
- pub fn create(allocator: *mem.Allocator, prng: *std.rand.Random, options: ClusterOptions) !*Cluster {
53
+ pub fn create(allocator: mem.Allocator, prng: std.rand.Random, options: ClusterOptions) !*Cluster {
53
54
  const cluster = try allocator.create(Cluster);
54
55
  errdefer allocator.destroy(cluster);
55
56
 
@@ -197,12 +198,8 @@ pub const Cluster = struct {
197
198
  var it = message_bus.pool.free_list;
198
199
  while (it) |message| : (it = message.next) messages_in_pool += 1;
199
200
  }
200
- {
201
- var it = message_bus.pool.header_only_free_list;
202
- while (it) |message| : (it = message.next) messages_in_pool += 1;
203
- }
204
201
 
205
- const total_messages = config.message_bus_messages_max + config.message_bus_headers_max;
202
+ const total_messages = message_pool.messages_max_replica;
206
203
  assert(messages_in_network + messages_in_pool == total_messages);
207
204
 
208
205
  replica.* = try Replica.init(
@@ -6,12 +6,13 @@ const config = @import("../config.zig");
6
6
  const MessagePool = @import("../message_pool.zig").MessagePool;
7
7
  const Message = MessagePool.Message;
8
8
  const Header = @import("../vsr.zig").Header;
9
+ const ProcessType = @import("../vsr.zig").ProcessType;
9
10
 
10
11
  const Network = @import("network.zig").Network;
11
12
 
12
13
  const log = std.log.scoped(.message_bus);
13
14
 
14
- pub const Process = union(enum) {
15
+ pub const Process = union(ProcessType) {
15
16
  replica: u8,
16
17
  client: u128,
17
18
  };
@@ -25,17 +26,17 @@ pub const MessageBus = struct {
25
26
 
26
27
  /// The callback to be called when a message is received. Use set_on_message() to set
27
28
  /// 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,
29
+ on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
30
+ on_message_context: ?*anyopaque = null,
30
31
 
31
32
  pub fn init(
32
- allocator: *std.mem.Allocator,
33
+ allocator: std.mem.Allocator,
33
34
  cluster: u32,
34
35
  process: Process,
35
36
  network: *Network,
36
37
  ) !MessageBus {
37
38
  return MessageBus{
38
- .pool = try MessagePool.init(allocator),
39
+ .pool = try MessagePool.init(allocator, @as(ProcessType, process)),
39
40
  .network = network,
40
41
  .cluster = cluster,
41
42
  .process = process,
@@ -43,7 +44,7 @@ pub const MessageBus = struct {
43
44
  }
44
45
 
45
46
  /// TODO
46
- pub fn deinit(bus: *MessageBus) void {}
47
+ pub fn deinit(_: *MessageBus) void {}
47
48
 
48
49
  pub fn set_on_message(
49
50
  bus: *MessageBus,
@@ -52,16 +53,16 @@ pub const MessageBus = struct {
52
53
  comptime on_message: fn (context: Context, message: *Message) void,
53
54
  ) void {
54
55
  bus.on_message_callback = struct {
55
- fn wrapper(_context: ?*c_void, message: *Message) void {
56
+ fn wrapper(_context: ?*anyopaque, message: *Message) void {
56
57
  on_message(@intToPtr(Context, @ptrToInt(_context)), message);
57
58
  }
58
59
  }.wrapper;
59
60
  bus.on_message_context = context;
60
61
  }
61
62
 
62
- pub fn tick(self: *MessageBus) void {}
63
+ pub fn tick(_: *MessageBus) void {}
63
64
 
64
- pub fn get_message(bus: *MessageBus) ?*Message {
65
+ pub fn get_message(bus: *MessageBus) *Message {
65
66
  return bus.pool.get_message();
66
67
  }
67
68
 
@@ -28,7 +28,7 @@ pub const Network = struct {
28
28
  message: *Message,
29
29
 
30
30
  pub fn deinit(packet: *const Packet, path: PacketSimulatorPath) void {
31
- const source_bus = &packet.network.busses.items[path.source];
31
+ const source_bus = &packet.network.buses.items[path.source];
32
32
  source_bus.unref(packet.message);
33
33
  }
34
34
  };
@@ -38,16 +38,16 @@ 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),
45
45
 
46
- busses: std.ArrayListUnmanaged(MessageBus),
46
+ buses: std.ArrayListUnmanaged(MessageBus),
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,
@@ -55,8 +55,8 @@ pub const Network = struct {
55
55
  const process_count = client_count + replica_count;
56
56
  assert(process_count <= std.math.maxInt(u8));
57
57
 
58
- var busses = try std.ArrayListUnmanaged(MessageBus).initCapacity(allocator, process_count);
59
- errdefer busses.deinit(allocator);
58
+ var buses = try std.ArrayListUnmanaged(MessageBus).initCapacity(allocator, process_count);
59
+ errdefer buses.deinit(allocator);
60
60
 
61
61
  var processes = try std.ArrayListUnmanaged(u128).initCapacity(allocator, process_count);
62
62
  errdefer processes.deinit(allocator);
@@ -71,18 +71,18 @@ pub const Network = struct {
71
71
  .allocator = allocator,
72
72
  .options = options,
73
73
  .packet_simulator = packet_simulator,
74
- .busses = busses,
74
+ .buses = buses,
75
75
  .processes = processes,
76
76
  };
77
77
  }
78
78
 
79
79
  pub fn deinit(network: *Network) void {
80
- // TODO: deinit the busses themselves when they gain a deinit()
81
- network.busses.deinit(network.allocator);
80
+ // TODO: deinit the buses themselves when they gain a deinit()
81
+ network.buses.deinit(network.allocator);
82
82
  network.processes.deinit(network.allocator);
83
83
  }
84
84
 
85
- /// Returns the address (index into Network.busses)
85
+ /// Returns the address (index into Network.buses)
86
86
  pub fn init_message_bus(network: *Network, cluster: u32, process: Process) !*MessageBus {
87
87
  const raw_process = switch (process) {
88
88
  .replica => |replica| replica,
@@ -97,9 +97,9 @@ pub const Network = struct {
97
97
  const bus = try MessageBus.init(network.allocator, cluster, process, network);
98
98
 
99
99
  network.processes.appendAssumeCapacity(raw_process);
100
- network.busses.appendAssumeCapacity(bus);
100
+ network.buses.appendAssumeCapacity(bus);
101
101
 
102
- return &network.busses.items[network.busses.items.len - 1];
102
+ return &network.buses.items[network.buses.items.len - 1];
103
103
  }
104
104
 
105
105
  pub fn send_message(network: *Network, message: *Message, path: Path) void {
@@ -131,18 +131,15 @@ pub const Network = struct {
131
131
  }
132
132
 
133
133
  pub fn get_message_bus(network: *Network, process: Process) *MessageBus {
134
- return &network.busses.items[network.process_to_address(process)];
134
+ return &network.buses.items[network.process_to_address(process)];
135
135
  }
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];
140
- const target_bus = &network.busses.items[path.target];
141
139
 
142
- const message = target_bus.get_message() orelse {
143
- log.debug("deliver_message: target message bus has no free messages, dropping", .{});
144
- return;
145
- };
140
+ const target_bus = &network.buses.items[path.target];
141
+
142
+ const message = target_bus.get_message();
146
143
  defer target_bus.unref(message);
147
144
 
148
145
  std.mem.copy(u8, message.buffer, packet.message.buffer);