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.
- package/README.md +3 -4
- package/package.json +2 -2
- package/scripts/postinstall.sh +2 -2
- package/src/node.zig +19 -27
- package/src/tigerbeetle/scripts/benchmark.bat +46 -0
- package/src/tigerbeetle/scripts/install.sh +1 -1
- package/src/tigerbeetle/scripts/install_zig.bat +4 -4
- package/src/tigerbeetle/scripts/install_zig.sh +4 -2
- package/src/tigerbeetle/scripts/lint.zig +8 -2
- package/src/tigerbeetle/scripts/vopr.sh +2 -2
- package/src/tigerbeetle/src/benchmark.zig +10 -12
- package/src/tigerbeetle/src/cli.zig +43 -20
- package/src/tigerbeetle/src/config.zig +26 -11
- 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 +213 -0
- package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +259 -167
- package/src/tigerbeetle/src/io/linux.zig +1038 -0
- package/src/tigerbeetle/src/io/test.zig +643 -0
- package/src/tigerbeetle/src/io/windows.zig +1161 -0
- package/src/tigerbeetle/src/io.zig +9 -1328
- package/src/tigerbeetle/src/main.zig +28 -15
- package/src/tigerbeetle/src/message_bus.zig +78 -107
- package/src/tigerbeetle/src/message_pool.zig +65 -58
- package/src/tigerbeetle/src/ring_buffer.zig +7 -0
- package/src/tigerbeetle/src/simulator.zig +44 -40
- package/src/tigerbeetle/src/state_machine.zig +58 -27
- package/src/tigerbeetle/src/storage.zig +7 -234
- package/src/tigerbeetle/src/test/cluster.zig +5 -8
- package/src/tigerbeetle/src/test/message_bus.zig +10 -9
- package/src/tigerbeetle/src/test/network.zig +16 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
- package/src/tigerbeetle/src/test/state_checker.zig +4 -3
- 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 +8 -128
- package/src/tigerbeetle/src/time.zig +61 -13
- package/src/tigerbeetle/src/vsr/client.zig +23 -37
- package/src/tigerbeetle/src/vsr/clock.zig +27 -44
- package/src/tigerbeetle/src/vsr/journal.zig +9 -12
- package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
- package/src/tigerbeetle/src/vsr/replica.zig +184 -204
- package/src/tigerbeetle/src/vsr.zig +287 -25
- 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
|
|
24
|
-
|
|
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(
|
|
27
|
-
.init => |args| try init(
|
|
28
|
-
.start => |args| try start(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
89
|
+
|
|
77
90
|
var state_machine = try StateMachine.init(
|
|
78
|
-
|
|
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,
|
|
96
|
+
var storage = try Storage.init(config.journal_size_max, storage_fd, io);
|
|
84
97
|
var message_bus = try MessageBus.init(
|
|
85
|
-
|
|
98
|
+
allocator,
|
|
86
99
|
cluster,
|
|
87
100
|
addresses,
|
|
88
101
|
replica_index,
|
|
89
|
-
|
|
102
|
+
io,
|
|
90
103
|
);
|
|
91
104
|
var time: Time = .{};
|
|
92
105
|
var replica = try Replica.init(
|
|
93
|
-
|
|
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
|
|
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
|
-
|
|
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: ?*
|
|
58
|
-
on_message_context: ?*
|
|
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:
|
|
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.
|
|
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: ?*
|
|
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(
|
|
158
|
+
pub fn deinit(_: *Self) void {}
|
|
152
159
|
|
|
153
|
-
fn init_tcp(address: std.net.Address) !os.socket_t {
|
|
154
|
-
const fd = try
|
|
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.
|
|
157
|
-
os.
|
|
163
|
+
os.SOCK.STREAM,
|
|
164
|
+
os.IPPROTO.TCP,
|
|
158
165
|
);
|
|
159
|
-
errdefer os.
|
|
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 (
|
|
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.
|
|
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.
|
|
184
|
+
try set(fd, os.SOL.SOCKET, os.SO.RCVBUF, config.tcp_rcvbuf);
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
203
|
-
if (
|
|
204
|
-
try set(fd, os.
|
|
205
|
-
try set(fd, os.
|
|
206
|
-
try set(fd, os.
|
|
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 (
|
|
213
|
-
try set(fd, os.
|
|
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
|
-
|
|
220
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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
|
|
418
|
-
/// connection is unused (i.e. peer == .none). We use
|
|
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 =
|
|
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 ==
|
|
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 =
|
|
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
|
-
|
|
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 ==
|
|
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 !=
|
|
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.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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 !=
|
|
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
|
-
|
|
637
|
-
|
|
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
|
-
|
|
657
|
-
|
|
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 !=
|
|
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()
|
|
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 !=
|
|
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()
|
|
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 !=
|
|
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 !=
|
|
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 !=
|
|
980
|
-
defer connection.fd =
|
|
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);
|