tigerbeetle-node 0.9.0 → 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 (79) hide show
  1. package/README.md +580 -179
  2. package/dist/benchmark.js +44 -36
  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 +68 -54
  18. package/dist/test.js.map +1 -1
  19. package/package-lock.json +26 -0
  20. package/package.json +13 -22
  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/scripts/download_node_headers.sh +0 -25
  28. package/src/tigerbeetle/scripts/benchmark.bat +0 -46
  29. package/src/tigerbeetle/scripts/benchmark.sh +0 -55
  30. package/src/tigerbeetle/scripts/install.sh +0 -6
  31. package/src/tigerbeetle/scripts/install_zig.bat +0 -109
  32. package/src/tigerbeetle/scripts/install_zig.sh +0 -84
  33. package/src/tigerbeetle/scripts/lint.zig +0 -199
  34. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -39
  35. package/src/tigerbeetle/scripts/vopr.bat +0 -48
  36. package/src/tigerbeetle/scripts/vopr.sh +0 -33
  37. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  38. package/src/tigerbeetle/src/benchmark.zig +0 -290
  39. package/src/tigerbeetle/src/cli.zig +0 -244
  40. package/src/tigerbeetle/src/config.zig +0 -239
  41. package/src/tigerbeetle/src/demo.zig +0 -125
  42. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  43. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  44. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -24
  45. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  46. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  47. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  48. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  49. package/src/tigerbeetle/src/fifo.zig +0 -104
  50. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  51. package/src/tigerbeetle/src/io/darwin.zig +0 -793
  52. package/src/tigerbeetle/src/io/linux.zig +0 -1038
  53. package/src/tigerbeetle/src/io/test.zig +0 -643
  54. package/src/tigerbeetle/src/io/windows.zig +0 -1161
  55. package/src/tigerbeetle/src/io.zig +0 -34
  56. package/src/tigerbeetle/src/main.zig +0 -144
  57. package/src/tigerbeetle/src/message_bus.zig +0 -1000
  58. package/src/tigerbeetle/src/message_pool.zig +0 -142
  59. package/src/tigerbeetle/src/ring_buffer.zig +0 -289
  60. package/src/tigerbeetle/src/simulator.zig +0 -417
  61. package/src/tigerbeetle/src/state_machine.zig +0 -2470
  62. package/src/tigerbeetle/src/storage.zig +0 -308
  63. package/src/tigerbeetle/src/test/cluster.zig +0 -351
  64. package/src/tigerbeetle/src/test/message_bus.zig +0 -93
  65. package/src/tigerbeetle/src/test/network.zig +0 -179
  66. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -387
  67. package/src/tigerbeetle/src/test/state_checker.zig +0 -145
  68. package/src/tigerbeetle/src/test/state_machine.zig +0 -76
  69. package/src/tigerbeetle/src/test/storage.zig +0 -438
  70. package/src/tigerbeetle/src/test/time.zig +0 -84
  71. package/src/tigerbeetle/src/tigerbeetle.zig +0 -222
  72. package/src/tigerbeetle/src/time.zig +0 -113
  73. package/src/tigerbeetle/src/unit_tests.zig +0 -14
  74. package/src/tigerbeetle/src/vsr/client.zig +0 -505
  75. package/src/tigerbeetle/src/vsr/clock.zig +0 -812
  76. package/src/tigerbeetle/src/vsr/journal.zig +0 -2293
  77. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  78. package/src/tigerbeetle/src/vsr/replica.zig +0 -5015
  79. package/src/tigerbeetle/src/vsr.zig +0 -1017
@@ -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
- }