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.
- package/dist/bin/aarch64-linux-gnu/client.node +0 -0
- package/dist/bin/aarch64-linux-musl/client.node +0 -0
- package/dist/bin/aarch64-macos/client.node +0 -0
- package/dist/bin/x86_64-linux-gnu/client.node +0 -0
- package/dist/bin/x86_64-linux-musl/client.node +0 -0
- package/dist/bin/x86_64-macos/client.node +0 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package-lock.json +66 -0
- package/package.json +6 -16
- package/src/index.ts +56 -1
- package/src/node.zig +9 -9
- package/dist/.client.node.sha256 +0 -1
- package/scripts/build_lib.sh +0 -61
- package/scripts/download_node_headers.sh +0 -32
- package/src/tigerbeetle/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -336
- package/src/tigerbeetle/src/config.zig +0 -233
- package/src/tigerbeetle/src/constants.zig +0 -428
- package/src/tigerbeetle/src/ewah.zig +0 -286
- package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
- package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
- package/src/tigerbeetle/src/fifo.zig +0 -120
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -814
- package/src/tigerbeetle/src/io/linux.zig +0 -1071
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1183
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/iops.zig +0 -107
- package/src/tigerbeetle/src/lsm/README.md +0 -308
- package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
- package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
- package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
- package/src/tigerbeetle/src/lsm/direction.zig +0 -11
- package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
- package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
- package/src/tigerbeetle/src/lsm/forest.zig +0 -205
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
- package/src/tigerbeetle/src/lsm/grid.zig +0 -573
- package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
- package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
- package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
- package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
- package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
- package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
- package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
- package/src/tigerbeetle/src/lsm/table.zig +0 -1009
- package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
- package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
- package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
- package/src/tigerbeetle/src/lsm/test.zig +0 -439
- package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
- package/src/tigerbeetle/src/message_bus.zig +0 -1013
- package/src/tigerbeetle/src/message_pool.zig +0 -156
- package/src/tigerbeetle/src/ring_buffer.zig +0 -399
- package/src/tigerbeetle/src/simulator.zig +0 -580
- package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
- package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
- package/src/tigerbeetle/src/state_machine.zig +0 -2099
- package/src/tigerbeetle/src/static_allocator.zig +0 -65
- package/src/tigerbeetle/src/stdx.zig +0 -171
- package/src/tigerbeetle/src/storage.zig +0 -393
- package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
- package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
- package/src/tigerbeetle/src/testing/cluster.zig +0 -444
- package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
- package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
- package/src/tigerbeetle/src/testing/id.zig +0 -99
- package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
- package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
- package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
- package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
- package/src/tigerbeetle/src/testing/storage.zig +0 -757
- package/src/tigerbeetle/src/testing/table.zig +0 -247
- package/src/tigerbeetle/src/testing/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
- package/src/tigerbeetle/src/time.zig +0 -112
- package/src/tigerbeetle/src/tracer.zig +0 -529
- package/src/tigerbeetle/src/unit_tests.zig +0 -40
- package/src/tigerbeetle/src/vopr.zig +0 -495
- package/src/tigerbeetle/src/vsr/README.md +0 -209
- package/src/tigerbeetle/src/vsr/client.zig +0 -544
- package/src/tigerbeetle/src/vsr/clock.zig +0 -855
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
- package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
- package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
- 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
|
-
}
|