tigerbeetle-node 0.11.13 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  2. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  3. package/dist/bin/aarch64-macos/client.node +0 -0
  4. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  5. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  6. package/dist/bin/x86_64-macos/client.node +0 -0
  7. package/dist/index.js +33 -1
  8. package/dist/index.js.map +1 -1
  9. package/package-lock.json +66 -0
  10. package/package.json +6 -16
  11. package/src/index.ts +56 -1
  12. package/src/node.zig +9 -9
  13. package/dist/.client.node.sha256 +0 -1
  14. package/scripts/build_lib.sh +0 -61
  15. package/scripts/download_node_headers.sh +0 -32
  16. package/src/tigerbeetle/scripts/benchmark.bat +0 -55
  17. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  18. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  19. package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
  22. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  23. package/src/tigerbeetle/scripts/install.bat +0 -7
  24. package/src/tigerbeetle/scripts/install.sh +0 -21
  25. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  26. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  27. package/src/tigerbeetle/scripts/lint.zig +0 -199
  28. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  29. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
  30. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  31. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  32. package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
  33. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  34. package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
  35. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  36. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  37. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  38. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  39. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  40. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  41. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  42. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  43. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  44. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  45. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  46. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  47. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  48. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  49. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  50. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  51. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  52. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  53. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  54. package/src/tigerbeetle/src/benchmark.zig +0 -336
  55. package/src/tigerbeetle/src/config.zig +0 -233
  56. package/src/tigerbeetle/src/constants.zig +0 -428
  57. package/src/tigerbeetle/src/ewah.zig +0 -286
  58. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  59. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  60. package/src/tigerbeetle/src/fifo.zig +0 -120
  61. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  62. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  63. package/src/tigerbeetle/src/io/linux.zig +0 -1071
  64. package/src/tigerbeetle/src/io/test.zig +0 -643
  65. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  66. package/src/tigerbeetle/src/io.zig +0 -34
  67. package/src/tigerbeetle/src/iops.zig +0 -107
  68. package/src/tigerbeetle/src/lsm/README.md +0 -308
  69. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  70. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  71. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  72. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  73. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  74. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  75. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  76. package/src/tigerbeetle/src/lsm/forest.zig +0 -205
  77. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
  78. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  79. package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
  80. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  81. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  82. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  83. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
  84. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  85. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  86. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  87. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  88. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
  89. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
  90. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  91. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  92. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  93. package/src/tigerbeetle/src/lsm/table.zig +0 -1009
  94. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
  95. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  96. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
  97. package/src/tigerbeetle/src/lsm/test.zig +0 -439
  98. package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
  99. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
  100. package/src/tigerbeetle/src/message_bus.zig +0 -1013
  101. package/src/tigerbeetle/src/message_pool.zig +0 -156
  102. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  103. package/src/tigerbeetle/src/simulator.zig +0 -580
  104. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
  105. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  106. package/src/tigerbeetle/src/state_machine.zig +0 -2099
  107. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  108. package/src/tigerbeetle/src/stdx.zig +0 -171
  109. package/src/tigerbeetle/src/storage.zig +0 -393
  110. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  111. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  112. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  113. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  114. package/src/tigerbeetle/src/testing/cluster.zig +0 -444
  115. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  116. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  117. package/src/tigerbeetle/src/testing/id.zig +0 -99
  118. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
  119. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  120. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  121. package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
  122. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  123. package/src/tigerbeetle/src/testing/table.zig +0 -247
  124. package/src/tigerbeetle/src/testing/time.zig +0 -84
  125. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  126. package/src/tigerbeetle/src/time.zig +0 -112
  127. package/src/tigerbeetle/src/tracer.zig +0 -529
  128. package/src/tigerbeetle/src/unit_tests.zig +0 -40
  129. package/src/tigerbeetle/src/vopr.zig +0 -495
  130. package/src/tigerbeetle/src/vsr/README.md +0 -209
  131. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  132. package/src/tigerbeetle/src/vsr/clock.zig +0 -855
  133. package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
  134. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  135. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  136. package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
  137. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  138. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  139. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  140. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  141. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  142. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  143. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  144. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  145. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  146. package/src/tigerbeetle/src/vsr.zig +0 -1425
