tigerbeetle-node 0.11.13 → 0.12.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 (146) hide show
  1. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  2. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  3. package/dist/bin/aarch64-macos/client.node +0 -0
  4. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  5. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  6. package/dist/bin/x86_64-macos/client.node +0 -0
  7. package/dist/index.js +33 -1
  8. package/dist/index.js.map +1 -1
  9. package/package-lock.json +66 -0
  10. package/package.json +6 -16
  11. package/src/index.ts +56 -1
  12. package/src/node.zig +9 -9
  13. package/dist/.client.node.sha256 +0 -1
  14. package/scripts/build_lib.sh +0 -61
  15. package/scripts/download_node_headers.sh +0 -32
  16. package/src/tigerbeetle/scripts/benchmark.bat +0 -55
  17. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  18. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  19. package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
  22. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  23. package/src/tigerbeetle/scripts/install.bat +0 -7
  24. package/src/tigerbeetle/scripts/install.sh +0 -21
  25. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  26. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  27. package/src/tigerbeetle/scripts/lint.zig +0 -199
  28. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  29. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
  30. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  31. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  32. package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
  33. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  34. package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
  35. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  36. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  37. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  38. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  39. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  40. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  41. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  42. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  43. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  44. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  45. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  46. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  47. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  48. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  49. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  50. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  51. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  52. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  53. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  54. package/src/tigerbeetle/src/benchmark.zig +0 -336
  55. package/src/tigerbeetle/src/config.zig +0 -233
  56. package/src/tigerbeetle/src/constants.zig +0 -428
  57. package/src/tigerbeetle/src/ewah.zig +0 -286
  58. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  59. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  60. package/src/tigerbeetle/src/fifo.zig +0 -120
  61. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  62. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  63. package/src/tigerbeetle/src/io/linux.zig +0 -1071
  64. package/src/tigerbeetle/src/io/test.zig +0 -643
  65. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  66. package/src/tigerbeetle/src/io.zig +0 -34
  67. package/src/tigerbeetle/src/iops.zig +0 -107
  68. package/src/tigerbeetle/src/lsm/README.md +0 -308
  69. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  70. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  71. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  72. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  73. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  74. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  75. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  76. package/src/tigerbeetle/src/lsm/forest.zig +0 -205
  77. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
  78. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  79. package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
  80. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  81. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  82. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  83. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
  84. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  85. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  86. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  87. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  88. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
  89. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
  90. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  91. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  92. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  93. package/src/tigerbeetle/src/lsm/table.zig +0 -1009
  94. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
  95. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  96. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
  97. package/src/tigerbeetle/src/lsm/test.zig +0 -439
  98. package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
  99. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
  100. package/src/tigerbeetle/src/message_bus.zig +0 -1013
  101. package/src/tigerbeetle/src/message_pool.zig +0 -156
  102. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  103. package/src/tigerbeetle/src/simulator.zig +0 -580
  104. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
  105. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  106. package/src/tigerbeetle/src/state_machine.zig +0 -2099
  107. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  108. package/src/tigerbeetle/src/stdx.zig +0 -171
  109. package/src/tigerbeetle/src/storage.zig +0 -393
  110. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  111. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  112. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  113. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  114. package/src/tigerbeetle/src/testing/cluster.zig +0 -444
  115. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  116. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  117. package/src/tigerbeetle/src/testing/id.zig +0 -99
  118. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
  119. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  120. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  121. package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
  122. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  123. package/src/tigerbeetle/src/testing/table.zig +0 -247
  124. package/src/tigerbeetle/src/testing/time.zig +0 -84
  125. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  126. package/src/tigerbeetle/src/time.zig +0 -112
  127. package/src/tigerbeetle/src/tracer.zig +0 -529
  128. package/src/tigerbeetle/src/unit_tests.zig +0 -40
  129. package/src/tigerbeetle/src/vopr.zig +0 -495
  130. package/src/tigerbeetle/src/vsr/README.md +0 -209
  131. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  132. package/src/tigerbeetle/src/vsr/clock.zig +0 -855
  133. package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
  134. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  135. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  136. package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
  137. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  138. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  139. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  140. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  141. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  142. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  143. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  144. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  145. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  146. package/src/tigerbeetle/src/vsr.zig +0 -1425
