tigerbeetle-node 0.11.8 → 0.11.9

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 (83) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +4 -3
  3. package/scripts/build_lib.sh +29 -0
  4. package/src/node.zig +1 -1
  5. package/src/tigerbeetle/scripts/validate_docs.sh +7 -1
  6. package/src/tigerbeetle/src/benchmark.zig +3 -3
  7. package/src/tigerbeetle/src/config.zig +29 -16
  8. package/src/tigerbeetle/src/constants.zig +30 -9
  9. package/src/tigerbeetle/src/ewah.zig +5 -5
  10. package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
  11. package/src/tigerbeetle/src/lsm/binary_search.zig +1 -1
  12. package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
  13. package/src/tigerbeetle/src/lsm/compaction.zig +34 -21
  14. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +85 -103
  15. package/src/tigerbeetle/src/lsm/grid.zig +19 -13
  16. package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -10
  17. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +12 -8
  18. package/src/tigerbeetle/src/lsm/merge_iterator.zig +1 -1
  19. package/src/tigerbeetle/src/lsm/segmented_array.zig +17 -17
  20. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +1 -1
  21. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +1 -1
  22. package/src/tigerbeetle/src/lsm/table.zig +8 -20
  23. package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
  24. package/src/tigerbeetle/src/lsm/table_iterator.zig +3 -3
  25. package/src/tigerbeetle/src/lsm/table_mutable.zig +14 -2
  26. package/src/tigerbeetle/src/lsm/tree.zig +31 -5
  27. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +86 -114
  28. package/src/tigerbeetle/src/message_bus.zig +4 -4
  29. package/src/tigerbeetle/src/message_pool.zig +7 -10
  30. package/src/tigerbeetle/src/ring_buffer.zig +22 -12
  31. package/src/tigerbeetle/src/simulator.zig +360 -214
  32. package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
  33. package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
  34. package/src/tigerbeetle/src/state_machine.zig +190 -178
  35. package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
  36. package/src/tigerbeetle/src/storage.zig +13 -6
  37. package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
  38. package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
  39. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
  40. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
  41. package/src/tigerbeetle/src/testing/cluster.zig +537 -0
  42. package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
  43. package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
  44. package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
  45. package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
  46. package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
  47. package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
  48. package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
  49. package/src/tigerbeetle/src/testing/storage.zig +754 -0
  50. package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
  51. package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
  52. package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
  53. package/src/tigerbeetle/src/tracer.zig +3 -3
  54. package/src/tigerbeetle/src/unit_tests.zig +4 -4
  55. package/src/tigerbeetle/src/vopr.zig +2 -2
  56. package/src/tigerbeetle/src/vsr/client.zig +5 -2
  57. package/src/tigerbeetle/src/vsr/clock.zig +93 -53
  58. package/src/tigerbeetle/src/vsr/journal.zig +29 -14
  59. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
  60. package/src/tigerbeetle/src/vsr/replica.zig +1383 -774
  61. package/src/tigerbeetle/src/vsr/replica_format.zig +2 -2
  62. package/src/tigerbeetle/src/vsr/superblock.zig +59 -43
  63. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
  64. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
  65. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  66. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +15 -7
  67. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
  68. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +1 -1
  69. package/src/tigerbeetle/src/vsr.zig +6 -4
  70. package/src/tigerbeetle/src/demo.zig +0 -132
  71. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  72. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  73. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -37
  74. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  75. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  76. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  77. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  78. package/src/tigerbeetle/src/test/cluster.zig +0 -352
  79. package/src/tigerbeetle/src/test/conductor.zig +0 -366
  80. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
  81. package/src/tigerbeetle/src/test/state_checker.zig +0 -169
  82. package/src/tigerbeetle/src/test/storage.zig +0 -864
  83. package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
