tigerbeetle-node 0.11.12 → 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 (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -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 => self.on_pong(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(self: *Self, pong: *const Message) void {
277
- assert(pong.header.command == .pong);
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,
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
- }