tigerbeetle-node 0.5.0 → 0.6.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 (51) hide show
  1. package/README.md +3 -4
  2. package/package.json +2 -2
  3. package/scripts/postinstall.sh +2 -2
  4. package/src/node.zig +19 -27
  5. package/src/tigerbeetle/scripts/benchmark.bat +46 -0
  6. package/src/tigerbeetle/scripts/install.sh +1 -1
  7. package/src/tigerbeetle/scripts/install_zig.bat +4 -4
  8. package/src/tigerbeetle/scripts/install_zig.sh +4 -2
  9. package/src/tigerbeetle/scripts/lint.zig +8 -2
  10. package/src/tigerbeetle/scripts/vopr.sh +2 -2
  11. package/src/tigerbeetle/src/benchmark.zig +10 -12
  12. package/src/tigerbeetle/src/cli.zig +43 -20
  13. package/src/tigerbeetle/src/config.zig +26 -11
  14. package/src/tigerbeetle/src/demo.zig +119 -97
  15. package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
  16. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
  17. package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
  18. package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
  19. package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
  20. package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
  21. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +2 -3
  22. package/src/tigerbeetle/src/io/benchmark.zig +213 -0
  23. package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +259 -167
  24. package/src/tigerbeetle/src/io/linux.zig +1038 -0
  25. package/src/tigerbeetle/src/io/test.zig +643 -0
  26. package/src/tigerbeetle/src/io/windows.zig +1161 -0
  27. package/src/tigerbeetle/src/io.zig +9 -1328
  28. package/src/tigerbeetle/src/main.zig +28 -15
  29. package/src/tigerbeetle/src/message_bus.zig +78 -107
  30. package/src/tigerbeetle/src/message_pool.zig +65 -58
  31. package/src/tigerbeetle/src/ring_buffer.zig +7 -0
  32. package/src/tigerbeetle/src/simulator.zig +44 -40
  33. package/src/tigerbeetle/src/state_machine.zig +58 -27
  34. package/src/tigerbeetle/src/storage.zig +7 -234
  35. package/src/tigerbeetle/src/test/cluster.zig +5 -8
  36. package/src/tigerbeetle/src/test/message_bus.zig +10 -9
  37. package/src/tigerbeetle/src/test/network.zig +16 -19
  38. package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
  39. package/src/tigerbeetle/src/test/state_checker.zig +4 -3
  40. package/src/tigerbeetle/src/test/state_machine.zig +4 -0
  41. package/src/tigerbeetle/src/test/storage.zig +23 -19
  42. package/src/tigerbeetle/src/test/time.zig +2 -2
  43. package/src/tigerbeetle/src/tigerbeetle.zig +8 -128
  44. package/src/tigerbeetle/src/time.zig +61 -13
  45. package/src/tigerbeetle/src/vsr/client.zig +23 -37
  46. package/src/tigerbeetle/src/vsr/clock.zig +27 -44
  47. package/src/tigerbeetle/src/vsr/journal.zig +9 -12
  48. package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
  49. package/src/tigerbeetle/src/vsr/replica.zig +184 -204
  50. package/src/tigerbeetle/src/vsr.zig +287 -25
  51. package/src/translate.zig +55 -55
@@ -1,4 +1,5 @@
1
1
  const std = @import("std");
2
+ const builtin = @import("builtin");
2
3
  const assert = std.debug.assert;
3
4
  const mem = std.mem;
4
5
 
