tigerbeetle-node 0.8.1 → 0.9.143

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/README.md +584 -184
  2. package/dist/benchmark.js +59 -51
  3. package/dist/benchmark.js.map +1 -1
  4. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  5. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  6. package/dist/bin/aarch64-macos/client.node +0 -0
  7. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  8. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  9. package/dist/bin/x86_64-macos/client.node +0 -0
  10. package/dist/bin/x86_64-windows/client.node +0 -0
  11. package/dist/bindings.d.ts +141 -0
  12. package/dist/bindings.js +112 -0
  13. package/dist/bindings.js.map +1 -0
  14. package/dist/index.d.ts +2 -125
  15. package/dist/index.js +51 -101
  16. package/dist/index.js.map +1 -1
  17. package/dist/test.js +69 -55
  18. package/dist/test.js.map +1 -1
  19. package/package-lock.json +26 -0
  20. package/package.json +17 -28
  21. package/src/benchmark.ts +58 -49
  22. package/src/bindings.ts +631 -0
  23. package/src/index.ts +71 -163
  24. package/src/node.zig +169 -148
  25. package/src/test.ts +71 -57
  26. package/src/translate.zig +19 -36
  27. package/.yarn/releases/yarn-berry.cjs +0 -55
  28. package/.yarnrc.yml +0 -1
  29. package/scripts/download_node_headers.sh +0 -25
  30. package/scripts/postinstall.sh +0 -6
  31. package/src/tigerbeetle/scripts/benchmark.bat +0 -46
  32. package/src/tigerbeetle/scripts/benchmark.sh +0 -55
  33. package/src/tigerbeetle/scripts/install.sh +0 -6
  34. package/src/tigerbeetle/scripts/install_zig.bat +0 -109
  35. package/src/tigerbeetle/scripts/install_zig.sh +0 -84
  36. package/src/tigerbeetle/scripts/lint.zig +0 -199
  37. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -39
  38. package/src/tigerbeetle/scripts/vopr.bat +0 -48
  39. package/src/tigerbeetle/scripts/vopr.sh +0 -33
  40. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  41. package/src/tigerbeetle/src/benchmark.zig +0 -290
  42. package/src/tigerbeetle/src/cli.zig +0 -244
  43. package/src/tigerbeetle/src/config.zig +0 -239
  44. package/src/tigerbeetle/src/demo.zig +0 -125
  45. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  46. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  47. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -24
  48. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  49. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  50. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  51. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  52. package/src/tigerbeetle/src/fifo.zig +0 -104
  53. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  54. package/src/tigerbeetle/src/io/darwin.zig +0 -793
  55. package/src/tigerbeetle/src/io/linux.zig +0 -1038
  56. package/src/tigerbeetle/src/io/test.zig +0 -643
  57. package/src/tigerbeetle/src/io/windows.zig +0 -1161
  58. package/src/tigerbeetle/src/io.zig +0 -34
  59. package/src/tigerbeetle/src/main.zig +0 -144
  60. package/src/tigerbeetle/src/message_bus.zig +0 -1000
  61. package/src/tigerbeetle/src/message_pool.zig +0 -142
  62. package/src/tigerbeetle/src/ring_buffer.zig +0 -289
  63. package/src/tigerbeetle/src/simulator.zig +0 -417
  64. package/src/tigerbeetle/src/state_machine.zig +0 -2470
  65. package/src/tigerbeetle/src/storage.zig +0 -308
  66. package/src/tigerbeetle/src/test/cluster.zig +0 -351
  67. package/src/tigerbeetle/src/test/message_bus.zig +0 -93
  68. package/src/tigerbeetle/src/test/network.zig +0 -179
  69. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -387
  70. package/src/tigerbeetle/src/test/state_checker.zig +0 -145
  71. package/src/tigerbeetle/src/test/state_machine.zig +0 -76
  72. package/src/tigerbeetle/src/test/storage.zig +0 -438
  73. package/src/tigerbeetle/src/test/time.zig +0 -84
  74. package/src/tigerbeetle/src/tigerbeetle.zig +0 -222
  75. package/src/tigerbeetle/src/time.zig +0 -113
  76. package/src/tigerbeetle/src/unit_tests.zig +0 -14
  77. package/src/tigerbeetle/src/vsr/client.zig +0 -505
  78. package/src/tigerbeetle/src/vsr/clock.zig +0 -812
  79. package/src/tigerbeetle/src/vsr/journal.zig +0 -2293
  80. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  81. package/src/tigerbeetle/src/vsr/replica.zig +0 -5015
  82. package/src/tigerbeetle/src/vsr.zig +0 -1017
  83. package/yarn.lock +0 -42
@@ -1,417 +0,0 @@
1
- const std = @import("std");
2
- const builtin = @import("builtin");
3
- const assert = std.debug.assert;
4
- const mem = std.mem;
5
-
6
- const config = @import("config.zig");
7
-
8
- const Client = @import("test/cluster.zig").Client;
9
- const Cluster = @import("test/cluster.zig").Cluster;
10
- const Header = @import("vsr.zig").Header;
11
- const Replica = @import("test/cluster.zig").Replica;
12
- const StateChecker = @import("test/state_checker.zig").StateChecker;
13
- const StateMachine = @import("test/cluster.zig").StateMachine;
14
- const PartitionMode = @import("test/packet_simulator.zig").PartitionMode;
15
-
16
- /// The `log` namespace in this root file is required to implement our custom `log` function.
17
- const output = std.log.scoped(.state_checker);
18
-
19
- /// Set this to `false` if you want to see how literally everything works.
20
- /// This will run much slower but will trace all logic across the cluster.
21
- const log_state_transitions_only = builtin.mode != .Debug;
22
-
23
- const log_health = std.log.scoped(.health);
24
-
25
- /// You can fine tune your log levels even further (debug/info/notice/warn/err/crit/alert/emerg):
26
- pub const log_level: std.log.Level = if (log_state_transitions_only) .info else .debug;
27
-
28
- var cluster: *Cluster = undefined;
29
-
30
- pub fn main() !void {
31
- // TODO Use std.testing.allocator when all deinit() leaks are fixed.
32
- const allocator = std.heap.page_allocator;
33
-
34
- var args = std.process.args();
35
-
36
- // Skip argv[0] which is the name of this executable:
37
- _ = args_next(&args, allocator);
38
-
39
- const seed_random = std.crypto.random.int(u64);
40
- const seed = seed_from_arg: {
41
- const arg_two = args_next(&args, allocator) orelse break :seed_from_arg seed_random;
42
- defer allocator.free(arg_two);
43
- break :seed_from_arg parse_seed(arg_two);
44
- };
45
-
46
- if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
47
- // We do not support ReleaseFast or ReleaseSmall because they disable assertions.
48
- @panic("the simulator must be run with -OReleaseSafe");
49
- }
50
-
51
- if (seed == seed_random) {
52
- if (builtin.mode != .ReleaseSafe) {
53
- // If no seed is provided, than Debug is too slow and ReleaseSafe is much faster.
54
- @panic("no seed provided: the simulator must be run with -OReleaseSafe");
55
- }
56
- if (log_level == .debug) {
57
- output.warn("no seed provided: full debug logs are enabled, this will be slow", .{});
58
- }
59
- }
60
-
61
- var prng = std.rand.DefaultPrng.init(seed);
62
- const random = prng.random();
63
-
64
- const replica_count = 1 + random.uintLessThan(u8, config.replicas_max);
65
- const client_count = 1 + random.uintLessThan(u8, config.clients_max);
66
- const node_count = replica_count + client_count;
67
-
68
- const ticks_max = 100_000_000;
69
- const request_probability = 1 + random.uintLessThan(u8, 99);
70
- const idle_on_probability = random.uintLessThan(u8, 20);
71
- const idle_off_probability = 10 + random.uintLessThan(u8, 10);
72
-
73
- cluster = try Cluster.create(allocator, random, .{
74
- .cluster = 0,
75
- .replica_count = replica_count,
76
- .client_count = client_count,
77
- .seed = random.int(u64),
78
- .network_options = .{
79
- .packet_simulator_options = .{
80
- .replica_count = replica_count,
81
- .client_count = client_count,
82
- .node_count = node_count,
83
-
84
- .seed = random.int(u64),
85
- .one_way_delay_mean = 3 + random.uintLessThan(u16, 10),
86
- .one_way_delay_min = random.uintLessThan(u16, 3),
87
- .packet_loss_probability = random.uintLessThan(u8, 30),
88
- .path_maximum_capacity = 2 + random.uintLessThan(u8, 19),
89
- .path_clog_duration_mean = random.uintLessThan(u16, 500),
90
- .path_clog_probability = random.uintLessThan(u8, 2),
91
- .packet_replay_probability = random.uintLessThan(u8, 50),
92
-
93
- .partition_mode = random_partition_mode(random),
94
- .partition_probability = random.uintLessThan(u8, 3),
95
- .unpartition_probability = 1 + random.uintLessThan(u8, 10),
96
- .partition_stability = 100 + random.uintLessThan(u32, 100),
97
- .unpartition_stability = random.uintLessThan(u32, 20),
98
- },
99
- },
100
- .storage_options = .{
101
- .seed = random.int(u64),
102
- .read_latency_min = random.uintLessThan(u16, 3),
103
- .read_latency_mean = 3 + random.uintLessThan(u16, 10),
104
- .write_latency_min = random.uintLessThan(u16, 3),
105
- .write_latency_mean = 3 + random.uintLessThan(u16, 100),
106
- .read_fault_probability = random.uintLessThan(u8, 10),
107
- .write_fault_probability = random.uintLessThan(u8, 10),
108
- },
109
- .health_options = .{
110
- .crash_probability = 0.0001,
111
- .crash_stability = random.uintLessThan(u32, 1_000),
112
- .restart_probability = 0.01,
113
- .restart_stability = random.uintLessThan(u32, 1_000),
114
- },
115
- });
116
- defer cluster.destroy();
117
-
118
- cluster.state_checker = try StateChecker.init(allocator, cluster);
119
- defer cluster.state_checker.deinit();
120
-
121
- for (cluster.replicas) |*replica| {
122
- replica.on_change_state = on_change_replica;
123
- }
124
- cluster.on_change_state = on_change_replica;
125
-
126
- output.info(
127
- \\
128
- \\ SEED={}
129
- \\
130
- \\ replicas={}
131
- \\ clients={}
132
- \\ request_probability={}%
133
- \\ idle_on_probability={}%
134
- \\ idle_off_probability={}%
135
- \\ one_way_delay_mean={} ticks
136
- \\ one_way_delay_min={} ticks
137
- \\ packet_loss_probability={}%
138
- \\ path_maximum_capacity={} messages
139
- \\ path_clog_duration_mean={} ticks
140
- \\ path_clog_probability={}%
141
- \\ packet_replay_probability={}%
142
- \\ partition_mode={}
143
- \\ partition_probability={}%
144
- \\ unpartition_probability={}%
145
- \\ partition_stability={} ticks
146
- \\ unpartition_stability={} ticks
147
- \\ read_latency_min={}
148
- \\ read_latency_mean={}
149
- \\ write_latency_min={}
150
- \\ write_latency_mean={}
151
- \\ read_fault_probability={}%
152
- \\ write_fault_probability={}%
153
- \\ crash_probability={d}%
154
- \\ crash_stability={} ticks
155
- \\ restart_probability={d}%
156
- \\ restart_stability={} ticks
157
- \\
158
- , .{
159
- seed,
160
- replica_count,
161
- client_count,
162
- request_probability,
163
- idle_on_probability,
164
- idle_off_probability,
165
- cluster.options.network_options.packet_simulator_options.one_way_delay_mean,
166
- cluster.options.network_options.packet_simulator_options.one_way_delay_min,
167
- cluster.options.network_options.packet_simulator_options.packet_loss_probability,
168
- cluster.options.network_options.packet_simulator_options.path_maximum_capacity,
169
- cluster.options.network_options.packet_simulator_options.path_clog_duration_mean,
170
- cluster.options.network_options.packet_simulator_options.path_clog_probability,
171
- cluster.options.network_options.packet_simulator_options.packet_replay_probability,
172
- cluster.options.network_options.packet_simulator_options.partition_mode,
173
- cluster.options.network_options.packet_simulator_options.partition_probability,
174
- cluster.options.network_options.packet_simulator_options.unpartition_probability,
175
- cluster.options.network_options.packet_simulator_options.partition_stability,
176
- cluster.options.network_options.packet_simulator_options.unpartition_stability,
177
- cluster.options.storage_options.read_latency_min,
178
- cluster.options.storage_options.read_latency_mean,
179
- cluster.options.storage_options.write_latency_min,
180
- cluster.options.storage_options.write_latency_mean,
181
- cluster.options.storage_options.read_fault_probability,
182
- cluster.options.storage_options.write_fault_probability,
183
- cluster.options.health_options.crash_probability * 100,
184
- cluster.options.health_options.crash_stability,
185
- cluster.options.health_options.restart_probability * 100,
186
- cluster.options.health_options.restart_stability,
187
- });
188
-
189
- var requests_sent: u64 = 0;
190
- var idle = false;
191
-
192
- // The minimum number of healthy replicas required for a crashed replica to be able to recover.
193
- const replica_normal_min = replicas: {
194
- if (replica_count == 1) {
195
- // A cluster of 1 can crash safely (as long as there is no disk corruption) since it
196
- // does not run the recovery protocol.
197
- break :replicas 0;
198
- } else {
199
- break :replicas cluster.replicas[0].quorum_view_change;
200
- }
201
- };
202
-
203
- // Disable most faults at startup, so that the replicas don't get stuck in recovery mode.
204
- for (cluster.storages) |*storage, i| {
205
- storage.faulty = replica_normal_min <= i;
206
- }
207
-
208
- // TODO When storage is supported, run more transitions than fit in the journal.
209
- const transitions_max = config.journal_slot_count / 2;
210
- var tick: u64 = 0;
211
- while (tick < ticks_max) : (tick += 1) {
212
- const health_options = &cluster.options.health_options;
213
- // The maximum number of replicas that can crash, with the cluster still able to recover.
214
- var crashes = cluster.replica_normal_count() -| replica_normal_min;
215
-
216
- for (cluster.storages) |*storage, replica| {
217
- if (cluster.replicas[replica].journal.recovered) {
218
-
219
- // TODO Remove this workaround when VSR recovery protocol is disabled.
220
- // When only the minimum number of replicas are healthy (no more crashes allowed),
221
- // disable storage faults on all healthy replicas.
222
- //
223
- // This is a workaround to avoid the deadlock that occurs when (for example) in a
224
- // cluster of 3 replicas, one is down, another has a corrupt prepare, and the last does
225
- // not have the prepare. The two healthy replicas can never complete a view change,
226
- // because two replicas are not enough to nack, and the unhealthy replica cannot
227
- // complete the VSR recovery protocol either.
228
- if (cluster.health[replica] == .up and crashes == 0) {
229
- storage.faulty = false;
230
- } else {
231
- // When a journal recovers for the first time, enable its storage faults.
232
- // Future crashes will recover in the presence of faults.
233
- storage.faulty = true;
234
- }
235
- }
236
- storage.tick();
237
- }
238
-
239
- for (cluster.replicas) |*replica| {
240
- switch (cluster.health[replica.replica]) {
241
- .up => |*ticks| {
242
- ticks.* -|= 1;
243
- replica.tick();
244
- cluster.state_checker.check_state(replica.replica);
245
-
246
- if (ticks.* != 0) continue;
247
- if (crashes == 0) continue;
248
- if (cluster.storages[replica.replica].writes.count() == 0) {
249
- if (!chance_f64(random, health_options.crash_probability)) continue;
250
- } else {
251
- if (!chance_f64(random, health_options.crash_probability * 10.0)) continue;
252
- }
253
-
254
- if (!try cluster.crash_replica(replica.replica)) continue;
255
- log_health.debug("crash replica={}", .{replica.replica});
256
- crashes -= 1;
257
- },
258
- .down => |*ticks| {
259
- ticks.* -|= 1;
260
- // Keep ticking the time so that it won't have diverged too far to synchronize
261
- // when the replica restarts.
262
- replica.clock.time.tick();
263
- assert(replica.status == .recovering);
264
- if (ticks.* == 0 and chance_f64(random, health_options.restart_probability)) {
265
- cluster.health[replica.replica] = .{ .up = health_options.restart_stability };
266
- log_health.debug("restart replica={}", .{replica.replica});
267
- }
268
- },
269
- }
270
- }
271
-
272
- cluster.network.packet_simulator.tick(cluster.health);
273
-
274
- for (cluster.clients) |*client| client.tick();
275
-
276
- if (cluster.state_checker.transitions == transitions_max) {
277
- if (cluster.state_checker.convergence() and
278
- cluster.replica_up_count() == replica_count)
279
- {
280
- break;
281
- }
282
- continue;
283
- } else {
284
- assert(cluster.state_checker.transitions < transitions_max);
285
- }
286
-
287
- if (requests_sent < transitions_max) {
288
- if (idle) {
289
- if (chance(random, idle_off_probability)) idle = false;
290
- } else {
291
- if (chance(random, request_probability)) {
292
- if (send_request(random)) requests_sent += 1;
293
- }
294
- if (chance(random, idle_on_probability)) idle = true;
295
- }
296
- }
297
- }
298
-
299
- if (cluster.state_checker.transitions < transitions_max) {
300
- output.err("you can reproduce this failure with seed={}", .{seed});
301
- @panic("unable to complete transitions_max before ticks_max");
302
- }
303
-
304
- assert(cluster.state_checker.convergence());
305
-
306
- output.info("\n PASSED ({} ticks)", .{tick});
307
- }
308
-
309
- /// Returns true, `p` percent of the time, else false.
310
- fn chance(random: std.rand.Random, p: u8) bool {
311
- assert(p <= 100);
312
- return random.uintLessThan(u8, 100) < p;
313
- }
314
-
315
- /// Returns true, `p` percent of the time, else false.
316
- fn chance_f64(random: std.rand.Random, p: f64) bool {
317
- assert(p <= 100.0);
318
- return random.float(f64) < p;
319
- }
320
-
321
- /// Returns the next argument for the simulator or null (if none available)
322
- fn args_next(args: *std.process.ArgIterator, allocator: std.mem.Allocator) ?[:0]const u8 {
323
- const err_or_bytes = args.next(allocator) orelse return null;
324
- return err_or_bytes catch @panic("Unable to extract next value from args");
325
- }
326
-
327
- fn on_change_replica(replica: *Replica) void {
328
- assert(cluster.state_machines[replica.replica].state == replica.state_machine.state);
329
- cluster.state_checker.check_state(replica.replica);
330
- }
331
-
332
- fn send_request(random: std.rand.Random) bool {
333
- const client_index = random.uintLessThan(u8, cluster.options.client_count);
334
-
335
- const client = &cluster.clients[client_index];
336
- const checker_request_queue = &cluster.state_checker.client_requests[client_index];
337
-
338
- // Ensure that we don't shortchange testing of the full client request queue length:
339
- assert(client.request_queue.buffer.len <= checker_request_queue.buffer.len);
340
- if (client.request_queue.full()) return false;
341
- if (checker_request_queue.full()) return false;
342
-
343
- const message = client.get_message();
344
- defer client.unref(message);
345
-
346
- const body_size_max = config.message_size_max - @sizeOf(Header);
347
- const body_size: u32 = switch (random.uintLessThan(u8, 100)) {
348
- 0...10 => 0,
349
- 11...89 => random.uintLessThan(u32, body_size_max),
350
- 90...99 => body_size_max,
351
- else => unreachable,
352
- };
353
-
354
- const body = message.buffer[@sizeOf(Header)..][0..body_size];
355
- if (chance(random, 10)) {
356
- std.mem.set(u8, body, 0);
357
- } else {
358
- random.bytes(body);
359
- }
360
-
361
- // While hashing the client ID with the request body prevents input collisions across clients,
362
- // it's still possible for the same client to generate the same body, and therefore input hash.
363
- const client_input = StateMachine.hash(client.id, body);
364
- checker_request_queue.push_assume_capacity(client_input);
365
- std.log.scoped(.test_client).debug("client {} sending input={x}", .{
366
- client_index,
367
- client_input,
368
- });
369
-
370
- client.request(0, client_callback, .hash, message, body_size);
371
-
372
- return true;
373
- }
374
-
375
- fn client_callback(
376
- user_data: u128,
377
- operation: StateMachine.Operation,
378
- results: Client.Error![]const u8,
379
- ) void {
380
- _ = operation;
381
- _ = results catch unreachable;
382
-
383
- assert(user_data == 0);
384
- }
385
-
386
- /// Returns a random partitioning mode, excluding .custom
387
- fn random_partition_mode(random: std.rand.Random) PartitionMode {
388
- const typeInfo = @typeInfo(PartitionMode).Enum;
389
- var enumAsInt = random.uintAtMost(typeInfo.tag_type, typeInfo.fields.len - 2);
390
- if (enumAsInt >= @enumToInt(PartitionMode.custom)) enumAsInt += 1;
391
- return @intToEnum(PartitionMode, enumAsInt);
392
- }
393
-
394
- fn parse_seed(bytes: []const u8) u64 {
395
- return std.fmt.parseUnsigned(u64, bytes, 10) catch |err| switch (err) {
396
- error.Overflow => @panic("seed exceeds a 64-bit unsigned integer"),
397
- error.InvalidCharacter => @panic("seed contains an invalid character"),
398
- };
399
- }
400
-
401
- pub fn log(
402
- comptime level: std.log.Level,
403
- comptime scope: @TypeOf(.EnumLiteral),
404
- comptime format: []const u8,
405
- args: anytype,
406
- ) void {
407
- if (log_state_transitions_only and scope != .state_checker) return;
408
-
409
- const prefix_default = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
410
- const prefix = if (log_state_transitions_only) "" else prefix_default;
411
-
412
- // Print the message to stdout, silently ignoring any errors
413
- const stderr = std.io.getStdErr().writer();
414
- std.debug.getStderrMutex().lock();
415
- defer std.debug.getStderrMutex().unlock();
416
- nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
417
- }