@@ -1,366 +0,0 @@
1
- //! The Conductor coordinates a set of Clients, scheduling requests (generated by the Workload),
2
- //! and receiving replies (validated by the Workload).
3
- //!
4
- //! Replies from the cluster may arrive out-of-order; the Conductor reassembles them in the
5
- //! correct order (by ascending op number) before passing them into the Workload.
6
- const std = @import("std");
7
- const assert = std.debug.assert;
8
- const log = std.log.scoped(.test_conductor);
9
-
10
- const vsr = @import("../vsr.zig");
11
- const util = @import("../util.zig");
12
- const constants = @import("../constants.zig");
13
- const IdPermutation = @import("id.zig").IdPermutation;
14
- const MessagePool = @import("../message_pool.zig").MessagePool;
15
- const Message = MessagePool.Message;
16
-
17
- // TODO(zig) This won't be necessary in Zig 0.10.
18
- const PriorityQueue = @import("./priority_queue.zig").PriorityQueue;
19
-
20
- /// Both messages belong to the Conductor's `MessagePool`.
21
- const PendingReply = struct {
22
- client_index: usize,
23
- request: *Message,
24
- reply: *Message,
25
-
26
- /// `PendingReply`s are ordered by ascending reply op.
27
- fn compare(context: void, a: PendingReply, b: PendingReply) std.math.Order {
28
- _ = context;
29
- return std.math.order(a.reply.header.op, b.reply.header.op);
30
- }
31
- };
32
-
33
- const PendingReplyQueue = PriorityQueue(PendingReply, void, PendingReply.compare);
34
-
35
- pub fn ConductorType(
36
- comptime Client: type,
37
- comptime MessageBus: type,
38
- comptime StateMachine: type,
39
- ) type {
40
- return struct {
41
- const Self = @This();
42
-
43
- /// Reply messages (from cluster to client) may be reordered during transit.
44
- /// The Conductor must reassemble them in the original order (ascending op/commit
45
- /// number) before handing them off to the Workload for verification.
46
- ///
47
- /// `Conduction.stalled_queue` hold replies (and corresponding requests) that are
48
- /// waiting to be processed.
49
- pub const stalled_queue_capacity =
50
- constants.clients_max * constants.client_request_queue_max * 2;
51
-
52
- random: std.rand.Random,
53
- workload: *StateMachine.Workload,
54
- options: Options,
55
- client_id_permutation: IdPermutation,
56
-
57
- clients: []Client,
58
- client_pools: []MessagePool,
59
- message_pool: MessagePool,
60
-
61
- /// The next op to be verified.
62
- /// Starts at 1, because op=0 is the root.
63
- stalled_op: u64 = 1,
64
-
65
- /// The list of messages waiting to be verified (the reply for a lower op has not yet arrived).
66
- /// Includes `register` messages.
67
- stalled_queue: PendingReplyQueue,
68
-
69
- /// Total number of messages sent, including those that have not been delivered.
70
- /// Does not include `register` messages.
71
- requests_sent: usize = 0,
72
-
73
- idle: bool = false,
74
-
75
- const Options = struct {
76
- cluster: u32,
77
- replica_count: u8,
78
- client_count: u8,
79
- message_bus_options: MessageBus.Options,
80
-
81
- /// The total number of requests to send. Does not count `register` messages.
82
- requests_max: usize,
83
-
84
- request_probability: u8, // percent
85
- idle_on_probability: u8, // percent
86
- idle_off_probability: u8, // percent
87
- };
88
-
89
- pub fn init(
90
- allocator: std.mem.Allocator,
91
- random: std.rand.Random,
92
- workload: *StateMachine.Workload,
93
- options: Options,
94
- ) !Self {
95
- assert(options.replica_count >= 1);
96
- assert(options.replica_count <= 6);
97
- assert(options.client_count > 0);
98
- assert(options.client_count * 2 < stalled_queue_capacity);
99
- assert(options.requests_max > 0);
100
-
101
- assert(options.request_probability > 0);
102
- assert(options.request_probability <= 100);
103
- assert(options.idle_on_probability <= 100);
104
- assert(options.idle_off_probability > 0);
105
- assert(options.idle_off_probability <= 100);
106
-
107
- // *2 for PendingReply.request and PendingReply.reply.
108
- var message_pool = try MessagePool.init_capacity(allocator, stalled_queue_capacity * 2);
109
- errdefer message_pool.deinit(allocator);
110
-
111
- var client_pools = try allocator.alloc(MessagePool, options.client_count);
112
- errdefer allocator.free(client_pools);
113
-
114
- for (client_pools) |*pool, i| {
115
- errdefer for (client_pools[0..i]) |*p| p.deinit(allocator);
116
- pool.* = try MessagePool.init(allocator, .client);
117
- }
118
- errdefer for (client_pools) |*p| p.deinit(allocator);
119
-
120
- var clients = try allocator.alloc(Client, options.client_count);
121
- errdefer allocator.free(clients);
122
-
123
- // Always use UUIDs because the simulator network expects client ids to never collide
124
- // with replica indices.
125
- const client_id_permutation = IdPermutation{ .random = random.int(u64) };
126
-
127
- for (clients) |*client, i| {
128
- errdefer for (clients[0..i]) |*c| c.deinit(allocator);
129
- client.* = try Client.init(
130
- allocator,
131
- // +1 so that index=0 is encoded as a valid id.
132
- client_id_permutation.encode(i + 1),
133
- options.cluster,
134
- options.replica_count,
135
- &client_pools[i],
136
- options.message_bus_options,
137
- );
138
- client.on_reply_callback = on_reply;
139
- }
140
- errdefer for (clients) |*c| c.deinit(allocator);
141
-
142
- var stalled_queue = PendingReplyQueue.init(allocator, {});
143
- errdefer stalled_queue.deinit();
144
- try stalled_queue.ensureTotalCapacity(stalled_queue_capacity);
145
-
146
- return Self{
147
- .random = random,
148
- .workload = workload,
149
- .options = options,
150
- .client_id_permutation = client_id_permutation,
151
- .clients = clients,
152
- .client_pools = client_pools,
153
- .message_pool = message_pool,
154
- .stalled_queue = stalled_queue,
155
- };
156
- }
157
-
158
- pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
159
- while (self.stalled_queue.removeOrNull()) |pending| {
160
- self.message_pool.unref(pending.request);
161
- self.message_pool.unref(pending.reply);
162
- }
163
- self.stalled_queue.deinit();
164
- self.message_pool.deinit(allocator);
165
-
166
- for (self.clients) |*client| client.deinit(allocator);
167
- allocator.free(self.clients);
168
-
169
- for (self.client_pools) |*pool| pool.deinit(allocator);
170
- allocator.free(self.client_pools);
171
- }
172
-
173
- /// The conductor is "done" when it has delivered `requests_max` requests and received all
174
- /// replies.
175
- pub fn done(self: *const Self) bool {
176
- assert(self.requests_sent <= self.options.requests_max);
177
- if (self.requests_sent < self.options.requests_max) return false;
178
-
179
- for (self.clients) |*client| {
180
- if (client.request_queue.count > 0) return false;
181
- }
182
-
183
- assert(self.workload.done());
184
- return true;
185
- }
186
-
187
- pub fn tick(self: *Self) void {
188
- for (self.clients) |*client| {
189
- // TODO(zig) Move this into init when `@returnAddress()` is available. It only needs to
190
- // be set once, it just requires a stable pointer to the Conductor.
191
- client.on_reply_context = self;
192
- client.tick();
193
- }
194
-
195
- if (self.done()) return;
196
-
197
- // Try to pick a client & queue a request.
198
-
199
- if (self.idle) {
200
- if (chance(self.random, self.options.idle_off_probability)) self.idle = false;
201
- } else {
202
- if (chance(self.random, self.options.idle_on_probability)) self.idle = true;
203
- }
204
- if (self.idle) return;
205
- if (!chance(self.random, self.options.request_probability)) return;
206
-
207
- if (self.requests_sent == self.options.requests_max) return;
208
- assert(self.requests_sent < self.options.requests_max);
209
-
210
- // Messages aren't added to `stalled_queue` until a reply arrives.
211
- // Before sending a new message, make sure there will definitely be room for it.
212
- var reserved: usize = 0;
213
- for (self.clients) |*c| {
214
- // Count the number of clients that are still waiting for a `register` to complete,
215
- // since they may start one at any time.
216
- reserved += @boolToInt(c.session == 0);
217
- // Count the number of requests queued.
218
- reserved += c.request_queue.count;
219
- }
220
-
221
- // +1 for the potential request — is there room in our queue?
222
- if (self.stalled_queue.len + reserved + 1 > stalled_queue_capacity) return;
223
-
224
- const client_index = self.random.uintLessThanBiased(usize, self.clients.len);
225
- var client = &self.clients[client_index];
226
-
227
- // Check for space in the client's own request queue.
228
- if (client.request_queue.count + 1 > constants.client_request_queue_max) return;
229
-
230
- var request_message = client.get_message();
231
- defer client.unref(request_message);
232
-
233
- const request_metadata = self.workload.build_request(
234
- client_index,
235
- @alignCast(
236
- @alignOf(vsr.Header),
237
- request_message.buffer[@sizeOf(vsr.Header)..constants.message_size_max],
238
- ),
239
- );
240
- assert(request_metadata.size <= constants.message_size_max - @sizeOf(vsr.Header));
241
-
242
- client.request(
243
- 0,
244
- request_callback,
245
- request_metadata.operation,
246
- request_message,
247
- request_metadata.size,
248
- );
249
- // Since we already checked the client's request queue for free space, `client.request()`
250
- // should always queue the request.
251
- assert(request_message == client.request_queue.tail_ptr().?.message);
252
- assert(request_message.header.client == client.id);
253
- assert(request_message.header.request == client.request_number - 1);
254
- assert(request_message.header.size == @sizeOf(vsr.Header) + request_metadata.size);
255
- assert(request_message.header.operation.cast(StateMachine) == request_metadata.operation);
256
-
257
- self.requests_sent += 1;
258
- assert(self.requests_sent <= self.options.requests_max);
259
- }
260
-
261
- /// The `request_callback` is not used. The Conductor needs access to the request/reply
262
- /// Messages to process them in the proper (op) order.
263
- ///
264
- /// See `on_reply`.
265
- fn request_callback(
266
- user_data: u128,
267
- operation: StateMachine.Operation,
268
- result: Client.Error![]const u8,
269
- ) void {
270
- _ = user_data;
271
- _ = operation;
272
- _ = result catch |err| switch (err) {
273
- error.TooManyOutstandingRequests => unreachable,
274
- };
275
- }
276
-
277
- fn on_reply(
278
- context: ?*anyopaque,
279
- client: *Client,
280
- request_message: *Message,
281
- reply_message: *Message,
282
- ) void {
283
- const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), context.?));
284
- assert(reply_message.header.cluster == self.options.cluster);
285
- assert(reply_message.header.invalid() == null);
286
- assert(reply_message.header.client == client.id);
287
- assert(reply_message.header.request == request_message.header.request);
288
- assert(reply_message.header.op >= self.stalled_op);
289
- assert(reply_message.header.command == .reply);
290
- assert(reply_message.header.operation == request_message.header.operation);
291
-
292
- const client_id = reply_message.header.client;
293
- // -1 because id=0 is not valid, so index=0→id=1.
294
- const client_index = @intCast(usize, self.client_id_permutation.decode(client_id) - 1);
295
- self.stalled_queue.add(.{
296
- .client_index = client_index,
297
- .request = self.clone_message(request_message),
298
- .reply = self.clone_message(reply_message),
299
- }) catch unreachable;
300
-
301
- if (reply_message.header.op == self.stalled_op) {
302
- self.consume_stalled_replies();
303
- }
304
- }
305
-
306
- /// Copy the message from a Client's MessagePool to the Conductor's MessagePool.
307
- ///
308
- /// The client has a finite amount of messages in its pool, and the Conductor needs to hold
309
- /// onto requests/replies until all preceeding requests/replies have arrived.
310
- ///
311
- /// Returns the Conductor's message.
312
- fn clone_message(self: *Self, message_client: *const Message) *Message {
313
- const message_conductor = self.message_pool.get_message();
314
- util.copy_disjoint(.exact, u8, message_conductor.buffer, message_client.buffer);
315
- return message_conductor;
316
- }
317
-
318
- fn consume_stalled_replies(self: *Self) void {
319
- assert(self.stalled_queue.len > 0);
320
- assert(self.stalled_queue.len <= stalled_queue_capacity);
321
- while (self.stalled_queue.peek()) |head| {
322
- assert(head.reply.header.op >= self.stalled_op);
323
- if (head.reply.header.op != self.stalled_op) break;
324
-
325
- const commit = self.stalled_queue.remove();
326
- defer self.message_pool.unref(commit.reply);
327
- defer self.message_pool.unref(commit.request);
328
-
329
- assert(commit.reply.references == 1);
330
- assert(commit.reply.header.command == .reply);
331
- assert(commit.reply.header.client == self.clients[commit.client_index].id);
332
- assert(commit.reply.header.request == commit.request.header.request);
333
- assert(commit.reply.header.op == self.stalled_op);
334
- assert(commit.reply.header.operation == commit.request.header.operation);
335
-
336
- assert(commit.request.references == 1);
337
- assert(commit.request.header.command == .request);
338
- assert(commit.request.header.client == self.clients[commit.client_index].id);
339
-
340
- log.debug("consume_stalled_replies: op={} operation={} client={} request={}", .{
341
- commit.reply.header.op,
342
- commit.reply.header.operation,
343
- commit.request.header.client,
344
- commit.request.header.request,
345
- });
346
-
347
- if (commit.request.header.operation != .register) {
348
- self.workload.on_reply(
349
- commit.client_index,
350
- commit.reply.header.operation,
351
- commit.reply.header.timestamp,
352
- commit.request.body(),
353
- commit.reply.body(),
354
- );
355
- }
356
- self.stalled_op += 1;
357
- }
358
- }
359
- };
360
- }
361
-
362
- /// Returns true, `p` percent of the time, else false.
363
- fn chance(random: std.rand.Random, p: u8) bool {
364
- assert(p <= 100);
365
- return random.uintLessThanBiased(u8, 100) < p;
366
- }