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
@@ -20,12 +20,24 @@ 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 arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
24
- const arena = &arena_allocator.allocator;
23
+ var io = try IO.init(128, 0);
24
+ defer io.deinit();
25
+
26
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
27
+ defer arena.deinit();
28
+
29
+ const allocator = arena.allocator();
25
30
 
26
- switch (cli.parse_args(arena)) {
27
- .init => |args| try init(arena, args.cluster, args.replica, args.dir_fd),
28
- .start => |args| try start(arena, args.cluster, args.replica, args.addresses, args.dir_fd),
31
+ switch (try cli.parse_args(allocator)) {
32
+ .init => |args| try init(&io, args.cluster, args.replica, args.dir_fd),
33
+ .start => |args| try start(
34
+ &io,
35
+ allocator,
36
+ args.cluster,
37
+ args.replica,
38
+ args.addresses,
39
+ args.dir_fd,
40
+ ),
29
41
  }
30
42
  }
31
43
 
@@ -34,14 +46,14 @@ const filename_fmt = "cluster_{d:0>10}_replica_{d:0>3}.tigerbeetle";
34
46
  const filename_len = fmt.count(filename_fmt, .{ 0, 0 });
35
47
 
36
48
  /// Create a .tigerbeetle data file for the given args and exit
37
- fn init(arena: *mem.Allocator, cluster: u32, replica: u8, dir_fd: os.fd_t) !void {
49
+ fn init(io: *IO, cluster: u32, replica: u8, dir_fd: os.fd_t) !void {
38
50
  // Add 1 for the terminating null byte
39
51
  var buffer: [filename_len + 1]u8 = undefined;
40
52
  const filename = fmt.bufPrintZ(&buffer, filename_fmt, .{ cluster, replica }) catch unreachable;
41
53
  assert(filename.len == filename_len);
42
54
 
43
55
  // TODO Expose data file size on the CLI.
44
- _ = try Storage.open(
56
+ _ = try io.open_file(
45
57
  dir_fd,
46
58
  filename,
47
59
  config.journal_size_max, // TODO Double-check that we have space for redundant headers.
@@ -53,7 +65,8 @@ fn init(arena: *mem.Allocator, cluster: u32, replica: u8, dir_fd: os.fd_t) !void
53
65
 
54
66
  /// Run as a replica server defined by the given args
55
67
  fn start(
56
- arena: *mem.Allocator,
68
+ io: *IO,
69
+ allocator: mem.Allocator,
57
70
  cluster: u32,
58
71
  replica_index: u8,
59
72
  addresses: []std.net.Address,
@@ -67,30 +80,30 @@ fn start(
67
80
  assert(filename.len == filename_len);
68
81
 
69
82
  // TODO Expose data file size on the CLI.
70
- const storage_fd = try Storage.open(
83
+ const storage_fd = try io.open_file(
71
84
  dir_fd,
72
85
  filename,
73
86
  config.journal_size_max, // TODO Double-check that we have space for redundant headers.
74
87
  false,
75
88
  );
76
- var io = try IO.init(128, 0);
89
+
77
90
  var state_machine = try StateMachine.init(
78
- arena,
91
+ allocator,
79
92
  config.accounts_max,
80
93
  config.transfers_max,
81
94
  config.commits_max,
82
95
  );
83
- var storage = try Storage.init(config.journal_size_max, storage_fd, &io);
96
+ var storage = try Storage.init(config.journal_size_max, storage_fd, io);
84
97
  var message_bus = try MessageBus.init(
85
- arena,
98
+ allocator,
86
99
  cluster,
87
100
  addresses,
88
101
  replica_index,
89
- &io,
102
+ io,
90
103
  );
91
104
  var time: Time = .{};
92
105
  var replica = try Replica.init(
93
- arena,
106
+ allocator,
94
107
  cluster,
95
108
  @intCast(u8, addresses.len),
96
109
  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 is_darwin = std.Target.current.isDarwin();
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);
@@ -17,14 +17,21 @@ const IO = @import("io.zig").IO;
17
17
  const MessagePool = @import("message_pool.zig").MessagePool;
18
18
  const Message = MessagePool.Message;
19
19
 
20
- const SendQueue = RingBuffer(*Message, config.connection_send_queue_max);
21
-
22
20
  pub const MessageBusReplica = MessageBusImpl(.replica);
23
21
  pub const MessageBusClient = MessageBusImpl(.client);
24
22
 
25
- const ProcessType = enum { replica, client };
23
+ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
24
+ const SendQueue = RingBuffer(*Message, switch (process_type) {
25
+ .replica => config.connection_send_queue_max_replica,
26
+ // A client has at most 1 in-flight request, plus pings.
27
+ .client => config.connection_send_queue_max_client,
28
+ });
29
+
30
+ const tcp_sndbuf = switch (process_type) {
31
+ .replica => config.tcp_sndbuf_replica,
32
+ .client => config.tcp_sndbuf_client,
33
+ };
26
34
 
27
- fn MessageBusImpl(comptime process_type: ProcessType) type {
28
35
  return struct {
29
36
  const Self = @This();
30
37
 
@@ -54,8 +61,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
54
61
 
55
62
  /// The callback to be called when a message is received. Use set_on_message() to set
56
63
  /// with type safety for the context pointer.
57
- on_message_callback: ?fn (context: ?*c_void, message: *Message) void = null,
58
- on_message_context: ?*c_void = null,
64
+ on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
65
+ on_message_context: ?*anyopaque = null,
59
66
 
60
67
  /// This slice is allocated with a fixed size in the init function and never reallocated.
61
68
  connections: []Connection,
@@ -75,7 +82,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
75
82
 
76
83
  /// Initialize the MessageBus for the given cluster, configuration and replica/client process.
77
84
  pub fn init(
78
- allocator: *mem.Allocator,
85
+ allocator: mem.Allocator,
79
86
  cluster: u32,
80
87
  configuration: []std.net.Address,
81
88
  process: switch (process_type) {
@@ -105,14 +112,14 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
105
112
  };
106
113
 
107
114
  var bus: Self = .{
108
- .pool = try MessagePool.init(allocator),
115
+ .pool = try MessagePool.init(allocator, process_type),
109
116
  .io = io,
110
117
  .cluster = cluster,
111
118
  .configuration = configuration,
112
119
  .process = switch (process_type) {
113
120
  .replica => .{
114
121
  .replica = process,
115
- .accept_fd = try init_tcp(configuration[process]),
122
+ .accept_fd = try init_tcp(io, configuration[process]),
116
123
  },
117
124
  .client => {},
118
125
  },
@@ -124,7 +131,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
124
131
 
125
132
  // Pre-allocate enough memory to hold all possible connections in the client map.
126
133
  if (process_type == .replica) {
127
- try bus.process.clients.ensureCapacity(allocator, config.connections_max);
134
+ try bus.process.clients.ensureTotalCapacity(allocator, config.connections_max);
128
135
  }
129
136
 
130
137
  return bus;
@@ -140,7 +147,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
140
147
  assert(bus.on_message_context == null);
141
148
 
142
149
  bus.on_message_callback = struct {
143
- fn wrapper(_context: ?*c_void, message: *Message) void {
150
+ fn wrapper(_context: ?*anyopaque, message: *Message) void {
144
151
  on_message(@intToPtr(Context, @ptrToInt(_context)), message);
145
152
  }
146
153
  }.wrapper;
@@ -148,15 +155,15 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
148
155
  }
149
156
 
150
157
  /// TODO This is required by the Client.
151
- pub fn deinit(bus: *Self) void {}
158
+ pub fn deinit(_: *Self) void {}
152
159
 
153
- fn init_tcp(address: std.net.Address) !os.socket_t {
154
- const fd = try os.socket(
160
+ fn init_tcp(io: *IO, address: std.net.Address) !os.socket_t {
161
+ const fd = try io.open_socket(
155
162
  address.any.family,
156
- os.SOCK_STREAM | sock_flags,
157
- os.IPPROTO_TCP,
163
+ os.SOCK.STREAM,
164
+ os.IPPROTO.TCP,
158
165
  );
159
- errdefer os.close(fd);
166
+ errdefer os.closeSocket(fd);
160
167
 
161
168
  const set = struct {
162
169
  fn set(_fd: os.socket_t, level: u32, option: u32, value: c_int) !void {
@@ -164,71 +171,55 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
164
171
  }
165
172
  }.set;
166
173
 
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
- // Set tcp recv buffer size
173
174
  if (config.tcp_rcvbuf > 0) rcvbuf: {
174
- if (!is_darwin) {
175
+ if (is_linux) {
175
176
  // Requires CAP_NET_ADMIN privilege (settle for SO_RCVBUF in case of an EPERM):
176
- if (set(fd, os.SOL_SOCKET, os.SO_RCVBUFFORCE, config.tcp_rcvbuf)) |_| {
177
+ if (set(fd, os.SOL.SOCKET, os.SO.RCVBUFFORCE, config.tcp_rcvbuf)) |_| {
177
178
  break :rcvbuf;
178
179
  } else |err| switch (err) {
179
180
  error.PermissionDenied => {},
180
181
  else => |e| return e,
181
182
  }
182
183
  }
183
- try set(fd, os.SOL_SOCKET, os.SO_RCVBUF, config.tcp_rcvbuf);
184
+ try set(fd, os.SOL.SOCKET, os.SO.RCVBUF, config.tcp_rcvbuf);
184
185
  }
185
186
 
186
- // Set tcp send buffer size
187
- if (config.tcp_sndbuf > 0) sndbuf: {
188
- if (!is_darwin) {
187
+ if (tcp_sndbuf > 0) sndbuf: {
188
+ if (is_linux) {
189
189
  // Requires CAP_NET_ADMIN privilege (settle for SO_SNDBUF in case of an EPERM):
190
- if (set(fd, os.SOL_SOCKET, os.SO_SNDBUFFORCE, config.tcp_sndbuf)) |_| {
190
+ if (set(fd, os.SOL.SOCKET, os.SO.SNDBUFFORCE, tcp_sndbuf)) |_| {
191
191
  break :sndbuf;
192
192
  } else |err| switch (err) {
193
193
  error.PermissionDenied => {},
194
194
  else => |e| return e,
195
195
  }
196
196
  }
197
- try set(fd, os.SOL_SOCKET, os.SO_SNDBUF, config.tcp_sndbuf);
197
+ try set(fd, os.SOL.SOCKET, os.SO.SNDBUF, tcp_sndbuf);
198
198
  }
199
199
 
200
- // Set tcp keep alive
201
200
  if (config.tcp_keepalive) {
202
- try set(fd, os.SOL_SOCKET, os.SO_KEEPALIVE, 1);
203
- if (!is_darwin) {
204
- try set(fd, os.IPPROTO_TCP, os.TCP_KEEPIDLE, config.tcp_keepidle);
205
- try set(fd, os.IPPROTO_TCP, os.TCP_KEEPINTVL, config.tcp_keepintvl);
206
- try set(fd, os.IPPROTO_TCP, os.TCP_KEEPCNT, config.tcp_keepcnt);
201
+ try set(fd, os.SOL.SOCKET, os.SO.KEEPALIVE, 1);
202
+ if (is_linux) {
203
+ try set(fd, os.IPPROTO.TCP, os.TCP.KEEPIDLE, config.tcp_keepidle);
204
+ try set(fd, os.IPPROTO.TCP, os.TCP.KEEPINTVL, config.tcp_keepintvl);
205
+ try set(fd, os.IPPROTO.TCP, os.TCP.KEEPCNT, config.tcp_keepcnt);
207
206
  }
208
207
  }
209
208
 
210
- // Set tcp user timeout
211
209
  if (config.tcp_user_timeout > 0) {
212
- if (!is_darwin) {
213
- try set(fd, os.IPPROTO_TCP, os.TCP_USER_TIMEOUT, config.tcp_user_timeout);
210
+ if (is_linux) {
211
+ try set(fd, os.IPPROTO.TCP, os.TCP.USER_TIMEOUT, config.tcp_user_timeout);
214
212
  }
215
213
  }
216
214
 
217
215
  // Set tcp no-delay
218
216
  if (config.tcp_nodelay) {
219
- const TCP_NODELAY: ?u32 = if (@hasDecl(os, "TCP_NODELAY"))
220
- @as(u32, os.TCP_NODELAY)
221
- else if (is_darwin)
222
- @as(u32, 1)
223
- else
224
- null;
225
-
226
- if (TCP_NODELAY) |tcp_nodelay| {
227
- try set(fd, os.IPPROTO_TCP, tcp_nodelay, 1);
217
+ if (is_linux) {
218
+ try set(fd, os.IPPROTO.TCP, os.TCP.NODELAY, 1);
228
219
  }
229
220
  }
230
221
 
231
- try set(fd, os.SOL_SOCKET, os.SO_REUSEADDR, 1);
222
+ try set(fd, os.SOL.SOCKET, os.SO.REUSEADDR, 1);
232
223
  try os.bind(fd, &address.any, address.getOsSockLen());
233
224
  try os.listen(fd, config.tcp_backlog);
234
225
 
@@ -289,7 +280,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
289
280
  if (connection.state == .terminating) return;
290
281
  }
291
282
 
292
- log.notice("all connections in use but not all replicas are connected, " ++
283
+ log.info("all connections in use but not all replicas are connected, " ++
293
284
  "attempting to disconnect a client", .{});
294
285
  for (bus.connections) |*connection| {
295
286
  if (connection.peer == .client) {
@@ -298,7 +289,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
298
289
  }
299
290
  }
300
291
 
301
- log.notice("failed to disconnect a client as no peer was a known client, " ++
292
+ log.info("failed to disconnect a client as no peer was a known client, " ++
302
293
  "attempting to disconnect an unknown peer.", .{});
303
294
  for (bus.connections) |*connection| {
304
295
  if (connection.peer == .unknown) {
@@ -332,7 +323,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
332
323
  on_accept,
333
324
  &bus.process.accept_completion,
334
325
  bus.process.accept_fd,
335
- sock_flags,
336
326
  );
337
327
  }
338
328
 
@@ -341,6 +331,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
341
331
  completion: *IO.Completion,
342
332
  result: IO.AcceptError!os.socket_t,
343
333
  ) void {
334
+ _ = completion;
335
+
344
336
  comptime assert(process_type == .replica);
345
337
  assert(bus.process.accept_connection != null);
346
338
  defer bus.process.accept_connection = null;
@@ -353,7 +345,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
353
345
  bus.process.accept_connection.?.on_accept(bus, fd);
354
346
  }
355
347
 
356
- pub fn get_message(bus: *Self) ?*Message {
348
+ pub fn get_message(bus: *Self) *Message {
357
349
  return bus.pool.get_message();
358
350
  }
359
351
 
@@ -414,11 +406,11 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
414
406
  terminating,
415
407
  } = .free,
416
408
  /// This is guaranteed to be valid only while state is connected.
417
- /// It will be reset to -1 during the shutdown process and is always -1 if the
418
- /// connection is unused (i.e. peer == .none). We use -1 instead of undefined here
409
+ /// It will be reset to IO.INVALID_SOCKET during the shutdown process and is always IO.INVALID_SOCKET if the
410
+ /// connection is unused (i.e. peer == .none). We use IO.INVALID_SOCKET instead of undefined here
419
411
  /// for safety to ensure an error if the invalid value is ever used, instead of
420
412
  /// potentially performing an action on an active fd.
421
- fd: os.socket_t = -1,
413
+ fd: os.socket_t = IO.INVALID_SOCKET,
422
414
 
423
415
  /// This completion is used for all recv operations.
424
416
  /// It is also used for the initial connect when establishing a replica connection.
@@ -454,12 +446,12 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
454
446
 
455
447
  assert(connection.peer == .none);
456
448
  assert(connection.state == .free);
457
- assert(connection.fd == -1);
449
+ assert(connection.fd == IO.INVALID_SOCKET);
458
450
 
459
451
  // The first replica's network address family determines the
460
452
  // family for all other replicas:
461
453
  const family = bus.configuration[0].any.family;
462
- connection.fd = os.socket(family, os.SOCK_STREAM | sock_flags, 0) catch return;
454
+ connection.fd = bus.io.open_socket(family, os.SOCK.STREAM, os.IPPROTO.TCP) catch return;
463
455
  connection.peer = .{ .replica = replica };
464
456
  connection.state = .connecting;
465
457
  bus.connections_used += 1;
@@ -469,7 +461,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
469
461
 
470
462
  var attempts = &bus.replicas_connect_attempts[replica];
471
463
  const ms = vsr.exponential_backoff_with_jitter(
472
- &bus.prng,
464
+ bus.prng.random(),
473
465
  config.connection_delay_min_ms,
474
466
  config.connection_delay_max_ms,
475
467
  attempts.*,
@@ -560,7 +552,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
560
552
  pub fn on_accept(connection: *Connection, bus: *Self, fd: os.socket_t) void {
561
553
  assert(connection.peer == .none);
562
554
  assert(connection.state == .accepting);
563
- assert(connection.fd == -1);
555
+ assert(connection.fd == IO.INVALID_SOCKET);
564
556
 
565
557
  connection.peer = .unknown;
566
558
  connection.state = .connected;
@@ -577,7 +569,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
577
569
 
578
570
  assert(connection.peer == .unknown or connection.peer == .replica);
579
571
  assert(connection.state == .connected);
580
- assert(connection.fd != -1);
572
+ assert(connection.fd != IO.INVALID_SOCKET);
581
573
 
582
574
  assert(connection.recv_submitted == false);
583
575
  assert(connection.recv_message == null);
@@ -597,15 +589,14 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
597
589
  .terminating => return,
598
590
  .free, .accepting => unreachable,
599
591
  }
600
- connection.send_queue.push(message.ref()) catch |err| switch (err) {
601
- error.NoSpaceLeft => {
602
- bus.unref(message);
603
- log.notice("message queue for peer {} full, dropping message", .{
604
- connection.peer,
605
- });
606
- return;
607
- },
608
- };
592
+ if (connection.send_queue.full()) {
593
+ log.info("message queue for peer {} full, dropping {s} message", .{
594
+ connection.peer,
595
+ @tagName(message.header.command),
596
+ });
597
+ return;
598
+ }
599
+ connection.send_queue.push_assume_capacity(message.ref());
609
600
  // If the connection has not yet been established we can't send yet.
610
601
  // Instead on_connect() will call send().
611
602
  if (connection.state == .connecting) {
@@ -625,7 +616,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
625
616
  pub fn terminate(connection: *Connection, bus: *Self, how: enum { shutdown, close }) void {
626
617
  assert(connection.peer != .none);
627
618
  assert(connection.state != .free);
628
- assert(connection.fd != -1);
619
+ assert(connection.fd != IO.INVALID_SOCKET);
629
620
  switch (how) {
630
621
  .shutdown => {
631
622
  // The shutdown syscall will cause currently in progress send/recv
@@ -633,12 +624,8 @@ 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.SHUT_RDWR);
637
- switch (os.errno(rc)) {
638
- 0 => {},
639
- os.EBADF => unreachable,
640
- os.EINVAL => unreachable,
641
- os.ENOTCONN => {
627
+ os.shutdown(connection.fd, .both) catch |err| switch (err) {
628
+ error.SocketNotConnected => {
642
629
  // This should only happen if we for some reason decide to terminate
643
630
  // a connection while a connect operation is in progress.
644
631
  // This is fine though, we simply continue with the logic below and
@@ -653,9 +640,9 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
653
640
  //assert(connection.recv_submitted);
654
641
  //assert(!connection.send_submitted);
655
642
  },
656
- os.ENOTSOCK => unreachable,
657
- else => |err| os.unexpectedErrno(err) catch {},
658
- }
643
+ // Ignore all the remaining errors for now
644
+ error.ConnectionAborted, error.ConnectionResetByPeer, error.BlockingOperationInProgress, error.NetworkSubsystemFailed, error.SystemResources, error.Unexpected => {},
645
+ };
659
646
  },
660
647
  .close => {},
661
648
  }
@@ -667,7 +654,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
667
654
  fn parse_messages(connection: *Connection, bus: *Self) void {
668
655
  assert(connection.peer != .none);
669
656
  assert(connection.state == .connected);
670
- assert(connection.fd != -1);
657
+ assert(connection.fd != IO.INVALID_SOCKET);
671
658
 
672
659
  while (connection.parse_message(bus)) |message| {
673
660
  defer bus.unref(message);
@@ -749,14 +736,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
749
736
  // `references` and `header` metadata.
750
737
  if (connection.recv_progress == header.size) return connection.recv_message.?.ref();
751
738
 
752
- const message = bus.get_message() orelse {
753
- // TODO Decrease the probability of this happening by:
754
- // 1. using a header-only message if possible.
755
- // 2. determining a true upper limit for static allocation.
756
- log.err("no free buffer available to deliver message from {}", .{connection.peer});
757
- connection.terminate(bus, .shutdown);
758
- return null;
759
- };
739
+ const message = bus.get_message();
760
740
  mem.copy(u8, message.buffer, data[0..header.size]);
761
741
  return message;
762
742
  }
@@ -794,7 +774,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
794
774
 
795
775
  assert(connection.peer != .none);
796
776
  assert(connection.state == .connected);
797
- assert(connection.fd != -1);
777
+ assert(connection.fd != IO.INVALID_SOCKET);
798
778
 
799
779
  if (connection.peer != .unknown) return;
800
780
 
@@ -841,14 +821,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
841
821
  return;
842
822
  }
843
823
 
844
- const new_message = bus.get_message() orelse {
845
- // TODO Decrease the probability of this happening by:
846
- // 1. using a header-only message if possible.
847
- // 2. determining a true upper limit for static allocation.
848
- log.err("no free buffer available to recv message from {}", .{connection.peer});
849
- connection.terminate(bus, .shutdown);
850
- return;
851
- };
824
+ const new_message = bus.get_message();
852
825
  defer bus.unref(new_message);
853
826
 
854
827
  if (connection.recv_message) |recv_message| {
@@ -872,7 +845,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
872
845
  fn recv(connection: *Connection, bus: *Self) void {
873
846
  assert(connection.peer != .none);
874
847
  assert(connection.state == .connected);
875
- assert(connection.fd != -1);
848
+ assert(connection.fd != IO.INVALID_SOCKET);
876
849
 
877
850
  assert(!connection.recv_submitted);
878
851
  connection.recv_submitted = true;
@@ -886,7 +859,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
886
859
  &connection.recv_completion,
887
860
  connection.fd,
888
861
  connection.recv_message.?.buffer[connection.recv_progress..config.message_size_max],
889
- if (is_darwin) 0 else os.MSG_NOSIGNAL,
890
862
  );
891
863
  }
892
864
 
@@ -918,7 +890,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
918
890
  fn send(connection: *Connection, bus: *Self) void {
919
891
  assert(connection.peer == .client or connection.peer == .replica);
920
892
  assert(connection.state == .connected);
921
- assert(connection.fd != -1);
893
+ assert(connection.fd != IO.INVALID_SOCKET);
922
894
  const message = connection.send_queue.head() orelse return;
923
895
  assert(!connection.send_submitted);
924
896
  connection.send_submitted = true;
@@ -929,7 +901,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
929
901
  &connection.send_completion,
930
902
  connection.fd,
931
903
  message.buffer[connection.send_progress..message.header.size],
932
- if (is_darwin) 0 else os.MSG_NOSIGNAL,
933
904
  );
934
905
  }
935
906
 
@@ -976,8 +947,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
976
947
  bus.unref(message);
977
948
  connection.recv_message = null;
978
949
  }
979
- assert(connection.fd != -1);
980
- defer connection.fd = -1;
950
+ assert(connection.fd != IO.INVALID_SOCKET);
951
+ defer connection.fd = IO.INVALID_SOCKET;
981
952
  // It's OK to use the send completion here as we know that no send
982
953
  // operation is currently in progress.
983
954
  bus.io.close(*Self, bus, on_close, &connection.send_completion, connection.fd);