@@ -1,544 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const mem = std.mem;
4
-
5
- const constants = @import("../constants.zig");
6
- const vsr = @import("../vsr.zig");
7
- const Header = vsr.Header;
8
-
9
- const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
10
- const MessagePool = @import("../message_pool.zig").MessagePool;
11
- const Message = @import("../message_pool.zig").MessagePool.Message;
12
-
13
- const log = std.log.scoped(.client);
14
-
15
- pub fn Client(comptime StateMachine_: type, comptime MessageBus: type) type {
16
- return struct {
17
- const Self = @This();
18
-
19
- pub const StateMachine = StateMachine_;
20
-
21
- pub const Error = error{
22
- TooManyOutstandingRequests,
23
- };
24
-
25
- pub const Request = struct {
26
- pub const Callback = fn (
27
- user_data: u128,
28
- operation: StateMachine.Operation,
29
- results: Error![]const u8,
30
- ) void;
31
- user_data: u128,
32
- // Null iff operation=register.
33
- callback: ?Callback,
34
- message: *Message,
35
- };
36
-
37
- allocator: mem.Allocator,
38
- message_bus: MessageBus,
39
-
40
- /// A universally unique identifier for the client (must not be zero).
41
- /// Used for routing replies back to the client via any network path (multi-path routing).
42
- /// The client ID must be ephemeral and random per process, and never persisted, so that
43
- /// lingering or zombie deployment processes cannot break correctness and/or liveness.
44
- /// A cryptographic random number generator must be used to ensure these properties.
45
- id: u128,
46
-
47
- /// The identifier for the cluster that this client intends to communicate with.
48
- cluster: u32,
49
-
50
- /// The number of replicas in the cluster.
51
- replica_count: u8,
52
-
53
- /// The total number of ticks elapsed since the client was initialized.
54
- ticks: u64 = 0,
55
-
56
- /// We hash-chain request/reply checksums to verify linearizability within a client session:
57
- /// * so that the parent of the next request is the checksum of the latest reply, and
58
- /// * so that the parent of the next reply is the checksum of the latest request.
59
- parent: u128 = 0,
60
-
61
- /// The session number for the client, zero when registering a session, non-zero thereafter.
62
- session: u64 = 0,
63
-
64
- /// The request number of the next request.
65
- request_number: u32 = 0,
66
-
67
- /// The highest view number seen by the client in messages exchanged with the cluster.
68
- /// Used to locate the current primary, and provide more information to a partitioned primary.
69
- view: u32 = 0,
70
-
71
- /// A client is allowed at most one inflight request at a time at the protocol layer.
72
- /// We therefore queue any further concurrent requests made by the application layer.
73
- request_queue: RingBuffer(Request, constants.client_request_queue_max, .array) = .{},
74
-
75
- /// The number of ticks without a reply before the client resends the inflight request.
76
- /// Dynamically adjusted as a function of recent request round-trip time.
77
- request_timeout: vsr.Timeout,
78
-
79
- /// The number of ticks before the client broadcasts a ping to the cluster.
80
- /// Used for end-to-end keepalive, and to discover a new primary between requests.
81
- ping_timeout: vsr.Timeout,
82
-
83
- /// Used to calculate exponential backoff with random jitter.
84
- /// Seeded with the client's ID.
85
- prng: std.rand.DefaultPrng,
86
-
87
- on_reply_context: ?*anyopaque = null,
88
- /// Used for testing. Called for replies to all operations (including `register`).
89
- on_reply_callback: ?fn (
90
- client: *Self,
91
- request: *Message,
92
- reply: *Message,
93
- ) void = null,
94
-
95
- pub fn init(
96
- allocator: mem.Allocator,
97
- id: u128,
98
- cluster: u32,
99
- replica_count: u8,
100
- message_pool: *MessagePool,
101
- message_bus_options: MessageBus.Options,
102
- ) !Self {
103
- assert(id > 0);
104
- assert(replica_count > 0);
105
-
106
- var message_bus = try MessageBus.init(
107
- allocator,
108
- cluster,
109
- .{ .client = id },
110
- message_pool,
111
- Self.on_message,
112
- message_bus_options,
113
- );
114
- errdefer message_bus.deinit(allocator);
115
-
116
- var self = Self{
117
- .allocator = allocator,
118
- .message_bus = message_bus,
119
- .id = id,
120
- .cluster = cluster,
121
- .replica_count = replica_count,
122
- .request_timeout = .{
123
- .name = "request_timeout",
124
- .id = id,
125
- .after = constants.rtt_ticks * constants.rtt_multiple,
126
- },
127
- .ping_timeout = .{
128
- .name = "ping_timeout",
129
- .id = id,
130
- .after = 30000 / constants.tick_ms,
131
- },
132
- .prng = std.rand.DefaultPrng.init(@truncate(u64, id)),
133
- };
134
-
135
- self.ping_timeout.start();
136
-
137
- return self;
138
- }
139
-
140
- pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
141
- while (self.request_queue.pop()) |inflight| {
142
- self.message_bus.unref(inflight.message);
143
- }
144
- self.message_bus.deinit(allocator);
145
- }
146
-
147
- pub fn on_message(message_bus: *MessageBus, message: *Message) void {
148
- const self = @fieldParentPtr(Self, "message_bus", message_bus);
149
- log.debug("{}: on_message: {}", .{ self.id, message.header });
150
- if (message.header.invalid()) |reason| {
151
- log.debug("{}: on_message: invalid ({s})", .{ self.id, reason });
152
- return;
153
- }
154
- if (message.header.cluster != self.cluster) {
155
- log.warn("{}: on_message: wrong cluster (cluster should be {}, not {})", .{
156
- self.id,
157
- self.cluster,
158
- message.header.cluster,
159
- });
160
- return;
161
- }
162
- switch (message.header.command) {
163
- .pong_client => self.on_pong_client(message),
164
- .reply => self.on_reply(message),
165
- .eviction => self.on_eviction(message),
166
- else => {
167
- log.warn("{}: on_message: ignoring misdirected {s} message", .{
168
- self.id,
169
- @tagName(message.header.command),
170
- });
171
- return;
172
- },
173
- }
174
- }
175
-
176
- pub fn tick(self: *Self) void {
177
- self.ticks += 1;
178
-
179
- self.message_bus.tick();
180
-
181
- self.ping_timeout.tick();
182
- self.request_timeout.tick();
183
-
184
- if (self.ping_timeout.fired()) self.on_ping_timeout();
185
- if (self.request_timeout.fired()) self.on_request_timeout();
186
- }
187
-
188
- pub fn request(
189
- self: *Self,
190
- user_data: u128,
191
- callback: Request.Callback,
192
- operation: StateMachine.Operation,
193
- message: *Message,
194
- message_body_size: usize,
195
- ) void {
196
- assert(operation != .reserved);
197
- assert(operation != .root);
198
- assert(operation != .register);
199
-
200
- self.register();
201
- assert(self.request_number > 0);
202
-
203
- // We will set parent, context, view and checksums only when sending for the first time:
204
- message.header.* = .{
205
- .client = self.id,
206
- .request = self.request_number,
207
- .cluster = self.cluster,
208
- .command = .request,
209
- .operation = vsr.Operation.from(StateMachine, operation),
210
- .size = @intCast(u32, @sizeOf(Header) + message_body_size),
211
- };
212
-
213
- log.debug("{}: request: user_data={} request={} size={} {s}", .{
214
- self.id,
215
- user_data,
216
- message.header.request,
217
- message.header.size,
218
- @tagName(operation),
219
- });
220
-
221
- if (self.request_queue.full()) {
222
- callback(user_data, operation, error.TooManyOutstandingRequests);
223
- return;
224
- }
225
-
226
- const was_empty = self.request_queue.empty();
227
-
228
- self.request_number += 1;
229
- self.request_queue.push_assume_capacity(.{
230
- .user_data = user_data,
231
- .callback = callback,
232
- .message = message.ref(),
233
- });
234
-
235
- // If the queue was empty, then there is no request inflight and we must send this one:
236
- if (was_empty) self.send_request_for_the_first_time(message);
237
- }
238
-
239
- /// Acquires a message from the message bus if one is available.
240
- pub fn get_message(self: *Self) *Message {
241
- return self.message_bus.get_message();
242
- }
243
-
244
- /// Releases a message back to the message bus.
245
- pub fn unref(self: *Self, message: *Message) void {
246
- self.message_bus.unref(message);
247
- }
248
-
249
- fn on_eviction(self: *Self, eviction: *const Message) void {
250
- assert(eviction.header.command == .eviction);
251
- assert(eviction.header.cluster == self.cluster);
252
-
253
- if (eviction.header.client != self.id) {
254
- log.warn("{}: on_eviction: ignoring (wrong client={})", .{
255
- self.id,
256
- eviction.header.client,
257
- });
258
- return;
259
- }
260
-
261
- if (eviction.header.view < self.view) {
262
- log.debug("{}: on_eviction: ignoring (older view={})", .{
263
- self.id,
264
- eviction.header.view,
265
- });
266
- return;
267
- }
268
-
269
- assert(eviction.header.client == self.id);
270
- assert(eviction.header.view >= self.view);
271
-
272
- log.err("{}: session evicted: too many concurrent client sessions", .{self.id});
273
- @panic("session evicted: too many concurrent client sessions");
274
- }
275
-
276
- fn on_pong_client(self: *Self, pong: *const Message) void {
277
- assert(pong.header.command == .pong_client);
278
- assert(pong.header.cluster == self.cluster);
279
-
280
- if (pong.header.client != 0) {
281
- log.debug("{}: on_pong: ignoring (client != 0)", .{self.id});
282
- return;
283
- }
284
-
285
- if (pong.header.view > self.view) {
286
- log.debug("{}: on_pong: newer view={}..{}", .{
287
- self.id,
288
- self.view,
289
- pong.header.view,
290
- });
291
- self.view = pong.header.view;
292
- }
293
-
294
- // Now that we know the view number, it's a good time to register if we haven't already:
295
- self.register();
296
- }
297
-
298
- fn on_reply(self: *Self, reply: *Message) void {
299
- // We check these checksums again here because this is the last time we get to downgrade
300
- // a correctness bug into a liveness bug, before we return data back to the application.
301
- assert(reply.header.valid_checksum());
302
- assert(reply.header.valid_checksum_body(reply.body()));
303
- assert(reply.header.command == .reply);
304
-
305
- if (reply.header.client != self.id) {
306
- log.debug("{}: on_reply: ignoring (wrong client={})", .{
307
- self.id,
308
- reply.header.client,
309
- });
310
- return;
311
- }
312
-
313
- if (self.request_queue.head_ptr()) |inflight| {
314
- if (reply.header.request < inflight.message.header.request) {
315
- log.debug("{}: on_reply: ignoring (request {} < {})", .{
316
- self.id,
317
- reply.header.request,
318
- inflight.message.header.request,
319
- });
320
- return;
321
- }
322
- } else {
323
- log.debug("{}: on_reply: ignoring (no inflight request)", .{self.id});
324
- return;
325
- }
326
-
327
- const inflight = self.request_queue.pop().?;
328
- defer self.message_bus.unref(inflight.message);
329
-
330
- log.debug("{}: on_reply: user_data={} request={} size={} {s}", .{
331
- self.id,
332
- inflight.user_data,
333
- reply.header.request,
334
- reply.header.size,
335
- @tagName(reply.header.operation.cast(StateMachine)),
336
- });
337
-
338
- assert(reply.header.parent == self.parent);
339
- assert(reply.header.client == self.id);
340
- assert(reply.header.context == 0);
341
- assert(reply.header.request == inflight.message.header.request);
342
- assert(reply.header.cluster == self.cluster);
343
- assert(reply.header.op == reply.header.commit);
344
- assert(reply.header.operation == inflight.message.header.operation);
345
-
346
- // The checksum of this reply becomes the parent of our next request:
347
- self.parent = reply.header.checksum;
348
-
349
- if (reply.header.view > self.view) {
350
- log.debug("{}: on_reply: newer view={}..{}", .{
351
- self.id,
352
- self.view,
353
- reply.header.view,
354
- });
355
- self.view = reply.header.view;
356
- }
357
-
358
- self.request_timeout.stop();
359
-
360
- if (inflight.message.header.operation == .register) {
361
- assert(self.session == 0);
362
- assert(reply.header.commit > 0);
363
- self.session = reply.header.commit; // The commit number becomes the session number.
364
- }
365
-
366
- // We must process the next request before releasing control back to the callback.
367
- // Otherwise, requests may run through send_request_for_the_first_time() more than once.
368
- if (self.request_queue.head_ptr()) |next_request| {
369
- self.send_request_for_the_first_time(next_request.message);
370
- }
371
-
372
- if (self.on_reply_callback) |on_reply_callback| {
373
- on_reply_callback(self, inflight.message, reply);
374
- }
375
-
376
- if (inflight.callback) |callback| {
377
- assert(inflight.message.header.operation != .register);
378
-
379
- callback(
380
- inflight.user_data,
381
- inflight.message.header.operation.cast(StateMachine),
382
- reply.body(),
383
- );
384
- } else {
385
- assert(inflight.message.header.operation == .register);
386
- }
387
- }
388
-
389
- fn on_ping_timeout(self: *Self) void {
390
- self.ping_timeout.reset();
391
-
392
- const ping = Header{
393
- .command = .ping_client,
394
- .cluster = self.cluster,
395
- .client = self.id,
396
- };
397
-
398
- // TODO If we haven't received a pong from a replica since our last ping, then back off.
399
- self.send_header_to_replicas(ping);
400
- }
401
-
402
- fn on_request_timeout(self: *Self) void {
403
- self.request_timeout.backoff(self.prng.random());
404
-
405
- const message = self.request_queue.head_ptr().?.message;
406
- assert(message.header.command == .request);
407
- assert(message.header.request < self.request_number);
408
- assert(message.header.checksum == self.parent);
409
- assert(message.header.context == self.session);
410
-
411
- log.debug("{}: on_request_timeout: resending request={} checksum={}", .{
412
- self.id,
413
- message.header.request,
414
- message.header.checksum,
415
- });
416
-
417
- // We assume the primary is down and round-robin through the cluster:
418
- self.send_message_to_replica(
419
- @intCast(u8, (self.view + self.request_timeout.attempts) % self.replica_count),
420
- message,
421
- );
422
- }
423
-
424
- /// The caller owns the returned message, if any, which has exactly 1 reference.
425
- fn create_message_from_header(self: *Self, header: Header) *Message {
426
- assert(header.client == self.id);
427
- assert(header.cluster == self.cluster);
428
- assert(header.size == @sizeOf(Header));
429
-
430
- const message = self.message_bus.pool.get_message();
431
- defer self.message_bus.unref(message);
432
-
433
- message.header.* = header;
434
- message.header.set_checksum_body(message.body());
435
- message.header.set_checksum();
436
-
437
- return message.ref();
438
- }
439
-
440
- /// Registers a session with the cluster for the client, if this has not yet been done.
441
- fn register(self: *Self) void {
442
- if (self.request_number > 0) return;
443
-
444
- const message = self.message_bus.get_message();
445
- defer self.message_bus.unref(message);
446
-
447
- // We will set parent, context, view and checksums only when sending for the first time:
448
- message.header.* = .{
449
- .client = self.id,
450
- .request = self.request_number,
451
- .cluster = self.cluster,
452
- .command = .request,
453
- .operation = .register,
454
- };
455
-
456
- assert(self.request_number == 0);
457
- self.request_number += 1;
458
-
459
- log.debug("{}: register: registering a session with the cluster", .{self.id});
460
-
461
- assert(self.request_queue.empty());
462
-
463
- self.request_queue.push_assume_capacity(.{
464
- .user_data = 0,
465
- .callback = null,
466
- .message = message.ref(),
467
- });
468
-
469
- self.send_request_for_the_first_time(message);
470
- }
471
-
472
- fn send_header_to_replica(self: *Self, replica: u8, header: Header) void {
473
- const message = self.create_message_from_header(header);
474
- defer self.message_bus.unref(message);
475
-
476
- self.send_message_to_replica(replica, message);
477
- }
478
-
479
- fn send_header_to_replicas(self: *Self, header: Header) void {
480
- const message = self.create_message_from_header(header);
481
- defer self.message_bus.unref(message);
482
-
483
- var replica: u8 = 0;
484
- while (replica < self.replica_count) : (replica += 1) {
485
- self.send_message_to_replica(replica, message);
486
- }
487
- }
488
-
489
- fn send_message_to_replica(self: *Self, replica: u8, message: *Message) void {
490
- log.debug("{}: sending {s} to replica {}: {}", .{
491
- self.id,
492
- @tagName(message.header.command),
493
- replica,
494
- message.header,
495
- });
496
-
497
- assert(replica < self.replica_count);
498
- assert(message.header.valid_checksum());
499
- assert(message.header.client == self.id);
500
- assert(message.header.cluster == self.cluster);
501
-
502
- self.message_bus.send_message_to_replica(replica, message);
503
- }
504
-
505
- fn send_request_for_the_first_time(self: *Self, message: *Message) void {
506
- assert(self.request_queue.head_ptr().?.message == message);
507
-
508
- assert(message.header.command == .request);
509
- assert(message.header.parent == 0);
510
- assert(message.header.context == 0);
511
- assert(message.header.request < self.request_number);
512
- assert(message.header.view == 0);
513
- assert(message.header.size <= constants.message_size_max);
514
-
515
- // We set the message checksums only when sending the request for the first time,
516
- // which is when we have the checksum of the latest reply available to set as `parent`,
517
- // and similarly also the session number if requests were queued while registering:
518
- message.header.parent = self.parent;
519
- message.header.context = self.session;
520
- // We also try to include our highest view number, so we wait until the request is ready
521
- // to be sent for the first time. However, beyond that, it is not necessary to update
522
- // the view number again, for example if it should change between now and resending.
523
- message.header.view = self.view;
524
- message.header.set_checksum_body(message.body());
525
- message.header.set_checksum();
526
-
527
- // The checksum of this request becomes the parent of our next reply:
528
- self.parent = message.header.checksum;
529
-
530
- log.debug("{}: send_request_for_the_first_time: request={} checksum={}", .{
531
- self.id,
532
- message.header.request,
533
- message.header.checksum,
534
- });
535
-
536
- assert(!self.request_timeout.ticking);
537
- self.request_timeout.start();
538
-
539
- // If our view number is out of date, then the old primary will forward our request.
540
- // If the primary is offline, then our request timeout will fire and we will round-robin.
541
- self.send_message_to_replica(@intCast(u8, self.view % self.replica_count), message);
542
- }
543
- };
544
- }