tigerbeetle-node 0.11.12 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,443 +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 message_pool = @import("../message_pool.zig");
7
- const MessagePool = message_pool.MessagePool;
8
- const Message = MessagePool.Message;
9
-
10
- const Storage = @import("storage.zig").Storage;
11
- const StorageFaultAtlas = @import("storage.zig").ClusterFaultAtlas;
12
- const Time = @import("time.zig").Time;
13
- const IdPermutation = @import("id.zig").IdPermutation;
14
-
15
- const MessageBus = @import("cluster/message_bus.zig").MessageBus;
16
- const Network = @import("cluster/network.zig").Network;
17
- const NetworkOptions = @import("cluster/network.zig").NetworkOptions;
18
- const StateCheckerType = @import("cluster/state_checker.zig").StateCheckerType;
19
- const StorageCheckerType = @import("cluster/storage_checker.zig").StorageCheckerType;
20
-
21
- const vsr = @import("../vsr.zig");
22
- pub const ReplicaFormat = vsr.ReplicaFormatType(Storage);
23
- const SuperBlock = vsr.SuperBlockType(Storage);
24
- const superblock_zone_size = @import("../vsr/superblock.zig").superblock_zone_size;
25
-
26
- pub const ReplicaHealth = enum { up, down };
27
-
28
- /// Integer values represent exit codes.
29
- // TODO This doesn't really belong in Cluster, but it is needed here so that StateChecker failures
30
- // use the particular exit code.
31
- pub const Failure = enum(u8) {
32
- /// Any assertion crash will be given an exit code of 127 by default.
33
- crash = 127,
34
- liveness = 128,
35
- correctness = 129,
36
- };
37
-
38
- /// Shift the id-generating index because the simulator network expects client ids to never collide
39
- /// with a replica index.
40
- const client_id_permutation_shift = constants.replicas_max;
41
-
42
- pub fn ClusterType(comptime StateMachineType: fn (comptime Storage: type, comptime constants: anytype) type) type {
43
- return struct {
44
- const Self = @This();
45
-
46
- pub const StateMachine = StateMachineType(Storage, .{
47
- .message_body_size_max = constants.message_body_size_max,
48
- });
49
- pub const Replica = vsr.ReplicaType(StateMachine, MessageBus, Storage, Time);
50
- pub const Client = vsr.Client(StateMachine, MessageBus);
51
- pub const StateChecker = StateCheckerType(Client, Replica);
52
- pub const StorageChecker = StorageCheckerType(Replica);
53
-
54
- pub const Options = struct {
55
- cluster_id: u32,
56
- replica_count: u8,
57
- client_count: u8,
58
- storage_size_limit: u64,
59
- storage_fault_atlas: StorageFaultAtlas.Options,
60
- seed: u64,
61
-
62
- network: NetworkOptions,
63
- storage: Storage.Options,
64
- state_machine: StateMachine.Options,
65
- };
66
-
67
- allocator: mem.Allocator,
68
- options: Options,
69
- on_client_reply: fn (
70
- cluster: *Self,
71
- client: usize,
72
- request: *Message,
73
- reply: *Message,
74
- ) void,
75
-
76
- network: *Network,
77
- storages: []Storage,
78
- storage_fault_atlas: *StorageFaultAtlas,
79
- replicas: []Replica,
80
- replica_pools: []MessagePool,
81
- replica_health: []ReplicaHealth,
82
-
83
- clients: []Client,
84
- client_pools: []MessagePool,
85
- client_id_permutation: IdPermutation,
86
-
87
- state_checker: StateChecker,
88
- storage_checker: StorageChecker,
89
-
90
- context: ?*anyopaque = null,
91
-
92
- pub fn init(
93
- allocator: mem.Allocator,
94
- /// Includes command=register messages.
95
- on_client_reply: fn (
96
- cluster: *Self,
97
- client: usize,
98
- request: *Message,
99
- reply: *Message,
100
- ) void,
101
- options: Options,
102
- ) !*Self {
103
- assert(options.replica_count >= 1);
104
- assert(options.replica_count <= 6);
105
- assert(options.client_count > 0);
106
- assert(options.storage_size_limit % constants.sector_size == 0);
107
- assert(options.storage_size_limit <= constants.storage_size_max);
108
- assert(options.storage.replica_index == null);
109
- assert(options.storage.fault_atlas == null);
110
-
111
- var prng = std.rand.DefaultPrng.init(options.seed);
112
- const random = prng.random();
113
-
114
- // TODO(Zig) Client.init()'s MessagePool.Options require a reference to the network — use
115
- // @returnAddress() instead.
116
- var network = try allocator.create(Network);
117
- errdefer allocator.destroy(network);
118
-
119
- network.* = try Network.init(
120
- allocator,
121
- options.replica_count,
122
- options.client_count,
123
- options.network,
124
- );
125
- errdefer network.deinit();
126
-
127
- // TODO(Zig) @returnAddress()
128
- var storage_fault_atlas = try allocator.create(StorageFaultAtlas);
129
- errdefer allocator.destroy(storage_fault_atlas);
130
-
131
- storage_fault_atlas.* = StorageFaultAtlas.init(
132
- options.replica_count,
133
- random,
134
- options.storage_fault_atlas,
135
- );
136
-
137
- const storages = try allocator.alloc(Storage, options.replica_count);
138
- errdefer allocator.free(storages);
139
-
140
- for (storages) |*storage, replica_index| {
141
- var storage_options = options.storage;
142
- storage_options.replica_index = @intCast(u8, replica_index);
143
- storage_options.fault_atlas = storage_fault_atlas;
144
- storage.* = try Storage.init(allocator, options.storage_size_limit, storage_options);
145
- // Disable most faults at startup, so that the replicas don't get stuck recovering_head.
146
- storage.faulty = replica_index >= vsr.quorums(options.replica_count).view_change;
147
- }
148
- errdefer for (storages) |*storage| storage.deinit(allocator);
149
-
150
- var replica_pools = try allocator.alloc(MessagePool, options.replica_count);
151
- errdefer allocator.free(replica_pools);
152
-
153
- for (replica_pools) |*pool, i| {
154
- errdefer for (replica_pools[0..i]) |*p| p.deinit(allocator);
155
- pool.* = try MessagePool.init(allocator, .replica);
156
- }
157
- errdefer for (replica_pools) |*pool| pool.deinit(allocator);
158
-
159
- const replicas = try allocator.alloc(Replica, options.replica_count);
160
- errdefer allocator.free(replicas);
161
-
162
- const replica_health = try allocator.alloc(ReplicaHealth, options.replica_count);
163
- errdefer allocator.free(replica_health);
164
- mem.set(ReplicaHealth, replica_health, .up);
165
-
166
- var client_pools = try allocator.alloc(MessagePool, options.client_count);
167
- errdefer allocator.free(client_pools);
168
-
169
- for (client_pools) |*pool, i| {
170
- errdefer for (client_pools[0..i]) |*p| p.deinit(allocator);
171
- pool.* = try MessagePool.init(allocator, .client);
172
- }
173
- errdefer for (replica_pools) |*pool| pool.deinit(allocator);
174
-
175
- const client_id_permutation = IdPermutation.generate(random);
176
- var clients = try allocator.alloc(Client, options.client_count);
177
- errdefer allocator.free(clients);
178
-
179
- for (clients) |*client, i| {
180
- errdefer for (clients[0..i]) |*c| c.deinit(allocator);
181
- client.* = try Client.init(
182
- allocator,
183
- client_id_permutation.encode(i + client_id_permutation_shift),
184
- options.cluster_id,
185
- options.replica_count,
186
- &client_pools[i],
187
- .{ .network = network },
188
- );
189
- network.link(client.message_bus.process, &client.message_bus);
190
- }
191
- errdefer for (clients) |*c| c.deinit(allocator);
192
-
193
- var state_checker =
194
- try StateChecker.init(allocator, options.cluster_id, replicas, clients);
195
- errdefer state_checker.deinit();
196
-
197
- var storage_checker = StorageChecker.init(allocator);
198
- errdefer storage_checker.deinit();
199
-
200
- // Format each replica's storage (equivalent to "tigerbeetle format ...").
201
- for (storages) |*storage, replica_index| {
202
- var superblock = try SuperBlock.init(allocator, .{
203
- .storage = storage,
204
- .message_pool = &replica_pools[replica_index],
205
- .storage_size_limit = options.storage_size_limit,
206
- });
207
- defer superblock.deinit(allocator);
208
-
209
- try vsr.format(
210
- Storage,
211
- allocator,
212
- options.cluster_id,
213
- @intCast(u8, replica_index),
214
- storage,
215
- &superblock,
216
- );
217
- }
218
-
219
- // We must heap-allocate the cluster since its pointer will be attached to the replica.
220
- // TODO(Zig) @returnAddress().
221
- var cluster = try allocator.create(Self);
222
- errdefer allocator.destroy(cluster);
223
-
224
- cluster.* = Self{
225
- .allocator = allocator,
226
- .options = options,
227
- .on_client_reply = on_client_reply,
228
- .network = network,
229
- .storages = storages,
230
- .storage_fault_atlas = storage_fault_atlas,
231
- .replicas = replicas,
232
- .replica_pools = replica_pools,
233
- .replica_health = replica_health,
234
- .clients = clients,
235
- .client_pools = client_pools,
236
- .client_id_permutation = client_id_permutation,
237
- .state_checker = state_checker,
238
- .storage_checker = storage_checker,
239
- };
240
-
241
- for (cluster.replicas) |_, replica_index| {
242
- try cluster.open_replica(@intCast(u8, replica_index), .{
243
- .resolution = constants.tick_ms * std.time.ns_per_ms,
244
- .offset_type = .linear,
245
- .offset_coefficient_A = 0,
246
- .offset_coefficient_B = 0,
247
- });
248
- }
249
- errdefer for (cluster.replicas) |*replica| replica.deinit(allocator);
250
-
251
- for (clients) |*client| {
252
- client.on_reply_context = cluster;
253
- client.on_reply_callback = client_on_reply;
254
- }
255
-
256
- return cluster;
257
- }
258
-
259
- pub fn deinit(cluster: *Self) void {
260
- cluster.storage_checker.deinit();
261
- cluster.state_checker.deinit();
262
- cluster.network.deinit();
263
- for (cluster.clients) |*client| client.deinit(cluster.allocator);
264
- for (cluster.client_pools) |*pool| pool.deinit(cluster.allocator);
265
- for (cluster.replicas) |*replica| replica.deinit(cluster.allocator);
266
- for (cluster.replica_pools) |*pool| pool.deinit(cluster.allocator);
267
- for (cluster.storages) |*storage| storage.deinit(cluster.allocator);
268
-
269
- cluster.allocator.free(cluster.clients);
270
- cluster.allocator.free(cluster.client_pools);
271
- cluster.allocator.free(cluster.replicas);
272
- cluster.allocator.free(cluster.replica_health);
273
- cluster.allocator.free(cluster.replica_pools);
274
- cluster.allocator.free(cluster.storages);
275
- cluster.allocator.destroy(cluster.storage_fault_atlas);
276
- cluster.allocator.destroy(cluster.network);
277
- cluster.allocator.destroy(cluster);
278
- }
279
-
280
- pub fn tick(cluster: *Self) void {
281
- cluster.network.tick();
282
-
283
- for (cluster.clients) |*client| client.tick();
284
- for (cluster.storages) |*storage| storage.tick();
285
- for (cluster.replicas) |*replica, i| {
286
- switch (cluster.replica_health[i]) {
287
- .up => replica.tick(),
288
- // Keep ticking the time so that it won't have diverged too far to synchronize
289
- // when the replica restarts.
290
- .down => replica.clock.time.tick(),
291
- }
292
- on_replica_change_state(replica);
293
- }
294
- }
295
-
296
- pub fn restart_replica(cluster: *Self, replica_index: u8) void {
297
- assert(cluster.replica_health[replica_index] == .down);
298
-
299
- cluster.network.process_enable(.{ .replica = replica_index });
300
- cluster.replica_health[replica_index] = .up;
301
- }
302
-
303
- /// Reset a replica to its initial state, simulating a random crash/panic.
304
- /// Leave the persistent storage untouched, and leave any currently
305
- /// inflight messages to/from the replica in the network.
306
- ///
307
- /// Returns whether the replica was crashed.
308
- /// Returns an error when the replica was unable to recover (open).
309
- pub fn crash_replica(cluster: *Self, replica_index: u8) !void {
310
- assert(cluster.replica_health[replica_index] == .up);
311
-
312
- // Reset the storage before the replica so that pending writes can (partially) finish.
313
- cluster.storages[replica_index].reset();
314
-
315
- const replica = &cluster.replicas[replica_index];
316
- const replica_time = replica.time;
317
- replica.deinit(cluster.allocator);
318
- cluster.network.process_disable(.{ .replica = replica_index });
319
- cluster.replica_health[replica_index] = .down;
320
-
321
- // Ensure that none of the replica's messages leaked when it was deinitialized.
322
- var messages_in_pool: usize = 0;
323
- const message_bus = cluster.network.get_message_bus(.{ .replica = replica_index });
324
- {
325
- var it = message_bus.pool.free_list;
326
- while (it) |message| : (it = message.next) messages_in_pool += 1;
327
- }
328
- assert(messages_in_pool == message_pool.messages_max_replica);
329
-
330
- // Logically it would make more sense to run this during restart, not immediately following
331
- // the crash. But having it here allows the replica's MessageBus to initialize and begin
332
- // queueing packets.
333
- //
334
- // Pass the old replica's Time through to the new replica. It will continue to tick while
335
- // the replica is crashed, to ensure the clocks don't desyncronize too far to recover.
336
- try cluster.open_replica(replica_index, replica_time);
337
- }
338
-
339
- fn open_replica(cluster: *Self, replica_index: u8, time: Time) !void {
340
- var replica = &cluster.replicas[replica_index];
341
- try replica.open(
342
- cluster.allocator,
343
- .{
344
- .replica_count = @intCast(u8, cluster.replicas.len),
345
- .storage = &cluster.storages[replica_index],
346
- // TODO Test restarting with a higher storage limit.
347
- .storage_size_limit = cluster.options.storage_size_limit,
348
- .message_pool = &cluster.replica_pools[replica_index],
349
- .time = time,
350
- .state_machine_options = cluster.options.state_machine,
351
- .message_bus_options = .{ .network = cluster.network },
352
- },
353
- );
354
- assert(replica.cluster == cluster.options.cluster_id);
355
- assert(replica.replica == replica_index);
356
- assert(replica.replica_count == cluster.replicas.len);
357
-
358
- replica.context = cluster;
359
- replica.on_change_state = on_replica_change_state;
360
- replica.on_compact = on_replica_compact;
361
- replica.on_checkpoint = on_replica_checkpoint;
362
- cluster.network.link(replica.message_bus.process, &replica.message_bus);
363
- }
364
-
365
- pub fn request(
366
- cluster: *Self,
367
- client_index: usize,
368
- request_operation: StateMachine.Operation,
369
- request_message: *Message,
370
- request_body_size: usize,
371
- ) void {
372
- cluster.clients[client_index].request(
373
- undefined,
374
- request_callback,
375
- request_operation,
376
- request_message,
377
- request_body_size,
378
- );
379
- }
380
-
381
- /// The `request_callback` is not used — Cluster uses `Client.on_reply_{context,callback}`
382
- /// instead because:
383
- /// - Cluster needs access to the request
384
- /// - Cluster needs access to the reply message (not just the body)
385
- /// - Cluster needs to know about command=register messages
386
- ///
387
- /// See `on_reply`.
388
- fn request_callback(
389
- user_data: u128,
390
- operation: StateMachine.Operation,
391
- result: Client.Error![]const u8,
392
- ) void {
393
- _ = user_data;
394
- _ = operation;
395
- _ = result catch |err| switch (err) {
396
- error.TooManyOutstandingRequests => unreachable,
397
- };
398
- }
399
-
400
- fn client_on_reply(client: *Client, request_message: *Message, reply_message: *Message) void {
401
- const cluster = @ptrCast(*Self, @alignCast(@alignOf(Self), client.on_reply_context.?));
402
- assert(reply_message.header.cluster == cluster.options.cluster_id);
403
- assert(reply_message.header.invalid() == null);
404
- assert(reply_message.header.client == client.id);
405
- assert(reply_message.header.request == request_message.header.request);
406
- assert(reply_message.header.command == .reply);
407
- assert(reply_message.header.operation == request_message.header.operation);
408
-
409
- const client_index = for (cluster.clients) |*c, i| {
410
- if (client == c) break i;
411
- } else unreachable;
412
-
413
- cluster.on_client_reply(cluster, client_index, request_message, reply_message);
414
- }
415
-
416
- fn on_replica_change_state(replica: *const Replica) void {
417
- const cluster = @ptrCast(*Self, @alignCast(@alignOf(Self), replica.context.?));
418
- cluster.state_checker.check_state(replica.replica) catch |err| {
419
- fatal(.correctness, "state checker error: {}", .{err});
420
- };
421
- }
422
-
423
- fn on_replica_compact(replica: *const Replica) void {
424
- const cluster = @ptrCast(*Self, @alignCast(@alignOf(Self), replica.context.?));
425
- cluster.storage_checker.replica_compact(replica) catch |err| {
426
- fatal(.correctness, "storage checker error: {}", .{err});
427
- };
428
- }
429
-
430
- fn on_replica_checkpoint(replica: *const Replica) void {
431
- const cluster = @ptrCast(*Self, @alignCast(@alignOf(Self), replica.context.?));
432
- cluster.storage_checker.replica_checkpoint(replica) catch |err| {
433
- fatal(.correctness, "storage checker error: {}", .{err});
434
- };
435
- }
436
-
437
- /// Print an error message and then exit with an exit code.
438
- fn fatal(failure: Failure, comptime fmt_string: []const u8, args: anytype) noreturn {
439
- std.log.scoped(.state_checker).err(fmt_string, args);
440
- std.os.exit(@enumToInt(failure));
441
- }
442
- };
443
- }
@@ -1,140 +0,0 @@
1
- //! Utils functions for writing fuzzers.
2
-
3
- const std = @import("std");
4
- const assert = std.debug.assert;
5
- const mem = std.mem;
6
-
7
- const log = std.log.scoped(.fuzz);
8
-
9
- /// Returns an integer of type `T` with an exponential distribution of rate `avg`.
10
- /// Note: If you specify a very high rate then `std.math.maxInt(T)` may be over-represented.
11
- pub fn random_int_exponential(random: std.rand.Random, comptime T: type, avg: T) T {
12
- comptime {
13
- const info = @typeInfo(T);
14
- assert(info == .Int);
15
- assert(info.Int.signedness == .unsigned);
16
- }
17
- const exp = random.floatExp(f64) * @intToFloat(f64, avg);
18
- return std.math.lossyCast(T, exp);
19
- }
20
-
21
- pub fn Distribution(comptime Enum: type) type {
22
- return std.enums.EnumFieldStruct(Enum, f64, null);
23
- }
24
-
25
- /// Return a distribution for use with `random_enum`.
26
- pub fn random_enum_distribution(
27
- random: std.rand.Random,
28
- comptime Enum: type,
29
- ) Distribution(Enum) {
30
- const fields = @typeInfo(Distribution(Enum)).Struct.fields;
31
- var distribution: Distribution(Enum) = undefined;
32
- var total: f64 = 0;
33
- inline for (fields) |field| {
34
- const p = @intToFloat(f64, random.uintLessThan(u8, 10));
35
- @field(distribution, field.name) = p;
36
- total += p;
37
- }
38
- // Ensure that at least one field has non-zero probability.
39
- if (total == 0) {
40
- @field(distribution, fields[0].name) = 1;
41
- }
42
- return distribution;
43
- }
44
-
45
- /// Generate a random `Enum`, given a distribution over the fields of the enum.
46
- pub fn random_enum(
47
- random: std.rand.Random,
48
- comptime Enum: type,
49
- distribution: Distribution(Enum),
50
- ) Enum {
51
- const fields = @typeInfo(Enum).Enum.fields;
52
- var total: f64 = 0;
53
- inline for (fields) |field| {
54
- total += @field(distribution, field.name);
55
- }
56
- assert(total > 0);
57
- var choice = random.float(f64) * total;
58
- inline for (fields) |field| {
59
- choice -= @field(distribution, field.name);
60
- if (choice < 0) return @intToEnum(Enum, field.value);
61
- }
62
- unreachable;
63
- }
64
-
65
- pub const FuzzArgs = struct {
66
- seed: u64,
67
- events_max: ?usize,
68
- };
69
-
70
- /// Parse common command-line arguments to fuzzers:
71
- ///
72
- /// [--seed u64]
73
- /// Sets the seed used for the random number generator.
74
- /// [--events-max usize]
75
- /// Override the fuzzer's default maximum number of generated events.
76
- pub fn parse_fuzz_args(allocator: mem.Allocator) !FuzzArgs {
77
- var seed: ?u64 = null;
78
- var events_max: ?usize = null;
79
-
80
- var args = std.process.args();
81
-
82
- // Discard executable name.
83
- allocator.free(try args.next(allocator).?);
84
-
85
- while (args.next(allocator)) |arg_or_err| {
86
- const arg = try arg_or_err;
87
- defer allocator.free(arg);
88
-
89
- if (std.mem.eql(u8, arg, "--seed")) {
90
- const seed_string_or_err = args.next(allocator) orelse
91
- std.debug.panic("Expected an argument to --seed", .{});
92
- const seed_string = try seed_string_or_err;
93
- defer allocator.free(seed_string);
94
-
95
- if (seed != null) {
96
- std.debug.panic("Received more than one \"--seed\"", .{});
97
- }
98
- seed = std.fmt.parseInt(u64, seed_string, 10) catch |err|
99
- std.debug.panic(
100
- "Could not parse \"{}\" as an integer seed: {}",
101
- .{ std.zig.fmtEscapes(seed_string), err },
102
- );
103
- } else if (std.mem.eql(u8, arg, "--events-max")) {
104
- const events_string_or_err = args.next(allocator) orelse
105
- std.debug.panic("Expected an argument to --events-max", .{});
106
- const events_string = try events_string_or_err;
107
- defer allocator.free(events_string);
108
-
109
- if (events_max != null) {
110
- std.debug.panic("Received more than one \"--events-max\"", .{});
111
- }
112
- events_max = std.fmt.parseInt(usize, events_string, 10) catch |err|
113
- std.debug.panic(
114
- "Could not parse \"{}\" as an integer events-max: {}",
115
- .{ std.zig.fmtEscapes(events_string), err },
116
- );
117
- } else {
118
- // When run with `--test-cmd`,
119
- // `zig run` also passes the location of the zig binary as an extra arg.
120
- // I don't know how to turn this off, so we just skip such args.
121
- if (!std.mem.endsWith(u8, arg, "zig")) {
122
- std.debug.panic("Unrecognized argument: \"{}\"", .{std.zig.fmtEscapes(arg)});
123
- }
124
- }
125
- }
126
-
127
- if (seed == null) {
128
- // If no seed was given, use a random seed instead.
129
- var buffer: [@sizeOf(u64)]u8 = undefined;
130
- try std.os.getrandom(&buffer);
131
- seed = @bitCast(u64, buffer);
132
- }
133
-
134
- log.info("Fuzz seed = {}", .{seed.?});
135
-
136
- return FuzzArgs{
137
- .seed = seed.?,
138
- .events_max = events_max,
139
- };
140
- }
@@ -1,66 +0,0 @@
1
- //! A tool for narrowing down the point of divergence between two executions that should be identical.
2
- //! Sprinkle calls to `emit(some_hash)` throughout the code.
3
- //! With `-Dhash-log-mode=create`, all emitted hashes are written to ./hash_log.
4
- //! With `-Dhash-log-mode=check`, all emitted hashes are checked against the hashes in ./hash_log.
5
- //! Otherwise, calls to `emit` are noops.
6
-
7
- const std = @import("std");
8
- const assert = std.debug.assert;
9
- const panic = std.debug.panic;
10
-
11
- const constants = @import("../constants.zig");
12
-
13
- var file: ?std.fs.File = null;
14
- var hash_count: usize = 0;
15
-
16
- fn ensure_init() void {
17
- if (file != null) return;
18
- switch (constants.hash_log_mode) {
19
- .none => unreachable,
20
- .create => {
21
- file = std.fs.cwd().createFile("./hash_log", .{ .truncate = true }) catch unreachable;
22
- },
23
- .check => {
24
- file = std.fs.cwd().openFile("./hash_log", .{ .read = true }) catch unreachable;
25
- },
26
- }
27
- }
28
-
29
- pub fn emit(hash: u128) void {
30
- @call(.{ .modifier = .never_inline }, emit_never_inline, .{hash});
31
- }
32
-
33
- // Don't inline because we want to be able to break on this function.
34
- fn emit_never_inline(hash: u128) void {
35
- switch (constants.hash_log_mode) {
36
- .none => {},
37
- .create => {
38
- ensure_init();
39
- std.fmt.format(file.?.writer(), "{x:0>32}\n", .{hash}) catch unreachable;
40
- hash_count += 1;
41
- },
42
- .check => {
43
- ensure_init();
44
- var buffer: [33]u8 = undefined;
45
- const bytes_read = file.?.readAll(&buffer) catch unreachable;
46
- if (bytes_read != 33) {
47
- panic("Unexpected end of hash_log at hash_count={}. Expected EOF, found {x:0>32}.", .{ hash_count, hash });
48
- }
49
- const expected_hash = std.fmt.parseInt(u128, buffer[0..32], 16) catch unreachable;
50
- if (hash != expected_hash) {
51
- panic(
52
- "Hash mismatch at hash_count={}. Expected {x:0>32}, found {x:0>32}.",
53
- .{ hash_count, expected_hash, hash },
54
- );
55
- }
56
- hash_count += 1;
57
- },
58
- }
59
- }
60
-
61
- pub fn emit_autohash(hashable: anytype, comptime strategy: std.hash.Strategy) void {
62
- if (constants.hash_log_mode == .none) return;
63
- var hasher = std.hash.Wyhash.init(0);
64
- std.hash.autoHashStrat(&hasher, hashable, strategy);
65
- emit(hasher.final());
66
- }