tigerbeetle-node 0.9.0 → 0.10.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 (83) hide show
  1. package/README.md +3 -2
  2. package/dist/index.d.ts +66 -61
  3. package/dist/index.js +66 -61
  4. package/dist/index.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/index.ts +5 -0
  7. package/src/node.zig +17 -18
  8. package/src/tigerbeetle/scripts/benchmark.bat +4 -3
  9. package/src/tigerbeetle/scripts/benchmark.sh +25 -10
  10. package/src/tigerbeetle/scripts/install.sh +2 -1
  11. package/src/tigerbeetle/scripts/install_zig.sh +14 -18
  12. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +12 -3
  13. package/src/tigerbeetle/scripts/vopr.sh +5 -5
  14. package/src/tigerbeetle/src/benchmark.zig +17 -9
  15. package/src/tigerbeetle/src/benchmark_array_search.zig +317 -0
  16. package/src/tigerbeetle/src/benchmarks/perf.zig +299 -0
  17. package/src/tigerbeetle/src/c/tb_client/context.zig +103 -0
  18. package/src/tigerbeetle/src/c/tb_client/packet.zig +80 -0
  19. package/src/tigerbeetle/src/c/tb_client/signal.zig +288 -0
  20. package/src/tigerbeetle/src/c/tb_client/thread.zig +329 -0
  21. package/src/tigerbeetle/src/c/tb_client.h +201 -0
  22. package/src/tigerbeetle/src/c/tb_client.zig +101 -0
  23. package/src/tigerbeetle/src/c/test.zig +1 -0
  24. package/src/tigerbeetle/src/cli.zig +142 -83
  25. package/src/tigerbeetle/src/config.zig +119 -10
  26. package/src/tigerbeetle/src/demo.zig +12 -8
  27. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +2 -2
  28. package/src/tigerbeetle/src/ewah.zig +318 -0
  29. package/src/tigerbeetle/src/ewah_benchmark.zig +121 -0
  30. package/src/tigerbeetle/src/eytzinger_benchmark.zig +317 -0
  31. package/src/tigerbeetle/src/fifo.zig +17 -1
  32. package/src/tigerbeetle/src/io/darwin.zig +12 -10
  33. package/src/tigerbeetle/src/io/linux.zig +25 -9
  34. package/src/tigerbeetle/src/io/windows.zig +13 -9
  35. package/src/tigerbeetle/src/iops.zig +101 -0
  36. package/src/tigerbeetle/src/lsm/binary_search.zig +214 -0
  37. package/src/tigerbeetle/src/lsm/bloom_filter.zig +82 -0
  38. package/src/tigerbeetle/src/lsm/compaction.zig +603 -0
  39. package/src/tigerbeetle/src/lsm/composite_key.zig +75 -0
  40. package/src/tigerbeetle/src/lsm/direction.zig +11 -0
  41. package/src/tigerbeetle/src/lsm/eytzinger.zig +587 -0
  42. package/src/tigerbeetle/src/lsm/forest.zig +630 -0
  43. package/src/tigerbeetle/src/lsm/grid.zig +473 -0
  44. package/src/tigerbeetle/src/lsm/groove.zig +939 -0
  45. package/src/tigerbeetle/src/lsm/k_way_merge.zig +452 -0
  46. package/src/tigerbeetle/src/lsm/level_iterator.zig +296 -0
  47. package/src/tigerbeetle/src/lsm/manifest.zig +680 -0
  48. package/src/tigerbeetle/src/lsm/manifest_level.zig +1169 -0
  49. package/src/tigerbeetle/src/lsm/manifest_log.zig +904 -0
  50. package/src/tigerbeetle/src/lsm/node_pool.zig +231 -0
  51. package/src/tigerbeetle/src/lsm/posted_groove.zig +399 -0
  52. package/src/tigerbeetle/src/lsm/segmented_array.zig +998 -0
  53. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +844 -0
  54. package/src/tigerbeetle/src/lsm/table.zig +932 -0
  55. package/src/tigerbeetle/src/lsm/table_immutable.zig +196 -0
  56. package/src/tigerbeetle/src/lsm/table_iterator.zig +295 -0
  57. package/src/tigerbeetle/src/lsm/table_mutable.zig +123 -0
  58. package/src/tigerbeetle/src/lsm/test.zig +429 -0
  59. package/src/tigerbeetle/src/lsm/tree.zig +1085 -0
  60. package/src/tigerbeetle/src/main.zig +119 -109
  61. package/src/tigerbeetle/src/message_bus.zig +49 -48
  62. package/src/tigerbeetle/src/message_pool.zig +15 -2
  63. package/src/tigerbeetle/src/ring_buffer.zig +126 -30
  64. package/src/tigerbeetle/src/simulator.zig +76 -44
  65. package/src/tigerbeetle/src/state_machine.zig +1022 -585
  66. package/src/tigerbeetle/src/storage.zig +46 -16
  67. package/src/tigerbeetle/src/test/cluster.zig +109 -63
  68. package/src/tigerbeetle/src/test/message_bus.zig +15 -24
  69. package/src/tigerbeetle/src/test/network.zig +26 -17
  70. package/src/tigerbeetle/src/test/state_checker.zig +7 -5
  71. package/src/tigerbeetle/src/test/state_machine.zig +159 -69
  72. package/src/tigerbeetle/src/test/storage.zig +57 -28
  73. package/src/tigerbeetle/src/tigerbeetle.zig +5 -0
  74. package/src/tigerbeetle/src/unit_tests.zig +8 -0
  75. package/src/tigerbeetle/src/util.zig +51 -0
  76. package/src/tigerbeetle/src/vsr/client.zig +21 -7
  77. package/src/tigerbeetle/src/vsr/journal.zig +154 -167
  78. package/src/tigerbeetle/src/vsr/replica.zig +744 -226
  79. package/src/tigerbeetle/src/vsr/superblock.zig +1743 -0
  80. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +258 -0
  81. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +644 -0
  82. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +546 -0
  83. package/src/tigerbeetle/src/vsr.zig +43 -115
@@ -3,41 +3,46 @@ const assert = std.debug.assert;
3
3
  const fmt = std.fmt;
4
4
  const mem = std.mem;
5
5
  const os = std.os;
6
- const log = std.log;
6
+ const log = std.log.scoped(.main);
7
7
 
8
8
  const config = @import("config.zig");
9
9
  pub const log_level: std.log.Level = @intToEnum(std.log.Level, config.log_level);
10
10
 
11
11
  const cli = @import("cli.zig");
12
+ const fatal = cli.fatal;
12
13
 
13
14
  const IO = @import("io.zig").IO;
14
15
  const Time = @import("time.zig").Time;
15
16
  const Storage = @import("storage.zig").Storage;
17
+
16
18
  const MessageBus = @import("message_bus.zig").MessageBusReplica;
17
- const StateMachine = @import("state_machine.zig").StateMachine;
19
+ const MessagePool = @import("message_pool.zig").MessagePool;
20
+ const StateMachine = @import("state_machine.zig").StateMachineType(Storage);
18
21
 
19
22
  const vsr = @import("vsr.zig");
20
- const Replica = vsr.Replica(StateMachine, MessageBus, Storage, Time);
23
+ const Replica = vsr.ReplicaType(StateMachine, MessageBus, Storage, Time);
21
24
 
22
- pub fn main() !void {
23
- var io = try IO.init(128, 0);
24
- defer io.deinit();
25
+ const SuperBlock = vsr.SuperBlockType(Storage);
26
+ const superblock_zone_size = @import("vsr/superblock.zig").superblock_zone_size;
27
+ const data_file_size_min = @import("vsr/superblock.zig").data_file_size_min;
28
+
29
+ comptime {
30
+ assert(config.deployment_environment == .production or
31
+ config.deployment_environment == .development);
32
+ }
25
33
 
34
+ pub fn main() !void {
26
35
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
27
36
  defer arena.deinit();
28
37
 
29
38
  const allocator = arena.allocator();
30
39
 
31
- switch (try cli.parse_args(allocator)) {
32
- .init => |args| try init(&io, args.cluster, args.replica, args.dir_fd),
33
- .start => |args| try start(
34
- &io,
35
- allocator,
36
- args.cluster,
37
- args.replica,
38
- args.addresses,
39
- args.dir_fd,
40
- ),
40
+ var parse_args = try cli.parse_args(allocator);
41
+ defer parse_args.deinit(allocator);
42
+
43
+ switch (parse_args) {
44
+ .format => |*args| try Command.format(allocator, args.cluster, args.replica, args.path),
45
+ .start => |*args| try Command.start(allocator, args.addresses, args.memory, args.path),
41
46
  }
42
47
  }
43
48
 
@@ -45,100 +50,105 @@ pub fn main() !void {
45
50
  const filename_fmt = "cluster_{d:0>10}_replica_{d:0>3}.tigerbeetle";
46
51
  const filename_len = fmt.count(filename_fmt, .{ 0, 0 });
47
52
 
48
- /// Create a .tigerbeetle data file for the given args and exit
49
- fn init(io: *IO, cluster: u32, replica: u8, dir_fd: os.fd_t) !void {
50
- // Add 1 for the terminating null byte
51
- var buffer: [filename_len + 1]u8 = undefined;
52
- const filename = fmt.bufPrintZ(&buffer, filename_fmt, .{ cluster, replica }) catch unreachable;
53
- assert(filename.len == filename_len);
54
-
55
- // TODO Expose data file size on the CLI.
56
- const fd = try io.open_file(
57
- dir_fd,
58
- filename,
59
- config.journal_size_max,
60
- true,
61
- );
62
- std.os.close(fd);
63
-
64
- const file = try (std.fs.Dir{ .fd = dir_fd }).openFile(filename, .{ .write = true });
65
- defer file.close();
66
-
67
- {
68
- const write_size_max = 4 * 1024 * 1024;
69
- var write: [write_size_max]u8 = undefined;
70
- var offset: u64 = 0;
71
- while (true) {
72
- const write_size = vsr.format_journal(cluster, offset, &write);
73
- if (write_size == 0) break;
74
- try file.writeAll(write[0..write_size]);
75
- offset += write_size;
76
- }
53
+ const Command = struct {
54
+ dir_fd: os.fd_t,
55
+ fd: os.fd_t,
56
+ io: IO,
57
+ storage: Storage,
58
+ message_pool: MessagePool,
59
+
60
+ fn init(
61
+ command: *Command,
62
+ allocator: mem.Allocator,
63
+ path: [:0]const u8,
64
+ must_create: bool,
65
+ ) !void {
66
+ // TODO Resolve the parent directory properly in the presence of .. and symlinks.
67
+ // TODO Handle physical volumes where there is no directory to fsync.
68
+ const dirname = std.fs.path.dirname(path) orelse ".";
69
+ command.dir_fd = try IO.open_dir(dirname);
70
+ errdefer os.close(command.dir_fd);
71
+
72
+ const basename = std.fs.path.basename(path);
73
+ command.fd = try IO.open_file(command.dir_fd, basename, data_file_size_min, must_create);
74
+ errdefer os.close(command.fd);
75
+
76
+ command.io = try IO.init(128, 0);
77
+ errdefer command.io.deinit();
78
+
79
+ command.storage = try Storage.init(&command.io, command.fd);
80
+ errdefer command.storage.deinit();
81
+
82
+ command.message_pool = try MessagePool.init(allocator, .replica);
83
+ errdefer command.message_pool.deinit(allocator);
77
84
  }
78
85
 
79
- log.info("initialized data file", .{});
80
- }
86
+ fn deinit(command: *Command, allocator: mem.Allocator) void {
87
+ command.message_pool.deinit(allocator);
88
+ command.storage.deinit();
89
+ command.io.deinit();
90
+ os.close(command.fd);
91
+ os.close(command.dir_fd);
92
+ }
81
93
 
82
- /// Run as a replica server defined by the given args
83
- fn start(
84
- io: *IO,
85
- allocator: mem.Allocator,
86
- cluster: u32,
87
- replica_index: u8,
88
- addresses: []std.net.Address,
89
- dir_fd: os.fd_t,
90
- ) !void {
91
- // Add 1 for the terminating null byte
92
- var buffer: [filename_len + 1]u8 = undefined;
93
- const filename = fmt.bufPrintZ(&buffer, filename_fmt, .{ cluster, replica_index }) catch {
94
- unreachable;
95
- };
96
- assert(filename.len == filename_len);
97
-
98
- // TODO Expose data file size on the CLI.
99
- const storage_fd = try io.open_file(
100
- dir_fd,
101
- filename,
102
- config.journal_size_max, // TODO Double-check that we have space for redundant headers.
103
- false,
104
- );
105
-
106
- var state_machine = try StateMachine.init(
107
- allocator,
108
- config.accounts_max,
109
- config.transfers_max,
110
- config.transfers_pending_max,
111
- );
112
- var storage = try Storage.init(config.journal_size_max, storage_fd, io);
113
- var message_bus = try MessageBus.init(
114
- allocator,
115
- cluster,
116
- addresses,
117
- replica_index,
118
- io,
119
- );
120
- var time: Time = .{};
121
- var replica = try Replica.init(
122
- allocator,
123
- cluster,
124
- @intCast(u8, addresses.len),
125
- replica_index,
126
- &time,
127
- &storage,
128
- &message_bus,
129
- &state_machine,
130
- );
131
- message_bus.set_on_message(*Replica, &replica, Replica.on_message);
132
-
133
- log.info("cluster={x} replica={}: listening on {}", .{
134
- cluster,
135
- replica_index,
136
- addresses[replica_index],
137
- });
138
-
139
- while (true) {
140
- replica.tick();
141
- message_bus.tick();
142
- try io.run_for_ns(config.tick_ms * std.time.ns_per_ms);
94
+ pub fn format(allocator: mem.Allocator, cluster: u32, replica: u8, path: [:0]const u8) !void {
95
+ var command: Command = undefined;
96
+ try command.init(allocator, path, true);
97
+ defer command.deinit(allocator);
98
+
99
+ var superblock = try SuperBlock.init(
100
+ allocator,
101
+ &command.storage,
102
+ &command.message_pool,
103
+ );
104
+ defer superblock.deinit(allocator);
105
+
106
+ try vsr.format(Storage, allocator, cluster, replica, &command.storage, &superblock);
143
107
  }
144
- }
108
+
109
+ pub fn start(
110
+ allocator: mem.Allocator,
111
+ addresses: []std.net.Address,
112
+ memory: u64,
113
+ path: [:0]const u8,
114
+ ) !void {
115
+ _ = memory; // TODO
116
+
117
+ var command: Command = undefined;
118
+ try command.init(allocator, path, false);
119
+ defer command.deinit(allocator);
120
+
121
+ var replica: Replica = undefined;
122
+ try replica.open(allocator, .{
123
+ .replica_count = @intCast(u8, addresses.len),
124
+ .storage = &command.storage,
125
+ .message_pool = &command.message_pool,
126
+ .time = .{},
127
+ .state_machine_options = .{
128
+ // TODO Tune lsm_forest_node_count better.
129
+ .lsm_forest_node_count = 4096,
130
+ .cache_size_accounts = config.accounts_max,
131
+ .cache_size_transfers = config.transfers_max,
132
+ .cache_size_posted = config.transfers_pending_max,
133
+ },
134
+ .message_bus_options = .{
135
+ .configuration = addresses,
136
+ .io = &command.io,
137
+ },
138
+ }) catch |err| switch (err) {
139
+ error.NoAddress => fatal("all --addresses must be provided", .{}),
140
+ else => err,
141
+ };
142
+
143
+ log.info("{}: cluster={}: listening on {}", .{
144
+ replica.replica,
145
+ replica.cluster,
146
+ addresses[replica.replica],
147
+ });
148
+
149
+ while (true) {
150
+ replica.tick();
151
+ try command.io.run_for_ns(config.tick_ms * std.time.ns_per_ms);
152
+ }
153
+ }
154
+ };
@@ -17,25 +17,30 @@ const IO = @import("io.zig").IO;
17
17
  const MessagePool = @import("message_pool.zig").MessagePool;
18
18
  const Message = MessagePool.Message;
19
19
 
20
- pub const MessageBusReplica = MessageBusImpl(.replica);
21
- pub const MessageBusClient = MessageBusImpl(.client);
20
+ pub const MessageBusReplica = MessageBusType(.replica);
21
+ pub const MessageBusClient = MessageBusType(.client);
22
22
 
23
- fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
23
+ fn MessageBusType(comptime process_type: vsr.ProcessType) type {
24
24
  const SendQueue = RingBuffer(*Message, switch (process_type) {
25
25
  .replica => config.connection_send_queue_max_replica,
26
26
  // A client has at most 1 in-flight request, plus pings.
27
27
  .client => config.connection_send_queue_max_client,
28
- });
28
+ }, .array);
29
29
 
30
30
  const tcp_sndbuf = switch (process_type) {
31
31
  .replica => config.tcp_sndbuf_replica,
32
32
  .client => config.tcp_sndbuf_client,
33
33
  };
34
34
 
35
+ const Process = union(vsr.ProcessType) {
36
+ replica: u8,
37
+ client: u128,
38
+ };
39
+
35
40
  return struct {
36
41
  const Self = @This();
37
42
 
38
- pool: MessagePool,
43
+ pool: *MessagePool,
39
44
  io: *IO,
40
45
 
41
46
  cluster: u32,
@@ -59,10 +64,8 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
59
64
  .client => void,
60
65
  },
61
66
 
62
- /// The callback to be called when a message is received. Use set_on_message() to set
63
- /// with type safety for the context pointer.
64
- on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
65
- on_message_context: ?*anyopaque = null,
67
+ /// The callback to be called when a message is received.
68
+ on_message_callback: fn (message_bus: *Self, message: *Message) void,
66
69
 
67
70
  /// This slice is allocated with a fixed size in the init function and never reallocated.
68
71
  connections: []Connection,
@@ -80,49 +83,54 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
80
83
  /// Seeded with the process' replica index or client ID.
81
84
  prng: std.rand.DefaultPrng,
82
85
 
86
+ pub const Options = struct {
87
+ configuration: []std.net.Address,
88
+ io: *IO,
89
+ };
90
+
83
91
  /// Initialize the MessageBus for the given cluster, configuration and replica/client process.
84
92
  pub fn init(
85
93
  allocator: mem.Allocator,
86
94
  cluster: u32,
87
- configuration: []std.net.Address,
88
- process: switch (process_type) {
89
- .replica => u8,
90
- .client => u128,
91
- },
92
- io: *IO,
95
+ process: Process,
96
+ message_pool: *MessagePool,
97
+ on_message_callback: fn (message_bus: *Self, message: *Message) void,
98
+ options: Options,
93
99
  ) !Self {
94
100
  // There must be enough connections for all replicas and at least one client.
95
- assert(config.connections_max > configuration.len);
101
+ assert(config.connections_max > options.configuration.len);
102
+ assert(@as(vsr.ProcessType, process) == process_type);
96
103
 
97
104
  const connections = try allocator.alloc(Connection, config.connections_max);
98
105
  errdefer allocator.free(connections);
99
106
  mem.set(Connection, connections, .{});
100
107
 
101
- const replicas = try allocator.alloc(?*Connection, configuration.len);
108
+ const replicas = try allocator.alloc(?*Connection, options.configuration.len);
102
109
  errdefer allocator.free(replicas);
103
110
  mem.set(?*Connection, replicas, null);
104
111
 
105
- const replicas_connect_attempts = try allocator.alloc(u64, configuration.len);
112
+ const replicas_connect_attempts = try allocator.alloc(u64, options.configuration.len);
106
113
  errdefer allocator.free(replicas_connect_attempts);
107
114
  mem.set(u64, replicas_connect_attempts, 0);
108
115
 
109
116
  const prng_seed = switch (process_type) {
110
- .replica => process,
111
- .client => @truncate(u64, process),
117
+ .replica => process.replica,
118
+ .client => @truncate(u64, process.client),
112
119
  };
113
120
 
114
121
  var bus: Self = .{
115
- .pool = try MessagePool.init(allocator, process_type),
116
- .io = io,
122
+ .pool = message_pool,
123
+ .io = options.io,
117
124
  .cluster = cluster,
118
- .configuration = configuration,
125
+ .configuration = options.configuration,
119
126
  .process = switch (process_type) {
120
127
  .replica => .{
121
- .replica = process,
122
- .accept_fd = try init_tcp(io, configuration[process]),
128
+ .replica = process.replica,
129
+ .accept_fd = try init_tcp(options.io, options.configuration[process.replica]),
123
130
  },
124
131
  .client => {},
125
132
  },
133
+ .on_message_callback = on_message_callback,
126
134
  .connections = connections,
127
135
  .replicas = replicas,
128
136
  .replicas_connect_attempts = replicas_connect_attempts,
@@ -137,25 +145,8 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
137
145
  return bus;
138
146
  }
139
147
 
140
- pub fn set_on_message(
141
- bus: *Self,
142
- comptime Context: type,
143
- context: Context,
144
- comptime on_message: fn (context: Context, message: *Message) void,
145
- ) void {
146
- assert(bus.on_message_callback == null);
147
- assert(bus.on_message_context == null);
148
-
149
- bus.on_message_callback = struct {
150
- fn wrapper(_context: ?*anyopaque, message: *Message) void {
151
- on_message(@intToPtr(Context, @ptrToInt(_context)), message);
152
- }
153
- }.wrapper;
154
- bus.on_message_context = context;
155
- }
156
-
157
148
  /// TODO This is required by the Client.
158
- pub fn deinit(_: *Self) void {}
149
+ pub fn deinit(_: *Self, _: std.mem.Allocator) void {}
159
150
 
160
151
  fn init_tcp(io: *IO, address: std.net.Address) !os.socket_t {
161
152
  const fd = try io.open_socket(
@@ -206,9 +197,9 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
206
197
  }
207
198
  }
208
199
 
209
- if (config.tcp_user_timeout > 0) {
200
+ if (config.tcp_user_timeout_ms > 0) {
210
201
  if (is_linux) {
211
- try set(fd, os.IPPROTO.TCP, os.TCP.USER_TIMEOUT, config.tcp_user_timeout);
202
+ try set(fd, os.IPPROTO.TCP, os.TCP.USER_TIMEOUT, config.tcp_user_timeout_ms);
212
203
  }
213
204
  }
214
205
 
@@ -641,7 +632,13 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
641
632
  //assert(!connection.send_submitted);
642
633
  },
643
634
  // Ignore all the remaining errors for now
644
- error.ConnectionAborted, error.ConnectionResetByPeer, error.BlockingOperationInProgress, error.NetworkSubsystemFailed, error.SystemResources, error.Unexpected => {},
635
+ error.ConnectionAborted,
636
+ error.ConnectionResetByPeer,
637
+ error.BlockingOperationInProgress,
638
+ error.NetworkSubsystemFailed,
639
+ error.SystemResources,
640
+ error.Unexpected,
641
+ => {},
645
642
  };
646
643
  },
647
644
  .close => {},
@@ -670,7 +667,11 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
670
667
  return null;
671
668
  }
672
669
 
673
- const header = mem.bytesAsValue(Header, data[0..@sizeOf(Header)]);
670
+ const header = mem.bytesAsValue(
671
+ Header,
672
+ @alignCast(@alignOf(Header), data[0..@sizeOf(Header)]),
673
+ );
674
+
674
675
  if (!connection.recv_checked_header) {
675
676
  if (!header.valid_checksum()) {
676
677
  log.err("invalid header checksum received from {}", .{connection.peer});
@@ -763,7 +764,7 @@ fn MessageBusImpl(comptime process_type: vsr.ProcessType) type {
763
764
  }
764
765
  }
765
766
 
766
- bus.on_message_callback.?(bus.on_message_context, message);
767
+ bus.on_message_callback(bus, message);
767
768
  }
768
769
 
769
770
  fn maybe_set_peer(connection: *Connection, bus: *Self, header: *const Header) void {
@@ -26,6 +26,7 @@ pub const messages_max_replica = messages_max: {
26
26
  sum += config.clients_max; // Replica.client_table
27
27
  sum += 1; // Replica.loopback_queue
28
28
  sum += config.pipeline_max; // Replica.pipeline
29
+ sum += 1; // Replica.commit_prepare
29
30
  // Replica.do_view_change_from_all_replicas quorum:
30
31
  // Replica.recovery_response_quorum is only used for recovery and does not increase the limit.
31
32
  // All other quorums are bitsets.
@@ -79,8 +80,11 @@ pub const MessagePool = struct {
79
80
  return message;
80
81
  }
81
82
 
82
- pub fn body(message: *Message) []u8 {
83
- return message.buffer[@sizeOf(Header)..message.header.size];
83
+ pub fn body(message: *Message) []align(@alignOf(Header)) u8 {
84
+ return @alignCast(
85
+ @alignOf(Header),
86
+ message.buffer[@sizeOf(Header)..message.header.size],
87
+ );
84
88
  }
85
89
  };
86
90
 
@@ -117,6 +121,15 @@ pub const MessagePool = struct {
117
121
 
118
122
  return ret;
119
123
  }
124
+
125
+ /// Frees all messages that were unused or returned to the pool via unref().
126
+ pub fn deinit(pool: *MessagePool, allocator: mem.Allocator) void {
127
+ while (pool.free_list) |message| {
128
+ pool.free_list = message.next;
129
+ allocator.free(message.buffer);
130
+ allocator.destroy(message);
131
+ }
132
+ }
120
133
 
121
134
  /// Get an unused message with a buffer of config.message_size_max.
122
135
  /// The returned message has exactly one reference.