@@ -16,15 +17,55 @@ comptime {
16
17
  /// message to be shifted to make space for 0 padding to vsr.sector_ceil.
17
18
  const message_size_max_padded = config.message_size_max + config.sector_size;
18
19
 
19
- /// A pool of reference-counted Messages, memory for which is allocated only once
20
- /// during initialization and reused thereafter. The config.message_bus_messages_max
21
- /// and config.message_bus_headers_max values determine the size of this pool.
20
+ /// The number of full-sized messages allocated at initialization by the replica message pool.
21
+ /// There must be enough messages to ensure that the replica can always progress, to avoid deadlock.
22
+ pub const messages_max_replica = messages_max: {
23
+ var sum: usize = 0;
24
+
25
+ sum += config.io_depth_read + config.io_depth_write; // Journal I/O
26
+ sum += config.clients_max; // Replica.client_table
27
+ sum += 1; // Replica.loopback_queue
28
+ sum += config.pipelining_max; // Replica.pipeline
29
+ sum += config.replicas_max; // Replica.do_view_change_from_all_replicas quorum (all others are bitsets)
30
+ sum += config.connections_max; // Connection.recv_message
31
+ sum += config.connections_max * config.connection_send_queue_max_replica; // Connection.send_queue
32
+ sum += 1; // Handle bursts (e.g. Connection.parse_message)
33
+ // Handle Replica.commit_op's reply:
34
+ // (This is separate from the burst +1 because they may occur concurrently).
35
+ sum += 1;
36
+ sum += 20; // TODO Our network simulator allows up to 20 messages for path_capacity_max.
37
+
38
+ break :messages_max sum;
39
+ };
40
+
41
+ /// The number of full-sized messages allocated at initialization by the client message pool.
42
+ pub const messages_max_client = messages_max: {
43
+ var sum: usize = 0;
44
+
45
+ sum += config.replicas_max; // Connection.recv_message
46
+ sum += config.replicas_max * config.connection_send_queue_max_client; // Connection.send_queue
47
+ sum += config.client_request_queue_max; // Client.request_queue
48
+ // Handle bursts (e.g. Connection.parse_message, or sending a ping when the send queue is full).
49
+ sum += 1;
50
+ sum += 20; // TODO Our network simulator allows up to 20 messages for path_capacity_max.
51
+
52
+ break :messages_max sum;
53
+ };
54
+
55
+ comptime {
56
+ // These conditions are necessary (but not sufficient) to prevent deadlocks.
57
+ assert(messages_max_replica > config.replicas_max);
58
+ assert(messages_max_client > config.client_request_queue_max);
59
+ }
60
+
61
+ /// A pool of reference-counted Messages, memory for which is allocated only once during
62
+ /// initialization and reused thereafter. The messages_max values determine the size of this pool.
22
63
  pub const MessagePool = struct {
23
64
  pub const Message = struct {
24
65
  // TODO: replace this with a header() function to save memory
25
66
  header: *Header,
26
- /// Unless this Message is header only, this buffer is in aligned to config.sector_size
27
- /// and casting to that alignment in order to perform Direct I/O is safe.
67
+ /// This buffer is aligned to config.sector_size and casting to that alignment in order
68
+ /// to perform Direct I/O is safe.
28
69
  buffer: []u8,
29
70
  references: u32 = 0,
30
71
  next: ?*Message,
@@ -38,27 +79,23 @@ pub const MessagePool = struct {
38
79
  pub fn body(message: *Message) []u8 {
39
80
  return message.buffer[@sizeOf(Header)..message.header.size];
40
81
  }
41
-
42
- fn header_only(message: Message) bool {
43
- const ret = message.buffer.len == @sizeOf(Header);
44
- assert(ret or message.buffer.len == message_size_max_padded);
45
- return ret;
46
- }
47
82
  };
48
83
 
49
84
  /// List of currently unused messages of message_size_max_padded
50
85
  free_list: ?*Message,
51
- /// List of currently usused header-sized messages
52
- header_only_free_list: ?*Message,
53
86
 
54
- pub fn init(allocator: *mem.Allocator) error{OutOfMemory}!MessagePool {
87
+ pub fn init(allocator: mem.Allocator, process_type: vsr.ProcessType) error{OutOfMemory}!MessagePool {
88
+ const messages_max: usize = switch (process_type) {
89
+ .replica => messages_max_replica,
90
+ .client => messages_max_client,
91
+ };
92
+
55
93
  var ret: MessagePool = .{
56
94
  .free_list = null,
57
- .header_only_free_list = null,
58
95
  };
59
96
  {
60
97
  var i: usize = 0;
61
- while (i < config.message_bus_messages_max) : (i += 1) {
98
+ while (i < messages_max) : (i += 1) {
62
99
  const buffer = try allocator.allocAdvanced(
63
100
  u8,
64
101
  config.sector_size,
@@ -74,59 +111,29 @@ pub const MessagePool = struct {
74
111
  ret.free_list = message;
75
112
  }
76
113
  }
77
- {
78
- var i: usize = 0;
79
- while (i < config.message_bus_headers_max) : (i += 1) {
80
- const header = try allocator.create(Header);
81
- const message = try allocator.create(Message);
82
- message.* = .{
83
- .header = header,
84
- .buffer = mem.asBytes(header),
85
- .next = ret.header_only_free_list,
86
- };
87
- ret.header_only_free_list = message;
88
- }
89
- }
90
114
 
91
115
  return ret;
92
116
  }
93
117
 
94
- /// Get an unused message with a buffer of config.message_size_max. If no such message is
95
- /// available, an error is returned. The returned message has exactly one reference.
96
- pub fn get_message(pool: *MessagePool) ?*Message {
97
- const ret = pool.free_list orelse return null;
98
- pool.free_list = ret.next;
99
- ret.next = null;
100
- assert(!ret.header_only());
101
- assert(ret.references == 0);
102
- ret.references = 1;
103
- return ret;
104
- }
118
+ /// Get an unused message with a buffer of config.message_size_max.
119
+ /// The returned message has exactly one reference.
120
+ pub fn get_message(pool: *MessagePool) *Message {
121
+ const message = pool.free_list.?;
122
+ pool.free_list = message.next;
123
+ message.next = null;
124
+ assert(message.references == 0);
105
125
 
106
- /// Get an unused message with a buffer only large enough to hold a header. If no such message
107
- /// is available, an error is returned. The returned message has exactly one reference.
108
- pub fn get_header_only_message(pool: *MessagePool) ?*Message {
109
- const ret = pool.header_only_free_list orelse return null;
110
- pool.header_only_free_list = ret.next;
111
- ret.next = null;
112
- assert(ret.header_only());
113
- assert(ret.references == 0);
114
- ret.references = 1;
115
- return ret;
126
+ message.references = 1;
127
+ return message;
116
128
  }
117
129
 
118
130
  /// Decrement the reference count of the message, possibly freeing it.
119
131
  pub fn unref(pool: *MessagePool, message: *Message) void {
120
132
  message.references -= 1;
121
133
  if (message.references == 0) {
122
- if (std.builtin.mode == .Debug) mem.set(u8, message.buffer, undefined);
123
- if (message.header_only()) {
124
- message.next = pool.header_only_free_list;
125
- pool.header_only_free_list = message;
126
- } else {
127
- message.next = pool.free_list;
128
- pool.free_list = message;
129
- }
134
+ if (builtin.mode == .Debug) mem.set(u8, message.buffer, undefined);
135
+ message.next = pool.free_list;
136
+ pool.free_list = message;
130
137
  }
131
138
  }
132
139
  };
@@ -76,6 +76,13 @@ pub fn RingBuffer(comptime T: type, comptime size: usize) type {
76
76
  self.advance_tail();
77
77
  }
78
78
 
79
+ /// Add an element to a RingBuffer, and assert that the capacity is sufficient.
80
+ pub fn push_assume_capacity(self: *Self, item: T) void {
81
+ self.push(item) catch |err| switch (err) {
82
+ error.NoSpaceLeft => unreachable,
83
+ };
84
+ }
85
+
79
86
  /// Remove and return the next item, if any.
80
87
  pub fn pop(self: *Self) ?T {
81
88
  const result = self.head() orelse return null;
@@ -1,4 +1,5 @@
1
1
  const std = @import("std");
2
+ const builtin = @import("builtin");
2
3
  const assert = std.debug.assert;
3
4
  const mem = std.mem;
4
5
 
@@ -17,7 +18,7 @@ const output = std.log.scoped(.state_checker);
17
18
 
18
19
  /// Set this to `false` if you want to see how literally everything works.
19
20
  /// This will run much slower but will trace all logic across the cluster.
20
- const log_state_transitions_only = std.builtin.mode != .Debug;
21
+ const log_state_transitions_only = builtin.mode != .Debug;
21
22
 
22
23
  /// You can fine tune your log levels even further (debug/info/notice/warn/err/crit/alert/emerg):
23
24
  pub const log_level: std.log.Level = if (log_state_transitions_only) .info else .debug;
@@ -40,13 +41,13 @@ pub fn main() !void {
40
41
  break :seed_from_arg parse_seed(arg_two);
41
42
  };
42
43
 
43
- if (std.builtin.mode == .ReleaseFast or std.builtin.mode == .ReleaseSmall) {
44
+ if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
44
45
  // We do not support ReleaseFast or ReleaseSmall because they disable assertions.
45
46
  @panic("the simulator must be run with -OReleaseSafe");
46
47
  }
47
48
 
48
49
  if (seed == seed_random) {
49
- if (std.builtin.mode != .ReleaseSafe) {
50
+ if (builtin.mode != .ReleaseSafe) {
50
51
  // If no seed is provided, than Debug is too slow and ReleaseSafe is much faster.
51
52
  @panic("no seed provided: the simulator must be run with -OReleaseSafe");
52
53
  }
@@ -56,53 +57,53 @@ pub fn main() !void {
56
57
  }
57
58
 
58
59
  var prng = std.rand.DefaultPrng.init(seed);
59
- const random = &prng.random;
60
+ const random = prng.random();
60
61
 
61
- const replica_count = 1 + prng.random.uintLessThan(u8, config.replicas_max);
62
- const client_count = 1 + prng.random.uintLessThan(u8, config.clients_max);
62
+ const replica_count = 1 + random.uintLessThan(u8, config.replicas_max);
63
+ const client_count = 1 + random.uintLessThan(u8, config.clients_max);
63
64
  const node_count = replica_count + client_count;
64
65
 
65
66
  const ticks_max = 100_000_000;
66
67
  const transitions_max = config.journal_size_max / config.message_size_max;
67
- const request_probability = 1 + prng.random.uintLessThan(u8, 99);
68
- const idle_on_probability = prng.random.uintLessThan(u8, 20);
69
- const idle_off_probability = 10 + prng.random.uintLessThan(u8, 10);
68
+ const request_probability = 1 + random.uintLessThan(u8, 99);
69
+ const idle_on_probability = random.uintLessThan(u8, 20);
70
+ const idle_off_probability = 10 + random.uintLessThan(u8, 10);
70
71
 
71
- cluster = try Cluster.create(allocator, &prng.random, .{
72
+ cluster = try Cluster.create(allocator, random, .{
72
73
  .cluster = 0,
73
74
  .replica_count = replica_count,
74
75
  .client_count = client_count,
75
- .seed = prng.random.int(u64),
76
+ .seed = random.int(u64),
76
77
  .network_options = .{
77
78
  .packet_simulator_options = .{
78
79
  .replica_count = replica_count,
79
80
  .client_count = client_count,
80
81
  .node_count = node_count,
81
82
 
82
- .seed = prng.random.int(u64),
83
- .one_way_delay_mean = 3 + prng.random.uintLessThan(u16, 10),
84
- .one_way_delay_min = prng.random.uintLessThan(u16, 3),
85
- .packet_loss_probability = prng.random.uintLessThan(u8, 30),
86
- .path_maximum_capacity = 20 + prng.random.uintLessThan(u8, 20),
87
- .path_clog_duration_mean = prng.random.uintLessThan(u16, 500),
88
- .path_clog_probability = prng.random.uintLessThan(u8, 2),
89
- .packet_replay_probability = prng.random.uintLessThan(u8, 50),
83
+ .seed = random.int(u64),
84
+ .one_way_delay_mean = 3 + random.uintLessThan(u16, 10),
85
+ .one_way_delay_min = random.uintLessThan(u16, 3),
86
+ .packet_loss_probability = random.uintLessThan(u8, 30),
87
+ .path_maximum_capacity = 2 + random.uintLessThan(u8, 19),
88
+ .path_clog_duration_mean = random.uintLessThan(u16, 500),
89
+ .path_clog_probability = random.uintLessThan(u8, 2),
90
+ .packet_replay_probability = random.uintLessThan(u8, 50),
90
91
 
91
92
  .partition_mode = random_partition_mode(random),
92
- .partition_probability = prng.random.uintLessThan(u8, 3),
93
- .unpartition_probability = 1 + prng.random.uintLessThan(u8, 10),
94
- .partition_stability = 100 + prng.random.uintLessThan(u32, 100),
95
- .unpartition_stability = prng.random.uintLessThan(u32, 20),
93
+ .partition_probability = random.uintLessThan(u8, 3),
94
+ .unpartition_probability = 1 + random.uintLessThan(u8, 10),
95
+ .partition_stability = 100 + random.uintLessThan(u32, 100),
96
+ .unpartition_stability = random.uintLessThan(u32, 20),
96
97
  },
97
98
  },
98
99
  .storage_options = .{
99
- .seed = prng.random.int(u64),
100
- .read_latency_min = prng.random.uintLessThan(u16, 3),
101
- .read_latency_mean = 3 + prng.random.uintLessThan(u16, 10),
102
- .write_latency_min = prng.random.uintLessThan(u16, 3),
103
- .write_latency_mean = 3 + prng.random.uintLessThan(u16, 10),
104
- .read_fault_probability = prng.random.uintLessThan(u8, 10),
105
- .write_fault_probability = prng.random.uintLessThan(u8, 10),
100
+ .seed = random.int(u64),
101
+ .read_latency_min = random.uintLessThan(u16, 3),
102
+ .read_latency_mean = 3 + random.uintLessThan(u16, 10),
103
+ .write_latency_min = random.uintLessThan(u16, 3),
104
+ .write_latency_mean = 3 + random.uintLessThan(u16, 10),
105
+ .read_fault_probability = random.uintLessThan(u8, 10),
106
+ .write_fault_probability = random.uintLessThan(u8, 10),
106
107
  },
107
108
  });
108
109
  defer cluster.destroy();
@@ -206,23 +207,23 @@ pub fn main() !void {
206
207
  }
207
208
 
208
209
  if (cluster.state_checker.transitions < transitions_max) {
209
- output.emerg("you can reproduce this failure with seed={}", .{seed});
210
+ output.err("you can reproduce this failure with seed={}", .{seed});
210
211
  @panic("unable to complete transitions_max before ticks_max");
211
212
  }
212
213
 
213
214
  assert(cluster.state_checker.convergence());
214
215
 
215
- output.info("\n PASSED", .{});
216
+ output.info("\n PASSED ({} ticks)", .{tick});
216
217
  }
217
218
 
218
219
  /// Returns true, `p` percent of the time, else false.
219
- fn chance(random: *std.rand.Random, p: u8) bool {
220
+ fn chance(random: std.rand.Random, p: u8) bool {
220
221
  assert(p <= 100);
221
222
  return random.uintLessThan(u8, 100) < p;
222
223
  }
223
224
 
224
225
  /// Returns the next argument for the simulator or null (if none available)
225
- fn args_next(args: *std.process.ArgIterator, allocator: *std.mem.Allocator) ?[:0]const u8 {
226
+ fn args_next(args: *std.process.ArgIterator, allocator: std.mem.Allocator) ?[:0]const u8 {
226
227
  const err_or_bytes = args.next(allocator) orelse return null;
227
228
  return err_or_bytes catch @panic("Unable to extract next value from args");
228
229
  }
@@ -232,7 +233,7 @@ fn on_change_replica(replica: *Replica) void {
232
233
  cluster.state_checker.check_state(replica.replica);
233
234
  }
234
235
 
235
- fn send_request(random: *std.rand.Random) bool {
236
+ fn send_request(random: std.rand.Random) bool {
236
237
  const client_index = random.uintLessThan(u8, cluster.options.client_count);
237
238
 
238
239
  const client = &cluster.clients[client_index];
@@ -243,7 +244,7 @@ fn send_request(random: *std.rand.Random) bool {
243
244
  if (client.request_queue.full()) return false;
244
245
  if (checker_request_queue.full()) return false;
245
246
 
246
- const message = client.get_message() orelse return false;
247
+ const message = client.get_message();
247
248
  defer client.unref(message);
248
249
 
249
250
  const body_size_max = config.message_size_max - @sizeOf(Header);
@@ -264,7 +265,7 @@ fn send_request(random: *std.rand.Random) bool {
264
265
  // While hashing the client ID with the request body prevents input collisions across clients,
265
266
  // it's still possible for the same client to generate the same body, and therefore input hash.
266
267
  const client_input = StateMachine.hash(client.id, body);
267
- checker_request_queue.push(client_input) catch unreachable;
268
+ checker_request_queue.push_assume_capacity(client_input);
268
269
  std.log.scoped(.test_client).debug("client {} sending input={x}", .{
269
270
  client_index,
270
271
  client_input,
@@ -280,11 +281,14 @@ fn client_callback(
280
281
  operation: StateMachine.Operation,
281
282
  results: Client.Error![]const u8,
282
283
  ) void {
284
+ _ = operation;
285
+ _ = results catch unreachable;
286
+
283
287
  assert(user_data == 0);
284
288
  }
285
289
 
286
290
  /// Returns a random partitioning mode, excluding .custom
287
- fn random_partition_mode(random: *std.rand.Random) PartitionMode {
291
+ fn random_partition_mode(random: std.rand.Random) PartitionMode {
288
292
  const typeInfo = @typeInfo(PartitionMode).Enum;
289
293
  var enumAsInt = random.uintAtMost(typeInfo.tag_type, typeInfo.fields.len - 2);
290
294
  if (enumAsInt >= @enumToInt(PartitionMode.custom)) enumAsInt += 1;
@@ -310,8 +314,8 @@ pub fn log(
310
314
  const prefix = if (log_state_transitions_only) "" else prefix_default;
311
315
 
312
316
  // Print the message to stdout, silently ignoring any errors
313
- const held = std.debug.getStderrMutex().acquire();
314
- defer held.release();
315
317
  const stderr = std.io.getStdErr().writer();
318
+ std.debug.getStderrMutex().lock();
319
+ defer std.debug.getStderrMutex().unlock();
316
320
  nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
317
321
  }
@@ -2,7 +2,25 @@ const std = @import("std");
2
2
  const assert = std.debug.assert;
3
3
  const log = std.log.scoped(.state_machine);
4
4
 
5
- usingnamespace @import("tigerbeetle.zig");
5
+ const tb = @import("tigerbeetle.zig");
6
+
7
+ const Account = tb.Account;
8
+ const AccountFlags = tb.AccountFlags;
9
+
10
+ const Transfer = tb.Transfer;
11
+ const TransferFlags = tb.TransferFlags;
12
+
13
+ const Commit = tb.Commit;
14
+ const CommitFlags = tb.CommitFlags;
15
+
16
+ const CreateAccountsResult = tb.CreateAccountsResult;
17
+ const CreateTransfersResult = tb.CreateTransfersResult;
18
+ const CommitTransfersResult = tb.CommitTransfersResult;
19
+
20
+ const CreateAccountResult = tb.CreateAccountResult;
21
+ const CreateTransferResult = tb.CreateTransferResult;
22
+ const CommitTransferResult = tb.CommitTransferResult;
23
+ const LookupAccountResult = tb.LookupAccountResult;
6
24
 
7
25
  const HashMapAccounts = std.AutoHashMap(u128, Account);
8
26
  const HashMapTransfers = std.AutoHashMap(u128, Transfer);
@@ -21,13 +39,9 @@ pub const StateMachine = struct {
21
39
  commit_transfers,
22
40
  lookup_accounts,
23
41
  lookup_transfers,
24
-
25
- pub fn jsonStringify(self: Command, options: StringifyOptions, writer: anytype) !void {
26
- try std.fmt.format(writer, "\"{}\"", .{@tagName(self)});
27
- }
28
42
  };
29
43
 
30
- allocator: *std.mem.Allocator,
44
+ allocator: std.mem.Allocator,
31
45
  prepare_timestamp: u64,
32
46
  commit_timestamp: u64,
33
47
  accounts: HashMapAccounts,
@@ -35,22 +49,22 @@ pub const StateMachine = struct {
35
49
  commits: HashMapCommits,
36
50
 
37
51
  pub fn init(
38
- allocator: *std.mem.Allocator,
52
+ allocator: std.mem.Allocator,
39
53
  accounts_max: usize,
40
54
  transfers_max: usize,
41
55
  commits_max: usize,
42
56
  ) !StateMachine {
43
57
  var accounts = HashMapAccounts.init(allocator);
44
58
  errdefer accounts.deinit();
45
- try accounts.ensureCapacity(@intCast(u32, accounts_max));
59
+ try accounts.ensureTotalCapacity(@intCast(u32, accounts_max));
46
60
 
47
61
  var transfers = HashMapTransfers.init(allocator);
48
62
  errdefer transfers.deinit();
49
- try transfers.ensureCapacity(@intCast(u32, transfers_max));
63
+ try transfers.ensureTotalCapacity(@intCast(u32, transfers_max));
50
64
 
51
65
  var commits = HashMapCommits.init(allocator);
52
66
  errdefer commits.deinit();
53
- try commits.ensureCapacity(@intCast(u32, commits_max));
67
+ try commits.ensureTotalCapacity(@intCast(u32, commits_max));
54
68
 
55
69
  // TODO After recovery, set prepare_timestamp max(wall clock, op timestamp).
56
70
  // TODO After recovery, set commit_timestamp max(wall clock, commit timestamp).
@@ -140,6 +154,8 @@ pub const StateMachine = struct {
140
154
  input: []const u8,
141
155
  output: []u8,
142
156
  ) usize {
157
+ _ = client;
158
+
143
159
  return switch (operation) {
144
160
  .init => unreachable,
145
161
  .register => 0,
@@ -263,7 +279,7 @@ pub const StateMachine = struct {
263
279
  const output_len = @divFloor(output.len, @sizeOf(Account)) * @sizeOf(Account);
264
280
  const results = std.mem.bytesAsSlice(Account, output[0..output_len]);
265
281
  var results_count: usize = 0;
266
- for (batch) |id, index| {
282
+ for (batch) |id| {
267
283
  if (self.get_account(id)) |result| {
268
284
  results[results_count] = result.*;
269
285
  results_count += 1;
@@ -277,7 +293,7 @@ pub const StateMachine = struct {
277
293
  const output_len = @divFloor(output.len, @sizeOf(Transfer)) * @sizeOf(Transfer);
278
294
  const results = std.mem.bytesAsSlice(Transfer, output[0..output_len]);
279
295
  var results_count: usize = 0;
280
- for (batch) |id, index| {
296
+ for (batch) |id| {
281
297
  if (self.get_transfer(id)) |result| {
282
298
  results[results_count] = result.*;
283
299
  results_count += 1;
@@ -538,7 +554,8 @@ const testing = std.testing;
538
554
  test "create/lookup accounts" {
539
555
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
540
556
  defer arena.deinit();
541
- const allocator = &arena.allocator;
557
+
558
+ const allocator = arena.allocator();
542
559
 
543
560
  const Vector = struct { result: CreateAccountResult, object: Account };
544
561
 
@@ -693,7 +710,8 @@ test "create/lookup accounts" {
693
710
  test "linked accounts" {
694
711
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
695
712
  defer arena.deinit();
696
- const allocator = &arena.allocator;
713
+
714
+ const allocator = arena.allocator();
697
715
 
698
716
  const accounts_max = 5;
699
717
  const transfers_max = 0;
@@ -733,7 +751,7 @@ test "linked accounts" {
733
751
  std.mem.zeroInit(Account, .{ .id = 3 }),
734
752
  };
735
753
 
736
- var state_machine = try StateMachine.init(allocator, accounts.len, transfers_max, commits_max);
754
+ var state_machine = try StateMachine.init(allocator, accounts_max, transfers_max, commits_max);
737
755
  defer state_machine.deinit();
738
756
 
739
757
  const input = std.mem.asBytes(&accounts);
@@ -774,7 +792,8 @@ test "linked accounts" {
774
792
  test "create/lookup/rollback transfers" {
775
793
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
776
794
  defer arena.deinit();
777
- const allocator = &arena.allocator;
795
+
796
+ const allocator = arena.allocator();
778
797
 
779
798
  var accounts = [_]Account{
780
799
  std.mem.zeroInit(Account, .{ .id = 1 }),
@@ -795,10 +814,12 @@ test "create/lookup/rollback transfers" {
795
814
 
796
815
  state_machine.prepare(0, .create_accounts, input);
797
816
  const size = state_machine.commit(0, .create_accounts, input, output);
798
- const results = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
799
817
 
800
- for (accounts) |account, i| {
801
- try testing.expectEqual(accounts[i], state_machine.get_account(accounts[i].id).?.*);
818
+ const errors = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
819
+ try testing.expectEqual(@as(usize, 0), errors.len);
820
+
821
+ for (accounts) |account| {
822
+ try testing.expectEqual(account, state_machine.get_account(account.id).?.*);
802
823
  }
803
824
 
804
825
  const Vector = struct { result: CreateTransferResult, object: Transfer };
@@ -1088,7 +1109,8 @@ test "create/lookup/rollback transfers" {
1088
1109
  test "create/lookup/rollback commits" {
1089
1110
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1090
1111
  defer arena.deinit();
1091
- const allocator = &arena.allocator;
1112
+
1113
+ const allocator = arena.allocator();
1092
1114
 
1093
1115
  const Vector = struct { result: CommitTransferResult, object: Commit };
1094
1116
 
@@ -1171,10 +1193,13 @@ test "create/lookup/rollback commits" {
1171
1193
  // Accounts:
1172
1194
  state_machine.prepare(0, .create_accounts, input);
1173
1195
  const size = state_machine.commit(0, .create_accounts, input, output);
1174
- const results = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1196
+ {
1197
+ const errors = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1198
+ try testing.expectEqual(@as(usize, 0), errors.len);
1199
+ }
1175
1200
 
1176
- for (accounts) |account, i| {
1177
- try testing.expectEqual(accounts[i], state_machine.get_account(accounts[i].id).?.*);
1201
+ for (accounts) |account| {
1202
+ try testing.expectEqual(account, state_machine.get_account(account.id).?.*);
1178
1203
  }
1179
1204
 
1180
1205
  // Transfers:
@@ -1182,11 +1207,17 @@ test "create/lookup/rollback commits" {
1182
1207
  const output_transfers = try allocator.alloc(u8, 4096);
1183
1208
 
1184
1209
  state_machine.prepare(0, .create_transfers, object_transfers);
1185
- const size_transfers = state_machine.commit(0, .create_transfers, object_transfers, output_transfers);
1186
- const results_transfers = std.mem.bytesAsSlice(CreateTransfersResult, output_transfers[0..size_transfers]);
1210
+ const size_transfers = state_machine.commit(
1211
+ 0,
1212
+ .create_transfers,
1213
+ object_transfers,
1214
+ output_transfers,
1215
+ );
1216
+ const errors = std.mem.bytesAsSlice(CreateTransfersResult, output_transfers[0..size_transfers]);
1217
+ try testing.expectEqual(@as(usize, 0), errors.len);
1187
1218
 
1188
- for (transfers) |transfer, i| {
1189
- try testing.expectEqual(transfers[i], state_machine.get_transfer(transfers[i].id).?.*);
1219
+ for (transfers) |transfer| {
1220
+ try testing.expectEqual(transfer, state_machine.get_transfer(transfer.id).?.*);
1190
1221
  }
1191
1222
 
1192
1223
  // Commits: