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,93 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
-
4
- const config = @import("../config.zig");
5
-
6
- const MessagePool = @import("../message_pool.zig").MessagePool;
7
- const Message = MessagePool.Message;
8
- const Header = @import("../vsr.zig").Header;
9
- const ProcessType = @import("../vsr.zig").ProcessType;
10
-
11
- const Network = @import("network.zig").Network;
12
-
13
- const log = std.log.scoped(.message_bus);
14
-
15
- pub const Process = union(ProcessType) {
16
- replica: u8,
17
- client: u128,
18
- };
19
-
20
- pub const MessageBus = struct {
21
- network: *Network,
22
- pool: MessagePool,
23
-
24
- cluster: u32,
25
- process: Process,
26
-
27
- /// The callback to be called when a message is received. Use set_on_message() to set
28
- /// with type safety for the context pointer.
29
- on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
30
- on_message_context: ?*anyopaque = null,
31
-
32
- pub fn init(
33
- allocator: std.mem.Allocator,
34
- cluster: u32,
35
- process: Process,
36
- network: *Network,
37
- ) !MessageBus {
38
- return MessageBus{
39
- .pool = try MessagePool.init(allocator, @as(ProcessType, process)),
40
- .network = network,
41
- .cluster = cluster,
42
- .process = process,
43
- };
44
- }
45
-
46
- /// TODO
47
- pub fn deinit(_: *MessageBus) void {}
48
-
49
- pub fn set_on_message(
50
- bus: *MessageBus,
51
- comptime Context: type,
52
- context: Context,
53
- comptime on_message: fn (context: Context, message: *Message) void,
54
- ) void {
55
- bus.on_message_callback = struct {
56
- fn wrapper(_context: ?*anyopaque, message: *Message) void {
57
- on_message(@intToPtr(Context, @ptrToInt(_context)), message);
58
- }
59
- }.wrapper;
60
- bus.on_message_context = context;
61
- }
62
-
63
- pub fn tick(_: *MessageBus) void {}
64
-
65
- pub fn get_message(bus: *MessageBus) *Message {
66
- return bus.pool.get_message();
67
- }
68
-
69
- pub fn unref(bus: *MessageBus, message: *Message) void {
70
- bus.pool.unref(message);
71
- }
72
-
73
- pub fn send_message_to_replica(bus: *MessageBus, replica: u8, message: *Message) void {
74
- // Messages sent by a process to itself should never be passed to the message bus
75
- if (bus.process == .replica) assert(replica != bus.process.replica);
76
-
77
- bus.network.send_message(message, .{
78
- .source = bus.process,
79
- .target = .{ .replica = replica },
80
- });
81
- }
82
-
83
- /// Try to send the message to the client with the given id.
84
- /// If the client is not currently connected, the message is silently dropped.
85
- pub fn send_message_to_client(bus: *MessageBus, client_id: u128, message: *Message) void {
86
- assert(bus.process == .replica);
87
-
88
- bus.network.send_message(message, .{
89
- .source = bus.process,
90
- .target = .{ .client = client_id },
91
- });
92
- }
93
- };
@@ -1,179 +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 config = @import("../config.zig");
7
- const vsr = @import("../vsr.zig");
8
-
9
- const MessagePool = @import("../message_pool.zig").MessagePool;
10
- const Message = MessagePool.Message;
11
-
12
- const MessageBus = @import("message_bus.zig").MessageBus;
13
- const Process = @import("message_bus.zig").Process;
14
-
15
- const PacketSimulator = @import("packet_simulator.zig").PacketSimulator;
16
- const PacketSimulatorOptions = @import("packet_simulator.zig").PacketSimulatorOptions;
17
- const PacketSimulatorPath = @import("packet_simulator.zig").Path;
18
-
19
- const log = std.log.scoped(.network);
20
-
21
- pub const NetworkOptions = struct {
22
- packet_simulator_options: PacketSimulatorOptions,
23
- };
24
-
25
- pub const Network = struct {
26
- pub const Packet = struct {
27
- network: *Network,
28
- message: *Message,
29
-
30
- pub fn deinit(packet: *const Packet, path: PacketSimulatorPath) void {
31
- const source_bus = &packet.network.buses.items[path.source];
32
- source_bus.unref(packet.message);
33
- }
34
- };
35
-
36
- pub const Path = struct {
37
- source: Process,
38
- target: Process,
39
- };
40
-
41
- allocator: std.mem.Allocator,
42
-
43
- options: NetworkOptions,
44
- packet_simulator: PacketSimulator(Packet),
45
-
46
- buses: std.ArrayListUnmanaged(MessageBus),
47
- processes: std.ArrayListUnmanaged(u128),
48
-
49
- pub fn init(
50
- allocator: std.mem.Allocator,
51
- replica_count: u8,
52
- client_count: u8,
53
- options: NetworkOptions,
54
- ) !Network {
55
- const process_count = client_count + replica_count;
56
- assert(process_count <= std.math.maxInt(u8));
57
-
58
- var buses = try std.ArrayListUnmanaged(MessageBus).initCapacity(allocator, process_count);
59
- errdefer buses.deinit(allocator);
60
-
61
- var processes = try std.ArrayListUnmanaged(u128).initCapacity(allocator, process_count);
62
- errdefer processes.deinit(allocator);
63
-
64
- const packet_simulator = try PacketSimulator(Packet).init(
65
- allocator,
66
- options.packet_simulator_options,
67
- );
68
- errdefer packet_simulator.deinit(allocator);
69
-
70
- return Network{
71
- .allocator = allocator,
72
- .options = options,
73
- .packet_simulator = packet_simulator,
74
- .buses = buses,
75
- .processes = processes,
76
- };
77
- }
78
-
79
- pub fn deinit(network: *Network) void {
80
- // TODO: deinit the buses themselves when they gain a deinit()
81
- network.buses.deinit(network.allocator);
82
- network.processes.deinit(network.allocator);
83
- }
84
-
85
- /// Returns the address (index into Network.buses)
86
- pub fn init_message_bus(network: *Network, cluster: u32, process: Process) !*MessageBus {
87
- const raw_process = switch (process) {
88
- .replica => |replica| replica,
89
- .client => |client| blk: {
90
- assert(client >= config.replicas_max);
91
- break :blk client;
92
- },
93
- };
94
-
95
- for (network.processes.items) |p| assert(p != raw_process);
96
-
97
- const bus = try MessageBus.init(network.allocator, cluster, process, network);
98
-
99
- network.processes.appendAssumeCapacity(raw_process);
100
- network.buses.appendAssumeCapacity(bus);
101
-
102
- return &network.buses.items[network.buses.items.len - 1];
103
- }
104
-
105
- pub fn send_message(network: *Network, message: *Message, path: Path) void {
106
- // TODO: we want to unref this message at some point between send()
107
- // and recv() for better realism.
108
- log.debug("send_message: {} > {}: {}", .{
109
- path.source,
110
- path.target,
111
- message.header.command,
112
- });
113
- network.packet_simulator.submit_packet(
114
- .{
115
- .message = message.ref(),
116
- .network = network,
117
- },
118
- deliver_message,
119
- .{
120
- .source = network.process_to_address(path.source),
121
- .target = network.process_to_address(path.target),
122
- },
123
- );
124
- }
125
-
126
- fn process_to_address(network: *Network, process: Process) u8 {
127
- for (network.processes.items) |p, i| {
128
- if (std.meta.eql(raw_process_to_process(p), process)) return @intCast(u8, i);
129
- }
130
- unreachable;
131
- }
132
-
133
- pub fn get_message_bus(network: *Network, process: Process) *MessageBus {
134
- return &network.buses.items[network.process_to_address(process)];
135
- }
136
-
137
- fn deliver_message(packet: Packet, path: PacketSimulatorPath) void {
138
- const network = packet.network;
139
-
140
- const target_bus = &network.buses.items[path.target];
141
-
142
- const message = target_bus.get_message();
143
- defer target_bus.unref(message);
144
-
145
- std.mem.copy(u8, message.buffer, packet.message.buffer);
146
-
147
- const process_path = .{
148
- .source = raw_process_to_process(network.processes.items[path.source]),
149
- .target = raw_process_to_process(network.processes.items[path.target]),
150
- };
151
-
152
- log.debug("deliver_message: {} > {}: {}", .{
153
- process_path.source,
154
- process_path.target,
155
- packet.message.header.command,
156
- });
157
-
158
- if (message.header.command == .request or message.header.command == .prepare) {
159
- const sector_ceil = vsr.sector_ceil(message.header.size);
160
- if (message.header.size != sector_ceil) {
161
- assert(message.header.size < sector_ceil);
162
- assert(message.buffer.len == config.message_size_max + config.sector_size);
163
- mem.set(u8, message.buffer[message.header.size..sector_ceil], 0);
164
- }
165
- }
166
-
167
- target_bus.on_message_callback.?(target_bus.on_message_context, message);
168
- }
169
-
170
- fn raw_process_to_process(raw: u128) Process {
171
- switch (raw) {
172
- 0...(config.replicas_max - 1) => return .{ .replica = @intCast(u8, raw) },
173
- else => {
174
- assert(raw >= config.replicas_max);
175
- return .{ .client = raw };
176
- },
177
- }
178
- }
179
- };
@@ -1,387 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const math = std.math;
4
-
5
- const log = std.log.scoped(.packet_simulator);
6
- const ReplicaHealth = @import("./cluster.zig").ReplicaHealth;
7
-
8
- pub const PacketSimulatorOptions = struct {
9
- /// Mean for the exponential distribution used to calculate forward delay.
10
- one_way_delay_mean: u64,
11
- one_way_delay_min: u64,
12
-
13
- packet_loss_probability: u8,
14
- packet_replay_probability: u8,
15
- seed: u64,
16
-
17
- replica_count: u8,
18
- client_count: u8,
19
- node_count: u8,
20
-
21
- /// How the partitions should be generated
22
- partition_mode: PartitionMode,
23
-
24
- /// Probability per tick that a partition will occur
25
- partition_probability: u8,
26
-
27
- /// Probability per tick that a partition will resolve
28
- unpartition_probability: u8,
29
-
30
- /// Minimum time a partition lasts
31
- partition_stability: u32,
32
-
33
- /// Minimum time the cluster is fully connected until it is partitioned again
34
- unpartition_stability: u32,
35
-
36
- /// The maximum number of in-flight packets a path can have before packets are randomly dropped.
37
- path_maximum_capacity: u8,
38
-
39
- /// Mean for the exponential distribution used to calculate how long a path is clogged for.
40
- path_clog_duration_mean: u64,
41
- path_clog_probability: u8,
42
- };
43
-
44
- pub const Path = struct {
45
- source: u8,
46
- target: u8,
47
- };
48
-
49
- /// Determines how the partitions are created. Partitions
50
- /// are two-way, i.e. if i cannot communicate with j, then
51
- /// j cannot communicate with i.
52
- ///
53
- /// Only replicas are partitioned. There will always be exactly two partitions.
54
- pub const PartitionMode = enum {
55
- /// Draws the size of the partition uniformly at random from (1, n-1).
56
- /// Replicas are randomly assigned a partition.
57
- uniform_size,
58
-
59
- /// Assigns each node to a partition uniformly at random. This biases towards
60
- /// equal-size partitions.
61
- uniform_partition,
62
-
63
- /// Isolates exactly one replica.
64
- isolate_single,
65
-
66
- /// User-defined partitioning algorithm.
67
- custom,
68
- };
69
-
70
- /// A fully connected network of nodes used for testing. Simulates the fault model:
71
- /// Packets may be dropped.
72
- /// Packets may be delayed.
73
- /// Packets may be replayed.
74
- pub const PacketStatistics = enum(u8) {
75
- dropped_due_to_partition,
76
- dropped_due_to_congestion,
77
- dropped_due_to_crash,
78
- dropped,
79
- replay,
80
- };
81
-
82
- pub fn PacketSimulator(comptime Packet: type) type {
83
- return struct {
84
- const Self = @This();
85
-
86
- const Data = struct {
87
- expiry: u64,
88
- callback: fn (packet: Packet, path: Path) void,
89
- packet: Packet,
90
- };
91
-
92
- /// A send and receive path between each node in the network. We use the `path` function to
93
- /// index it.
94
- paths: []std.PriorityQueue(Data, void, Self.order_packets),
95
-
96
- /// We can arbitrary clog a path until a tick.
97
- path_clogged_till: []u64,
98
- ticks: u64 = 0,
99
- options: PacketSimulatorOptions,
100
- prng: std.rand.DefaultPrng,
101
- stats: [@typeInfo(PacketStatistics).Enum.fields.len]u32 = [_]u32{0} **
102
- @typeInfo(PacketStatistics).Enum.fields.len,
103
-
104
- is_partitioned: bool,
105
- partition: []bool,
106
- replicas: []u8,
107
- stability: u32,
108
-
109
- pub fn init(allocator: std.mem.Allocator, options: PacketSimulatorOptions) !Self {
110
- assert(options.one_way_delay_mean >= options.one_way_delay_min);
111
- var self = Self{
112
- .paths = try allocator.alloc(
113
- std.PriorityQueue(Data, void, Self.order_packets),
114
- @as(usize, options.node_count) * options.node_count,
115
- ),
116
- .path_clogged_till = try allocator.alloc(
117
- u64,
118
- @as(usize, options.node_count) * options.node_count,
119
- ),
120
- .options = options,
121
- .prng = std.rand.DefaultPrng.init(options.seed),
122
-
123
- .is_partitioned = false,
124
- .stability = options.unpartition_stability,
125
- .partition = try allocator.alloc(bool, @as(usize, options.replica_count)),
126
- .replicas = try allocator.alloc(u8, @as(usize, options.replica_count)),
127
- };
128
-
129
- for (self.replicas) |_, i| {
130
- self.replicas[i] = @intCast(u8, i);
131
- }
132
-
133
- for (self.paths) |*queue| {
134
- queue.* = std.PriorityQueue(Data, void, Self.order_packets).init(allocator, {});
135
- try queue.ensureTotalCapacity(options.path_maximum_capacity);
136
- }
137
-
138
- for (self.path_clogged_till) |*clogged_till| {
139
- clogged_till.* = 0;
140
- }
141
-
142
- return self;
143
- }
144
-
145
- pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
146
- for (self.paths) |*queue| {
147
- while (queue.popOrNull()) |*data| data.packet.deinit();
148
- queue.deinit();
149
- }
150
- allocator.free(self.paths);
151
- allocator.free(self.path_clogged_till);
152
- allocator.free(self.partition);
153
- allocator.free(self.replicas);
154
- }
155
-
156
- fn order_packets(context: void, a: Data, b: Data) math.Order {
157
- _ = context;
158
-
159
- return math.order(a.expiry, b.expiry);
160
- }
161
-
162
- fn should_drop(self: *Self) bool {
163
- return self.prng.random().uintAtMost(u8, 100) < self.options.packet_loss_probability;
164
- }
165
-
166
- fn path_index(self: *Self, path: Path) usize {
167
- assert(path.source < self.options.node_count and path.target < self.options.node_count);
168
-
169
- return @as(usize, path.source) * self.options.node_count + path.target;
170
- }
171
-
172
- pub fn path_queue(self: *Self, path: Path) *std.PriorityQueue(Data, void, Self.order_packets) {
173
- return &self.paths[self.path_index(path)];
174
- }
175
-
176
- fn is_clogged(self: *Self, path: Path) bool {
177
- return self.path_clogged_till[self.path_index(path)] > self.ticks;
178
- }
179
-
180
- fn should_clog(self: *Self, path: Path) bool {
181
- _ = path;
182
-
183
- return self.prng.random().uintAtMost(u8, 100) < self.options.path_clog_probability;
184
- }
185
-
186
- fn clog_for(self: *Self, path: Path, ticks: u64) void {
187
- const clog_expiry = &self.path_clogged_till[self.path_index(path)];
188
- clog_expiry.* = self.ticks + ticks;
189
- log.debug("Path path.source={} path.target={} clogged for ticks={}", .{
190
- path.source,
191
- path.target,
192
- ticks,
193
- });
194
- }
195
-
196
- fn should_replay(self: *Self) bool {
197
- return self.prng.random().uintAtMost(u8, 100) < self.options.packet_replay_probability;
198
- }
199
-
200
- fn should_partition(self: *Self) bool {
201
- return self.prng.random().uintAtMost(u8, 100) < self.options.partition_probability;
202
- }
203
-
204
- fn should_unpartition(self: *Self) bool {
205
- return self.prng.random().uintAtMost(u8, 100) < self.options.unpartition_probability;
206
- }
207
-
208
- /// Return a value produced using an exponential distribution with
209
- /// the minimum and mean specified in self.options
210
- fn one_way_delay(self: *Self) u64 {
211
- const min = self.options.one_way_delay_min;
212
- const mean = self.options.one_way_delay_mean;
213
- return min + @floatToInt(u64, @intToFloat(f64, mean - min) * self.prng.random().floatExp(f64));
214
- }
215
-
216
- /// Partitions the network. Guaranteed to isolate at least one replica.
217
- fn partition_network(
218
- self: *Self,
219
- ) void {
220
- assert(self.options.replica_count > 1);
221
-
222
- self.is_partitioned = true;
223
- self.stability = self.options.partition_stability;
224
-
225
- switch (self.options.partition_mode) {
226
- .uniform_size => {
227
- // Exclude cases sz == 0 and sz == replica_count
228
- const sz =
229
- 1 + self.prng.random().uintAtMost(u8, self.options.replica_count - 2);
230
- self.prng.random().shuffle(u8, self.replicas);
231
- for (self.replicas) |r, i| {
232
- self.partition[r] = i < sz;
233
- }
234
- },
235
- .uniform_partition => {
236
- var only_same = true;
237
- self.partition[0] =
238
- self.prng.random().uintLessThan(u8, 2) == 1;
239
-
240
- var i: usize = 1;
241
- while (i < self.options.replica_count) : (i += 1) {
242
- self.partition[i] =
243
- self.prng.random().uintLessThan(u8, 2) == 1;
244
- only_same =
245
- only_same and (self.partition[i - 1] == self.partition[i]);
246
- }
247
-
248
- if (only_same) {
249
- const n = self.prng.random().uintLessThan(u8, self.options.replica_count);
250
- self.partition[n] = true;
251
- }
252
- },
253
- .isolate_single => {
254
- for (self.replicas) |_, i| {
255
- self.partition[i] = false;
256
- }
257
- const n = self.prng.random().uintLessThan(u8, self.options.replica_count);
258
- self.partition[n] = true;
259
- },
260
- // Put your own partitioning logic here.
261
- .custom => unreachable,
262
- }
263
- }
264
-
265
- fn unpartition_network(
266
- self: *Self,
267
- ) void {
268
- self.is_partitioned = false;
269
- self.stability = self.options.unpartition_stability;
270
-
271
- for (self.replicas) |_, i| {
272
- self.partition[i] = false;
273
- }
274
- }
275
-
276
- fn replicas_are_in_different_partitions(self: *Self, from: u8, to: u8) bool {
277
- return from < self.options.replica_count and
278
- to < self.options.replica_count and
279
- self.partition[from] != self.partition[to];
280
- }
281
-
282
- pub fn tick(self: *Self, cluster_health: []const ReplicaHealth) void {
283
- self.ticks += 1;
284
-
285
- if (self.stability > 0) {
286
- self.stability -= 1;
287
- } else {
288
- if (self.is_partitioned) {
289
- if (self.should_unpartition()) {
290
- self.unpartition_network();
291
- log.err("unpartitioned network: partition={d}", .{self.partition});
292
- }
293
- } else {
294
- if (self.options.replica_count > 1 and self.should_partition()) {
295
- self.partition_network();
296
- log.err("partitioned network: partition={d}", .{self.partition});
297
- }
298
- }
299
- }
300
-
301
- var from: u8 = 0;
302
- while (from < self.options.node_count) : (from += 1) {
303
- var to: u8 = 0;
304
- while (to < self.options.node_count) : (to += 1) {
305
- const path = .{ .source = from, .target = to };
306
- if (self.is_clogged(path)) continue;
307
-
308
- const queue = self.path_queue(path);
309
- while (queue.peek()) |*data| {
310
- if (data.expiry > self.ticks) break;
311
- _ = queue.remove();
312
-
313
- if (self.is_partitioned and
314
- self.replicas_are_in_different_partitions(from, to))
315
- {
316
- self.stats[@enumToInt(PacketStatistics.dropped_due_to_partition)] += 1;
317
- log.err("dropped packet (different partitions): from={} to={}", .{ from, to });
318
- data.packet.deinit(path);
319
- continue;
320
- }
321
-
322
- if (self.should_drop()) {
323
- self.stats[@enumToInt(PacketStatistics.dropped)] += 1;
324
- log.err("dropped packet from={} to={}.", .{ from, to });
325
- data.packet.deinit(path);
326
- continue;
327
- }
328
-
329
- if (to < self.options.replica_count and cluster_health[to] == .down) {
330
- self.stats[@enumToInt(PacketStatistics.dropped_due_to_crash)] += 1;
331
- log.err("dropped packet (destination is crashed): from={} to={}", .{ from, to });
332
- data.packet.deinit(path);
333
- continue;
334
- }
335
-
336
- if (self.should_replay()) {
337
- self.submit_packet(data.packet, data.callback, path);
338
-
339
- log.debug("replayed packet from={} to={}", .{ from, to });
340
- self.stats[@enumToInt(PacketStatistics.replay)] += 1;
341
-
342
- data.callback(data.packet, path);
343
- } else {
344
- log.debug("delivering packet from={} to={}", .{ from, to });
345
- data.callback(data.packet, path);
346
- data.packet.deinit(path);
347
- }
348
- }
349
-
350
- const reverse_path: Path = .{ .source = to, .target = from };
351
-
352
- if (self.should_clog(reverse_path)) {
353
- log.debug("reverse path clogged", .{});
354
- const mean = @intToFloat(f64, self.options.path_clog_duration_mean);
355
- const ticks = @floatToInt(u64, mean * self.prng.random().floatExp(f64));
356
- self.clog_for(reverse_path, ticks);
357
- }
358
- }
359
- }
360
- }
361
-
362
- pub fn submit_packet(
363
- self: *Self,
364
- packet: Packet,
365
- callback: fn (packet: Packet, path: Path) void,
366
- path: Path,
367
- ) void {
368
- const queue = self.path_queue(path);
369
- var queue_length = queue.count();
370
- if (queue_length + 1 > queue.capacity()) {
371
- const index = self.prng.random().uintLessThanBiased(u64, queue_length);
372
- const data = queue.removeIndex(index);
373
- data.packet.deinit(path);
374
- log.err("submit_packet: {} reached capacity, dropped packet={}", .{
375
- path,
376
- index,
377
- });
378
- }
379
-
380
- queue.add(.{
381
- .expiry = self.ticks + self.one_way_delay(),
382
- .packet = packet,
383
- .callback = callback,
384
- }) catch unreachable;
385
- }
386
- };
387
- }