@@ -1,82 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
-
4
- const MessagePool = @import("../../message_pool.zig").MessagePool;
5
- const Message = MessagePool.Message;
6
- const Header = @import("../../vsr.zig").Header;
7
- const ProcessType = @import("../../vsr.zig").ProcessType;
8
-
9
- const Network = @import("network.zig").Network;
10
-
11
- const log = std.log.scoped(.message_bus);
12
-
13
- pub const Process = union(ProcessType) {
14
- replica: u8,
15
- client: u128,
16
- };
17
-
18
- pub const MessageBus = struct {
19
- network: *Network,
20
- pool: *MessagePool,
21
-
22
- cluster: u32,
23
- process: Process,
24
-
25
- /// The callback to be called when a message is received.
26
- on_message_callback: fn (message_bus: *MessageBus, message: *Message) void,
27
-
28
- pub const Options = struct {
29
- network: *Network,
30
- };
31
-
32
- pub fn init(
33
- _: std.mem.Allocator,
34
- cluster: u32,
35
- process: Process,
36
- message_pool: *MessagePool,
37
- on_message_callback: fn (message_bus: *MessageBus, message: *Message) void,
38
- options: Options,
39
- ) !MessageBus {
40
- return MessageBus{
41
- .network = options.network,
42
- .pool = message_pool,
43
- .cluster = cluster,
44
- .process = process,
45
- .on_message_callback = on_message_callback,
46
- };
47
- }
48
-
49
- /// TODO
50
- pub fn deinit(_: *MessageBus, _: std.mem.Allocator) void {}
51
-
52
- pub fn tick(_: *MessageBus) void {}
53
-
54
- pub fn get_message(bus: *MessageBus) *Message {
55
- return bus.pool.get_message();
56
- }
57
-
58
- pub fn unref(bus: *MessageBus, message: *Message) void {
59
- bus.pool.unref(message);
60
- }
61
-
62
- pub fn send_message_to_replica(bus: *MessageBus, replica: u8, message: *Message) void {
63
- // Messages sent by a process to itself should never be passed to the message bus
64
- if (bus.process == .replica) assert(replica != bus.process.replica);
65
-
66
- bus.network.send_message(message, .{
67
- .source = bus.process,
68
- .target = .{ .replica = replica },
69
- });
70
- }
71
-
72
- /// Try to send the message to the client with the given id.
73
- /// If the client is not currently connected, the message is silently dropped.
74
- pub fn send_message_to_client(bus: *MessageBus, client_id: u128, message: *Message) void {
75
- assert(bus.process == .replica);
76
-
77
- bus.network.send_message(message, .{
78
- .source = bus.process,
79
- .target = .{ .client = client_id },
80
- });
81
- }
82
- };
@@ -1,237 +0,0 @@
1
- const std = @import("std");
2
- const math = std.math;
3
- const mem = std.mem;
4
- const assert = std.debug.assert;
5
-
6
- const constants = @import("../../constants.zig");
7
- const vsr = @import("../../vsr.zig");
8
- const stdx = @import("../../stdx.zig");
9
-
10
- const MessagePool = @import("../../message_pool.zig").MessagePool;
11
- const Message = MessagePool.Message;
12
-
13
- const MessageBus = @import("message_bus.zig").MessageBus;
14
- const Process = @import("message_bus.zig").Process;
15
-
16
- const PacketSimulatorType = @import("../packet_simulator.zig").PacketSimulatorType;
17
- const PacketSimulatorOptions = @import("../packet_simulator.zig").PacketSimulatorOptions;
18
- const PacketSimulatorPath = @import("../packet_simulator.zig").Path;
19
-
20
- const log = std.log.scoped(.network);
21
-
22
- pub const NetworkOptions = PacketSimulatorOptions;
23
-
24
- pub const Network = struct {
25
- pub const Packet = struct {
26
- network: *Network,
27
- message: *Message,
28
-
29
- pub fn deinit(packet: *const Packet) void {
30
- packet.network.message_pool.unref(packet.message);
31
- }
32
- };
33
-
34
- pub const Path = struct {
35
- source: Process,
36
- target: Process,
37
- };
38
-
39
- allocator: std.mem.Allocator,
40
-
41
- options: NetworkOptions,
42
- packet_simulator: PacketSimulatorType(Packet),
43
-
44
- // TODO(Zig) If this stored a ?*MessageBus, then a process's bus could be set to `null` while
45
- // the replica is crashed, and replaced when it is destroyed. But Zig complains:
46
- //
47
- // ./src/test/message_bus.zig:20:24: error: struct 'test.message_bus.MessageBus' depends on itself
48
- // pub const MessageBus = struct {
49
- // ^
50
- // ./src/test/message_bus.zig:21:5: note: while checking this field
51
- // network: *Network,
52
- // ^
53
- buses: std.ArrayListUnmanaged(*MessageBus),
54
- buses_enabled: std.ArrayListUnmanaged(bool),
55
- processes: std.ArrayListUnmanaged(u128),
56
- /// A pool of messages that are in the network (sent, but not yet delivered).
57
- message_pool: MessagePool,
58
-
59
- pub fn init(
60
- allocator: std.mem.Allocator,
61
- replica_count: u8,
62
- client_count: u8,
63
- options: NetworkOptions,
64
- ) !Network {
65
- const process_count = client_count + replica_count;
66
- assert(process_count <= std.math.maxInt(u8));
67
-
68
- var buses = try std.ArrayListUnmanaged(*MessageBus).initCapacity(allocator, process_count);
69
- errdefer buses.deinit(allocator);
70
-
71
- var buses_enabled = try std.ArrayListUnmanaged(bool).initCapacity(allocator, process_count);
72
- errdefer buses_enabled.deinit(allocator);
73
-
74
- var processes = try std.ArrayListUnmanaged(u128).initCapacity(allocator, process_count);
75
- errdefer processes.deinit(allocator);
76
-
77
- var packet_simulator = try PacketSimulatorType(Packet).init(allocator, options);
78
- errdefer packet_simulator.deinit(allocator);
79
-
80
- // Count:
81
- // - replica → replica paths (excluding self-loops)
82
- // - replica → client paths
83
- // - client → replica paths
84
- // but not client→client paths; clients never message one another.
85
- const path_count = @as(usize, replica_count) * @as(usize, replica_count - 1) +
86
- 2 * @as(usize, replica_count) * @as(usize, client_count);
87
- const message_pool = try MessagePool.init_capacity(
88
- allocator,
89
- // +1 so we can allocate an extra packet when all packet queues are at capacity,
90
- // so that `PacketSimulator.submit_packet` can choose which packet to drop.
91
- 1 + @as(usize, options.path_maximum_capacity) * path_count,
92
- );
93
- errdefer message_pool.deinit(allocator);
94
-
95
- return Network{
96
- .allocator = allocator,
97
- .options = options,
98
- .packet_simulator = packet_simulator,
99
- .buses = buses,
100
- .buses_enabled = buses_enabled,
101
- .processes = processes,
102
- .message_pool = message_pool,
103
- };
104
- }
105
-
106
- pub fn deinit(network: *Network) void {
107
- network.buses.deinit(network.allocator);
108
- network.buses_enabled.deinit(network.allocator);
109
- network.processes.deinit(network.allocator);
110
- network.packet_simulator.deinit(network.allocator);
111
- network.message_pool.deinit(network.allocator);
112
- }
113
-
114
- pub fn tick(network: *Network) void {
115
- network.packet_simulator.tick();
116
- }
117
-
118
- pub fn link(network: *Network, process: Process, message_bus: *MessageBus) void {
119
- const raw_process = switch (process) {
120
- .replica => |replica| replica,
121
- .client => |client| blk: {
122
- assert(client >= constants.replicas_max);
123
- break :blk client;
124
- },
125
- };
126
-
127
- for (network.processes.items) |existing_process, i| {
128
- if (existing_process == raw_process) {
129
- network.buses.items[i] = message_bus;
130
- break;
131
- }
132
- } else {
133
- network.processes.appendAssumeCapacity(raw_process);
134
- network.buses.appendAssumeCapacity(message_bus);
135
- network.buses_enabled.appendAssumeCapacity(true);
136
- }
137
- assert(network.processes.items.len == network.buses.items.len);
138
- }
139
-
140
- pub fn process_enable(network: *Network, process: Process) void {
141
- assert(!network.buses_enabled.items[network.process_to_address(process)]);
142
- network.buses_enabled.items[network.process_to_address(process)] = true;
143
- }
144
-
145
- pub fn process_disable(network: *Network, process: Process) void {
146
- assert(network.buses_enabled.items[network.process_to_address(process)]);
147
- network.buses_enabled.items[network.process_to_address(process)] = false;
148
- }
149
-
150
- pub fn send_message(network: *Network, message: *Message, path: Path) void {
151
- log.debug("send_message: {} > {}: {}", .{
152
- path.source,
153
- path.target,
154
- message.header.command,
155
- });
156
-
157
- const network_message = network.message_pool.get_message();
158
- defer network.message_pool.unref(network_message);
159
-
160
- stdx.copy_disjoint(.exact, u8, network_message.buffer, message.buffer);
161
-
162
- network.packet_simulator.submit_packet(
163
- .{
164
- .message = network_message.ref(),
165
- .network = network,
166
- },
167
- deliver_message,
168
- .{
169
- .source = network.process_to_address(path.source),
170
- .target = network.process_to_address(path.target),
171
- },
172
- );
173
- }
174
-
175
- fn process_to_address(network: *const Network, process: Process) u8 {
176
- for (network.processes.items) |p, i| {
177
- if (std.meta.eql(raw_process_to_process(p), process)) return @intCast(u8, i);
178
- }
179
- unreachable;
180
- }
181
-
182
- pub fn get_message_bus(network: *Network, process: Process) *MessageBus {
183
- return network.buses.items[network.process_to_address(process)];
184
- }
185
-
186
- fn deliver_message(packet: Packet, path: PacketSimulatorPath) void {
187
- const network = packet.network;
188
- const process_path = .{
189
- .source = raw_process_to_process(network.processes.items[path.source]),
190
- .target = raw_process_to_process(network.processes.items[path.target]),
191
- };
192
-
193
- if (!network.buses_enabled.items[path.target]) {
194
- log.debug("deliver_message: {} > {}: {} (dropped; target is down)", .{
195
- process_path.source,
196
- process_path.target,
197
- packet.message.header.command,
198
- });
199
- return;
200
- }
201
-
202
- const target_bus = network.buses.items[path.target];
203
- const target_message = target_bus.get_message();
204
- defer target_bus.unref(target_message);
205
-
206
- stdx.copy_disjoint(.exact, u8, target_message.buffer, packet.message.buffer);
207
-
208
- log.debug("deliver_message: {} > {}: {}", .{
209
- process_path.source,
210
- process_path.target,
211
- packet.message.header.command,
212
- });
213
-
214
- if (target_message.header.command == .request or
215
- target_message.header.command == .prepare)
216
- {
217
- const sector_ceil = vsr.sector_ceil(target_message.header.size);
218
- if (target_message.header.size != sector_ceil) {
219
- assert(target_message.header.size < sector_ceil);
220
- assert(target_message.buffer.len == constants.message_size_max);
221
- mem.set(u8, target_message.buffer[target_message.header.size..sector_ceil], 0);
222
- }
223
- }
224
-
225
- target_bus.on_message_callback(target_bus, target_message);
226
- }
227
-
228
- fn raw_process_to_process(raw: u128) Process {
229
- switch (raw) {
230
- 0...(constants.replicas_max - 1) => return .{ .replica = @intCast(u8, raw) },
231
- else => {
232
- assert(raw >= constants.replicas_max);
233
- return .{ .client = raw };
234
- },
235
- }
236
- }
237
- };
@@ -1,169 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const mem = std.mem;
4
-
5
- const constants = @import("../../constants.zig");
6
- const vsr = @import("../../vsr.zig");
7
- const RingBuffer = @import("../../ring_buffer.zig").RingBuffer;
8
-
9
- const message_pool = @import("../../message_pool.zig");
10
- const MessagePool = message_pool.MessagePool;
11
- const Message = MessagePool.Message;
12
-
13
- const ReplicaSet = std.StaticBitSet(constants.replicas_max);
14
- const Commits = std.ArrayList(struct {
15
- header: vsr.Header,
16
- replicas: ReplicaSet = ReplicaSet.initEmpty(),
17
- });
18
-
19
- const log = std.log.scoped(.state_checker);
20
-
21
- pub fn StateCheckerType(comptime Client: type, comptime Replica: type) type {
22
- return struct {
23
- const Self = @This();
24
-
25
- replica_count: u8,
26
-
27
- commits: Commits,
28
- commit_mins: [constants.replicas_max]u64 = [_]u64{0} ** constants.replicas_max,
29
-
30
- replicas: []const Replica,
31
- clients: []const Client,
32
-
33
- /// The number of times the canonical state has been advanced.
34
- requests_committed: u64 = 0,
35
-
36
- pub fn init(
37
- allocator: mem.Allocator,
38
- cluster: u32,
39
- replicas: []const Replica,
40
- clients: []const Client,
41
- ) !Self {
42
- const root_prepare = vsr.Header.root_prepare(cluster);
43
-
44
- var commits = Commits.init(allocator);
45
- errdefer commits.deinit();
46
-
47
- var commit_replicas = ReplicaSet.initEmpty();
48
- for (replicas) |_, i| commit_replicas.set(i);
49
- try commits.append(.{
50
- .header = root_prepare,
51
- .replicas = commit_replicas,
52
- });
53
-
54
- return Self{
55
- .replica_count = @intCast(u8, replicas.len),
56
- .commits = commits,
57
- .replicas = replicas,
58
- .clients = clients,
59
- };
60
- }
61
-
62
- pub fn deinit(state_checker: *Self) void {
63
- state_checker.commits.deinit();
64
- }
65
-
66
- pub fn check_state(state_checker: *Self, replica_index: u8) !void {
67
- const replica = &state_checker.replicas[replica_index];
68
-
69
- const commit_root = replica.superblock.working.vsr_state.commit_min_checksum;
70
-
71
- const commit_a = state_checker.commit_mins[replica_index];
72
- const commit_b = replica.commit_min;
73
-
74
- const header_b = replica.journal.header_with_op(replica.commit_min);
75
- assert(header_b != null or replica.commit_min == replica.op_checkpoint());
76
- assert(header_b == null or header_b.?.op == commit_b);
77
-
78
- const checksum_a = state_checker.commits.items[commit_a].header.checksum;
79
- const checksum_b = if (header_b) |h| h.checksum else commit_root;
80
-
81
- assert(checksum_b != commit_root or replica.commit_min == replica.superblock.working.vsr_state.commit_min);
82
- assert((commit_a == commit_b) == (checksum_a == checksum_b));
83
-
84
- if (checksum_a == checksum_b) return;
85
-
86
- assert(commit_b < commit_a or commit_a + 1 == commit_b);
87
- state_checker.commit_mins[replica_index] = commit_b;
88
-
89
- // If some other replica has already reached this state, then it will be in the commit history:
90
- if (replica.commit_min < state_checker.commits.items.len) {
91
- state_checker.commits.items[replica.commit_min].replicas.set(replica_index);
92
-
93
- assert(replica.commit_min < state_checker.commits.items.len);
94
- // A replica may transition more than once to the same state, for example, when
95
- // restarting after a crash and replaying the log. The more important invariant is that
96
- // the cluster as a whole may not transition to the same state more than once, and once
97
- // transitioned may not regress.
98
- log.info("{d:0>4}/{d:0>4} {x:0>32} > {x:0>32} {}", .{
99
- replica.commit_min,
100
- state_checker.requests_committed,
101
- checksum_a,
102
- checksum_b,
103
- replica_index,
104
- });
105
- return;
106
- }
107
-
108
- if (header_b == null) return;
109
- assert(header_b.?.parent == checksum_a);
110
- assert(header_b.?.op > 0);
111
- assert(header_b.?.command == .prepare);
112
- assert(header_b.?.operation != .reserved);
113
-
114
- // The replica has transitioned to state `b` that is not yet in the commit history.
115
- // Check if this is a valid new state based on the originating client's inflight request.
116
- const client = for (state_checker.clients) |*client| {
117
- if (client.id == header_b.?.client) break client;
118
- } else unreachable;
119
-
120
- if (client.request_queue.empty()) {
121
- return error.ReplicaTransitionedToInvalidState;
122
- }
123
-
124
- const request = client.request_queue.head_ptr_const().?;
125
- assert(request.message.header.client == header_b.?.client);
126
- assert(request.message.header.request == header_b.?.request);
127
- assert(request.message.header.command == .request);
128
- assert(request.message.header.operation == header_b.?.operation);
129
- assert(request.message.header.size == header_b.?.size);
130
- // `checksum_body` will not match; the leader's StateMachine updated the timestamps in the
131
- // prepare body's accounts/transfers.
132
-
133
- state_checker.requests_committed += 1;
134
- assert(state_checker.requests_committed == header_b.?.op);
135
-
136
- log.info(" {d:0>4} {x:0>32} > {x:0>32} {}", .{
137
- state_checker.requests_committed,
138
- checksum_a,
139
- checksum_b,
140
- replica_index,
141
- });
142
-
143
- assert(state_checker.commits.items.len == header_b.?.op);
144
- state_checker.commits.append(.{ .header = header_b.?.* }) catch unreachable;
145
- state_checker.commits.items[header_b.?.op].replicas.set(replica_index);
146
- }
147
-
148
- pub fn convergence(state_checker: *Self) bool {
149
- const a = state_checker.commits.items.len - 1;
150
- for (state_checker.commit_mins[0..state_checker.replica_count]) |b| {
151
- if (b != a) {
152
- return false;
153
- }
154
- }
155
-
156
- for (state_checker.commits.items) |commit, i| {
157
- assert(commit.replicas.count() == state_checker.replica_count);
158
- assert(commit.header.command == .prepare);
159
- assert(commit.header.op == i);
160
- if (i > 0) {
161
- const previous = state_checker.commits.items[i - 1].header;
162
- assert(commit.header.parent == previous.checksum);
163
- assert(commit.header.view >= previous.view);
164
- }
165
- }
166
- return true;
167
- }
168
- };
169
- }
@@ -1,202 +0,0 @@
1
- //! Verify deterministic storage.
2
- //!
3
- //! At each replica compact and checkpoint, check that storage is byte-for-byte identical across
4
- //! replicas.
5
- //!
6
- //! Areas verified at compaction (half-measure):
7
- //! - Acquired Grid blocks (ignores skipped recovery compactions)
8
- //! TODO Because ManifestLog acquires blocks potentially several beats prior to actually writing
9
- //! the block, this check will need to be removed or use a different strategy.
10
- //!
11
- //! Areas verified at checkpoint:
12
- //! - WAL prepares
13
- //! - SuperBlock Manifest, FreeSet, ClientTable
14
- //! - Acquired Grid blocks
15
- //!
16
- //! Areas not verified:
17
- //! - SuperBlock headers, which hold replica-specific state.
18
- //! - WAL headers, which may differ because the WAL writes deliberately corrupt redundant headers
19
- //! to faulty slots to ensure recovery is consistent.
20
- //! - Non-allocated Grid blocks, which may differ due to state transfer.
21
- const std = @import("std");
22
- const assert = std.debug.assert;
23
- const log = std.log.scoped(.storage_checker);
24
-
25
- const constants = @import("../../constants.zig");
26
- const vsr = @import("../../vsr.zig");
27
- const superblock = @import("../../vsr/superblock.zig");
28
- const SuperBlockHeader = superblock.SuperBlockHeader;
29
- const Storage = @import("../storage.zig").Storage;
30
-
31
- /// After each compaction half measure, save the cumulative hash of all acquired grid blocks.
32
- ///
33
- /// (Track half-measures instead of beats because the on-disk state mid-compaction is
34
- /// nondeterministic; it depends on IO progress.)
35
- const Compactions = std.ArrayList(u128);
36
-
37
- /// Maps from op_checkpoint to cumulative storage checksum.
38
- ///
39
- /// Not every checkpoint is necessarily recorded — a replica calls on_checkpoint *at most* once.
40
- /// For example, a replica will not call on_checkpoint if it crashes (during a checkpoint) after
41
- /// writing 2 superblock copies. (This could be repeated by other replicas, causing a checkpoint
42
- /// op to be skipped in Checkpoints).
43
- const Checkpoints = std.AutoHashMap(u64, Checkpoint);
44
-
45
- const Checkpoint = struct {
46
- // The superblock trailers are an XOR of all copies of all respective trailers, not the
47
- // `SuperBlockHeader.{trailer}_checksum`.
48
- checksum_superblock_manifest: u128,
49
- checksum_superblock_free_set: u128,
50
- checksum_superblock_client_table: u128,
51
- checksum_wal_prepares: u128,
52
- checksum_grid: u128,
53
- };
54
-
55
- pub fn StorageCheckerType(comptime Replica: type) type {
56
- return struct {
57
- const Self = @This();
58
-
59
- allocator: std.mem.Allocator,
60
- compactions: Compactions,
61
- checkpoints: Checkpoints,
62
-
63
- pub fn init(allocator: std.mem.Allocator) Self {
64
- var compactions = Compactions.init(allocator);
65
- errdefer compactions.deinit();
66
-
67
- var checkpoints = Checkpoints.init(allocator);
68
- errdefer checkpoints.deinit();
69
-
70
- return Self{
71
- .allocator = allocator,
72
- .compactions = compactions,
73
- .checkpoints = checkpoints,
74
- };
75
- }
76
-
77
- pub fn deinit(checker: *Self) void {
78
- checker.compactions.deinit();
79
- checker.checkpoints.deinit();
80
- }
81
-
82
- pub fn replica_compact(checker: *Self, replica: *const Replica) !void {
83
- // TODO(Beat Compaction) Remove when deterministic beat compaction is fixed.
84
- // Until then this is too noisy.
85
- if (1 == 1) return;
86
-
87
- // If we are recovering from a crash, don't test the checksum until we are caught up.
88
- // Until then our grid's checksum is too far ahead.
89
- if (replica.superblock.working.vsr_state.op_compacted(replica.commit_min)) return;
90
-
91
- // TODO(Beat Compaction) Remove when deterministic beat compaction is implemented.
92
- const half_measure_beat_count = @divExact(constants.lsm_batch_multiple, 2);
93
- if ((replica.commit_min + 1) % half_measure_beat_count != 0) return;
94
-
95
- const checksum = checksum_grid(replica);
96
- log.debug("{}: replica_compact: op={} area=grid checksum={}", .{
97
- replica.replica,
98
- replica.commit_min,
99
- checksum,
100
- });
101
-
102
- // -1 since we never compact op=1.
103
- const compactions_index = @divExact(replica.commit_min + 1, half_measure_beat_count) - 1;
104
- if (compactions_index == checker.compactions.items.len) {
105
- try checker.compactions.append(checksum);
106
- } else {
107
- const checksum_expect = checker.compactions.items[compactions_index];
108
- if (checksum_expect != checksum) {
109
- log.err("{}: replica_compact: mismatch area=grid expect={} actual={}", .{
110
- replica.replica,
111
- checksum_expect,
112
- checksum,
113
- });
114
- return error.StorageMismatch;
115
- }
116
- }
117
- }
118
-
119
- pub fn replica_checkpoint(checker: *Self, replica: *const Replica) !void {
120
- const storage = replica.superblock.storage;
121
- const working = replica.superblock.working;
122
-
123
- // TODO(Beat Compaction) Remove when deterministic storage is fixed.
124
- // Until then this is too noisy.
125
- if (1 == 1) return;
126
-
127
- var checkpoint = Checkpoint{
128
- .checksum_superblock_manifest = 0,
129
- .checksum_superblock_free_set = 0,
130
- .checksum_superblock_client_table = 0,
131
- .checksum_wal_prepares = checksum_wal_prepares(storage),
132
- .checksum_grid = checksum_grid(replica),
133
- };
134
-
135
- inline for (.{ .manifest, .free_set, .client_table }) |trailer| {
136
- const trailer_area = @field(superblock.areas, trailer);
137
- const trailer_size = @field(working, @tagName(trailer) ++ "_size");
138
- var copy: u8 = 0;
139
- while (copy < constants.superblock_copies) : (copy += 1) {
140
- @field(checkpoint, "checksum_superblock_" ++ @tagName(trailer.field)) |=
141
- vsr.checksum(storage.memory[trailer_area.offset(copy)..][0..trailer_size]);
142
- }
143
- }
144
-
145
- inline for (std.meta.fields(Checkpoint)) |field| {
146
- log.debug("{}: replica_checkpoint: checkpoint={} area={s} value={}", .{
147
- replica.replica,
148
- replica.op_checkpoint(),
149
- field.name,
150
- @field(checkpoint, field.name),
151
- });
152
- }
153
-
154
- const checkpoint_expect = checker.checkpoints.get(replica.op_checkpoint()) orelse {
155
- // This replica is the first to reach op_checkpoint.
156
- try checker.checkpoints.putNoClobber(replica.op_checkpoint(), checkpoint);
157
- return;
158
- };
159
-
160
- var fail: bool = false;
161
- inline for (std.meta.fields(Checkpoint)) |field| {
162
- const field_actual = @field(checkpoint, field.name);
163
- const field_expect = @field(checkpoint_expect, field.name);
164
- if (!std.meta.eql(field_expect, field_actual)) {
165
- fail = true;
166
- log.debug("{}: replica_checkpoint: mismatch area={s} expect={} actual={}", .{
167
- replica.replica,
168
- field.name,
169
- @field(checkpoint_expect, field.name),
170
- @field(checkpoint, field.name),
171
- });
172
- }
173
- }
174
- if (fail) return error.StorageMismatch;
175
- }
176
-
177
- fn checksum_wal_prepares(storage: *const Storage) u128 {
178
- var checksum: u128 = 0;
179
- for (storage.wal_prepares()) |*prepare| {
180
- assert(prepare.header.valid_checksum());
181
- assert(prepare.header.command == .prepare);
182
-
183
- // Only checksum the actual message header+body. Any leftover space is nondeterministic,
184
- // because the current prepare may have overwritten a longer message.
185
- checksum ^= vsr.checksum(std.mem.asBytes(prepare)[0..prepare.header.size]);
186
- }
187
- return checksum;
188
- }
189
-
190
- fn checksum_grid(replica: *const Replica) u128 {
191
- const storage = replica.superblock.storage;
192
- var acquired = replica.superblock.free_set.blocks.iterator(.{ .kind = .unset });
193
- var checksum: u128 = 0;
194
- while (acquired.next()) |address_index| {
195
- const block = storage.grid_block(address_index + 1);
196
- const block_header = std.mem.bytesToValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
197
- checksum ^= vsr.checksum(block[0..block_header.size]);
198
- }
199
- return checksum;
200
- }
201
- };
202
- }