tigerbeetle-node 0.11.3 → 0.11.5

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 (74) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +1 -1
  3. package/src/node.zig +10 -5
  4. package/src/tigerbeetle/src/benchmark.zig +4 -4
  5. package/src/tigerbeetle/src/c/tb_client/context.zig +6 -6
  6. package/src/tigerbeetle/src/c/tb_client/echo_client.zig +2 -2
  7. package/src/tigerbeetle/src/c/tb_client/thread.zig +0 -1
  8. package/src/tigerbeetle/src/c/tb_client.h +97 -111
  9. package/src/tigerbeetle/src/c/tb_client.zig +30 -19
  10. package/src/tigerbeetle/src/c/tb_client_header.zig +218 -0
  11. package/src/tigerbeetle/src/c/test.zig +14 -14
  12. package/src/tigerbeetle/src/cli.zig +12 -12
  13. package/src/tigerbeetle/src/config.zig +183 -379
  14. package/src/tigerbeetle/src/constants.zig +394 -0
  15. package/src/tigerbeetle/src/demo.zig +4 -4
  16. package/src/tigerbeetle/src/ewah_fuzz.zig +2 -0
  17. package/src/tigerbeetle/src/io/darwin.zig +4 -4
  18. package/src/tigerbeetle/src/io/linux.zig +6 -6
  19. package/src/tigerbeetle/src/io/windows.zig +4 -4
  20. package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
  21. package/src/tigerbeetle/src/lsm/compaction.zig +15 -10
  22. package/src/tigerbeetle/src/lsm/forest.zig +2 -2
  23. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +18 -15
  24. package/src/tigerbeetle/src/lsm/grid.zig +5 -5
  25. package/src/tigerbeetle/src/lsm/groove.zig +8 -42
  26. package/src/tigerbeetle/src/lsm/level_iterator.zig +2 -2
  27. package/src/tigerbeetle/src/lsm/manifest.zig +19 -23
  28. package/src/tigerbeetle/src/lsm/manifest_level.zig +2 -2
  29. package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -8
  30. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +25 -12
  31. package/src/tigerbeetle/src/lsm/posted_groove.zig +4 -15
  32. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +13 -13
  33. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +2 -2
  34. package/src/tigerbeetle/src/lsm/table.zig +43 -35
  35. package/src/tigerbeetle/src/lsm/table_immutable.zig +4 -4
  36. package/src/tigerbeetle/src/lsm/table_iterator.zig +17 -9
  37. package/src/tigerbeetle/src/lsm/table_mutable.zig +3 -3
  38. package/src/tigerbeetle/src/lsm/test.zig +6 -6
  39. package/src/tigerbeetle/src/lsm/tree.zig +75 -47
  40. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +27 -28
  41. package/src/tigerbeetle/src/main.zig +32 -23
  42. package/src/tigerbeetle/src/message_bus.zig +25 -25
  43. package/src/tigerbeetle/src/message_pool.zig +17 -17
  44. package/src/tigerbeetle/src/simulator.zig +7 -12
  45. package/src/tigerbeetle/src/state_machine.zig +582 -1806
  46. package/src/tigerbeetle/src/storage.zig +12 -12
  47. package/src/tigerbeetle/src/test/accounting/auditor.zig +2 -2
  48. package/src/tigerbeetle/src/test/accounting/workload.zig +5 -5
  49. package/src/tigerbeetle/src/test/cluster.zig +8 -8
  50. package/src/tigerbeetle/src/test/conductor.zig +6 -5
  51. package/src/tigerbeetle/src/test/fuzz.zig +19 -0
  52. package/src/tigerbeetle/src/test/message_bus.zig +0 -2
  53. package/src/tigerbeetle/src/test/network.zig +5 -5
  54. package/src/tigerbeetle/src/test/state_checker.zig +2 -2
  55. package/src/tigerbeetle/src/test/storage.zig +54 -51
  56. package/src/tigerbeetle/src/test/storage_checker.zig +3 -3
  57. package/src/tigerbeetle/src/test/table.zig +226 -0
  58. package/src/tigerbeetle/src/time.zig +0 -1
  59. package/src/tigerbeetle/src/tracer.zig +402 -214
  60. package/src/tigerbeetle/src/unit_tests.zig +1 -0
  61. package/src/tigerbeetle/src/vsr/client.zig +5 -5
  62. package/src/tigerbeetle/src/vsr/clock.zig +9 -8
  63. package/src/tigerbeetle/src/vsr/journal.zig +47 -47
  64. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +13 -11
  65. package/src/tigerbeetle/src/vsr/replica.zig +56 -54
  66. package/src/tigerbeetle/src/vsr/replica_format.zig +8 -8
  67. package/src/tigerbeetle/src/vsr/superblock.zig +55 -55
  68. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +9 -9
  69. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +4 -3
  70. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +2 -0
  71. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +9 -6
  72. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +5 -5
  73. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +2 -0
  74. package/src/tigerbeetle/src/vsr.zig +20 -20
@@ -0,0 +1,218 @@
1
+ const std = @import("std");
2
+ const tb = @import("../tigerbeetle.zig");
3
+ const tb_client = @import("tb_client.zig");
4
+
5
+ const type_mappings = .{
6
+ .{ tb.AccountFlags, "TB_ACCOUNT_FLAGS" },
7
+ .{ tb.Account, "tb_account_t" },
8
+ .{ tb.TransferFlags, "TB_TRANSFER_FLAGS" },
9
+ .{ tb.Transfer, "tb_transfer_t" },
10
+ .{ tb.CreateAccountResult, "TB_CREATE_ACCOUNT_RESULT" },
11
+ .{ tb.CreateTransferResult, "TB_CREATE_TRANSFER_RESULT" },
12
+ .{ tb.CreateAccountsResult, "tb_create_accounts_result_t" },
13
+ .{ tb.CreateTransfersResult, "tb_create_transfers_result_t" },
14
+ .{ tb_client.tb_operation_t, "TB_OPERATION" },
15
+ .{ tb_client.tb_packet_status_t, "TB_PACKET_STATUS" },
16
+ .{ tb_client.tb_packet_t, "tb_packet_t" },
17
+ .{ tb_client.tb_packet_list_t, "tb_packet_list_t" },
18
+ .{ tb_client.tb_client_t, "tb_client_t" },
19
+ .{ tb_client.tb_status_t, "TB_STATUS" },
20
+ };
21
+
22
+ fn resolve_c_type(comptime Type: type) []const u8 {
23
+ switch (@typeInfo(Type)) {
24
+ .Array => |info| return resolve_c_type(info.child),
25
+ .Enum => |info| return resolve_c_type(info.tag_type),
26
+ .Struct => return resolve_c_type(std.meta.Int(.unsigned, @bitSizeOf(Type))),
27
+ .Int => |info| {
28
+ std.debug.assert(info.signedness == .unsigned);
29
+ return switch (info.bits) {
30
+ 8 => "uint8_t",
31
+ 16 => "uint16_t",
32
+ 32 => "uint32_t",
33
+ 64 => "uint64_t",
34
+ 128 => "tb_uint128_t",
35
+ else => @compileError("invalid int type"),
36
+ };
37
+ },
38
+ .Optional => |info| switch (@typeInfo(info.child)) {
39
+ .Pointer => return resolve_c_type(info.child),
40
+ else => @compileError("Unsupported optional type: " ++ @typeName(Type)),
41
+ },
42
+ .Pointer => |info| {
43
+ std.debug.assert(info.size != .Slice);
44
+ std.debug.assert(!info.is_allowzero);
45
+
46
+ inline for (type_mappings) |type_mapping| {
47
+ const ZigType = type_mapping[0];
48
+ const c_name = type_mapping[1];
49
+
50
+ if (info.child == ZigType) {
51
+ const prefix = if (@typeInfo(ZigType) == .Struct) "struct " else "";
52
+ return prefix ++ c_name ++ "*";
53
+ }
54
+ }
55
+
56
+ return resolve_c_type(info.child) ++ "*";
57
+ },
58
+ .Void, .Opaque => return "void",
59
+ else => @compileError("Unhandled type: " ++ @typeName(Type)),
60
+ }
61
+ }
62
+
63
+ fn to_uppercase(comptime input: []const u8) []const u8 {
64
+ comptime var output: [input.len]u8 = undefined;
65
+ inline for (output) |*char, i| {
66
+ char.* = input[i];
67
+ char.* -= 32 * @as(u8, @boolToInt(char.* >= 'a' and char.* <= 'z'));
68
+ }
69
+ return &output;
70
+ }
71
+
72
+ fn emit_enum(
73
+ buffer: *std.ArrayList(u8),
74
+ comptime type_info: anytype,
75
+ comptime c_name: []const u8,
76
+ comptime value_fmt: []const u8,
77
+ comptime skip_fields: []const []const u8,
78
+ ) !void {
79
+ var suffix_pos = std.mem.lastIndexOf(u8, c_name, "_").?;
80
+ if (std.mem.count(u8, c_name, "_") == 1) suffix_pos = c_name.len;
81
+
82
+ try buffer.writer().print("typedef enum {s} {{\n", .{c_name});
83
+
84
+ inline for (type_info.fields) |field, i| {
85
+ comptime var skip = false;
86
+ inline for (skip_fields) |sf| {
87
+ skip = skip or comptime std.mem.eql(u8, sf, field.name);
88
+ }
89
+
90
+ if (!skip) {
91
+ try buffer.writer().print(" {s}_{s} = " ++ value_fmt ++ ",\n", .{
92
+ c_name[0..suffix_pos],
93
+ to_uppercase(field.name),
94
+ i,
95
+ });
96
+ }
97
+ }
98
+
99
+ try buffer.writer().print("}} {s};\n\n", .{c_name});
100
+ }
101
+
102
+ fn emit_struct(
103
+ buffer: *std.ArrayList(u8),
104
+ comptime type_info: anytype,
105
+ comptime c_name: []const u8,
106
+ ) !void {
107
+ try buffer.writer().print("typedef struct {s} {{\n", .{c_name});
108
+
109
+ inline for (type_info.fields) |field| {
110
+ try buffer.writer().print(" {s} {s}", .{
111
+ resolve_c_type(field.field_type),
112
+ field.name,
113
+ });
114
+
115
+ switch (@typeInfo(field.field_type)) {
116
+ .Array => |array| try buffer.writer().print("[{d}]", .{array.len}),
117
+ else => {},
118
+ }
119
+
120
+ try buffer.writer().print(";\n", .{});
121
+ }
122
+
123
+ try buffer.writer().print("}} {s};\n\n", .{c_name});
124
+ }
125
+
126
+ pub fn main() !void {
127
+ @setEvalBranchQuota(100_000);
128
+
129
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
130
+ defer arena.deinit();
131
+ const allocator = arena.allocator();
132
+
133
+ var buffer = std.ArrayList(u8).init(allocator);
134
+ try buffer.writer().print(
135
+ \\ //////////////////////////////////////////////////////////
136
+ \\ // This file was auto-generated by tb_client_header.zig //
137
+ \\ // Do not manually modify. //
138
+ \\ //////////////////////////////////////////////////////////
139
+ \\
140
+ \\#ifndef TB_CLIENT_H
141
+ \\#define TB_CLIENT_H
142
+ \\
143
+ \\#include <stddef.h>
144
+ \\#include <stdint.h>
145
+ \\#include <stdbool.h>
146
+ \\
147
+ \\typedef __uint128_t tb_uint128_t;
148
+ \\
149
+ \\
150
+ , .{});
151
+
152
+ // Emit C type declarations.
153
+ inline for (type_mappings) |type_mapping| {
154
+ const ZigType = type_mapping[0];
155
+ const c_name = type_mapping[1];
156
+
157
+ switch (@typeInfo(ZigType)) {
158
+ .Struct => |info| switch (info.layout) {
159
+ .Auto => @compileError("Invalid C struct type: " ++ @typeName(ZigType)),
160
+ .Packed => try emit_enum(&buffer, info, c_name, "1 << {d}", &.{"padding"}),
161
+ .Extern => try emit_struct(&buffer, info, c_name),
162
+ },
163
+ .Enum => |info| {
164
+ comptime var skip: []const []const u8 = &.{};
165
+ if (ZigType == tb_client.tb_operation_t) {
166
+ skip = &.{ "reserved", "root", "register" };
167
+ }
168
+
169
+ try emit_enum(&buffer, info, c_name, "{d}", skip);
170
+ },
171
+ else => try buffer.writer().print("typedef {s} {s}; \n\n", .{
172
+ resolve_c_type(ZigType),
173
+ c_name,
174
+ }),
175
+ }
176
+ }
177
+
178
+ // Emit C function declarations.
179
+ // TODO: use `std.meta.declaractions` and generate with pub + export functions.
180
+ // Zig 0.9.1 has `decl.data.Fn.arg_names` but it's currently/incorrectly a zero-sized slice.
181
+ try buffer.writer().print(
182
+ \\TB_STATUS tb_client_init(
183
+ \\ tb_client_t* out_client,
184
+ \\ struct tb_packet_list_t* out_packets,
185
+ \\ uint32_t cluster_id,
186
+ \\ const char* address_ptr,
187
+ \\ uint32_t address_len,
188
+ \\ uint32_t packets_count,
189
+ \\ uintptr_t on_completion_ctx,
190
+ \\ void (*on_completion_fn)(uintptr_t, tb_client_t, tb_packet_t*, const uint8_t*, uint32_t)
191
+ \\);
192
+ \\
193
+ \\TB_STATUS tb_client_init_echo(
194
+ \\ tb_client_t* out_client,
195
+ \\ struct tb_packet_list_t* out_packets,
196
+ \\ uint32_t cluster_id,
197
+ \\ const char* address_ptr,
198
+ \\ uint32_t address_len,
199
+ \\ uint32_t packets_count,
200
+ \\ uintptr_t on_completion_ctx,
201
+ \\ void (*on_completion_fn)(uintptr_t, tb_client_t, tb_packet_t*, const uint8_t*, uint32_t)
202
+ \\);
203
+ \\
204
+ \\void tb_client_submit(
205
+ \\ tb_client_t client,
206
+ \\ struct tb_packet_list_t* packets
207
+ \\);
208
+ \\
209
+ \\void tb_client_deinit(
210
+ \\ tb_client_t client
211
+ \\);
212
+ \\
213
+ \\
214
+ , .{});
215
+
216
+ try buffer.writer().print("#endif // TB_CLIENT_H\n\n", .{});
217
+ try std.fs.cwd().writeFile("src/c/tb_client.h", buffer.items);
218
+ }
@@ -6,7 +6,7 @@ const testing = std.testing;
6
6
  const c = @cImport(@cInclude("tb_client.h"));
7
7
 
8
8
  const util = @import("../util.zig");
9
- const config = @import("../config.zig");
9
+ const constants = @import("../constants.zig");
10
10
  const Packet = @import("tb_client/packet.zig").Packet;
11
11
 
12
12
  const Mutex = std.Thread.Mutex;
@@ -91,10 +91,10 @@ const Completion = struct {
91
91
  // 3. the data marshaling is correct, and exactly the same data sent was received back.
92
92
  test "c_client echo" {
93
93
  // Using the create_accounts operation for this test.
94
- const RequestContext = RequestContextType(config.message_body_size_max);
95
- const create_accounts_operation: u8 = c.TB_OP_CREATE_ACCOUNTS;
94
+ const RequestContext = RequestContextType(constants.message_body_size_max);
95
+ const create_accounts_operation: u8 = c.TB_OPERATION_CREATE_ACCOUNTS;
96
96
  const event_size = @sizeOf(c.tb_account_t);
97
- const event_request_max = @divFloor(config.message_body_size_max, event_size);
97
+ const event_request_max = @divFloor(constants.message_body_size_max, event_size);
98
98
 
99
99
  // Initializing an echo client for testing purposes.
100
100
  // We ensure that the retry mechanism is being tested
@@ -103,7 +103,7 @@ test "c_client echo" {
103
103
  var tb_packet_list: c.tb_packet_list_t = undefined;
104
104
  const cluster_id = 0;
105
105
  const address = "3000";
106
- const packets_count: u32 = config.client_request_queue_max * 2;
106
+ const packets_count: u32 = constants.client_request_queue_max * 2;
107
107
  const tb_context: usize = 42;
108
108
  const result = c.tb_client_init_echo(
109
109
  &tb_client,
@@ -221,7 +221,7 @@ test "c_client tb_status" {
221
221
  // More addresses thant "replicas_max" should return "TB_STATUS_ADDRESS_LIMIT_EXCEEDED":
222
222
  try assert_status(
223
223
  1,
224
- ("3000," ** config.replicas_max) ++ "3001",
224
+ ("3000," ** constants.replicas_max) ++ "3001",
225
225
  c.TB_STATUS_ADDRESS_LIMIT_EXCEEDED,
226
226
  );
227
227
 
@@ -234,7 +234,7 @@ test "c_client tb_status" {
234
234
 
235
235
  // Asserts the validation rules associated with the "TB_PACKET_STATUS" enum.
236
236
  test "c_client tb_packet_status" {
237
- const RequestContext = RequestContextType(config.message_body_size_max);
237
+ const RequestContext = RequestContextType(constants.message_body_size_max);
238
238
 
239
239
  var tb_client: c.tb_client_t = undefined;
240
240
  var tb_packet_list: c.tb_packet_list_t = undefined;
@@ -299,12 +299,12 @@ test "c_client tb_packet_status" {
299
299
 
300
300
  var packet_list = @ptrCast(*Packet.List, &tb_packet_list);
301
301
 
302
- // Messages larger than config.message_body_size_max should return "too_much_data":
302
+ // Messages larger than constants.message_body_size_max should return "too_much_data":
303
303
  try assert_result(
304
304
  tb_client,
305
305
  packet_list,
306
- c.TB_OP_CREATE_TRANSFERS,
307
- config.message_body_size_max + @sizeOf(c.tb_transfer_t),
306
+ c.TB_OPERATION_CREATE_TRANSFERS,
307
+ constants.message_body_size_max + @sizeOf(c.tb_transfer_t),
308
308
  c.TB_PACKET_TOO_MUCH_DATA,
309
309
  );
310
310
 
@@ -343,28 +343,28 @@ test "c_client tb_packet_status" {
343
343
  try assert_result(
344
344
  tb_client,
345
345
  packet_list,
346
- c.TB_OP_CREATE_ACCOUNTS,
346
+ c.TB_OPERATION_CREATE_ACCOUNTS,
347
347
  0,
348
348
  c.TB_PACKET_INVALID_DATA_SIZE,
349
349
  );
350
350
  try assert_result(
351
351
  tb_client,
352
352
  packet_list,
353
- c.TB_OP_CREATE_TRANSFERS,
353
+ c.TB_OPERATION_CREATE_TRANSFERS,
354
354
  @sizeOf(c.tb_transfer_t) - 1,
355
355
  c.TB_PACKET_INVALID_DATA_SIZE,
356
356
  );
357
357
  try assert_result(
358
358
  tb_client,
359
359
  packet_list,
360
- c.TB_OP_LOOKUP_TRANSFERS,
360
+ c.TB_OPERATION_LOOKUP_TRANSFERS,
361
361
  @sizeOf(u128) + 1,
362
362
  c.TB_PACKET_INVALID_DATA_SIZE,
363
363
  );
364
364
  try assert_result(
365
365
  tb_client,
366
366
  packet_list,
367
- c.TB_OP_LOOKUP_ACCOUNTS,
367
+ c.TB_OPERATION_LOOKUP_ACCOUNTS,
368
368
  @sizeOf(u128) * 2.5,
369
369
  c.TB_PACKET_INVALID_DATA_SIZE,
370
370
  );
@@ -7,7 +7,7 @@ const meta = std.meta;
7
7
  const net = std.net;
8
8
  const os = std.os;
9
9
 
10
- const config = @import("config.zig");
10
+ const constants = @import("constants.zig");
11
11
  const tigerbeetle = @import("tigerbeetle.zig");
12
12
  const vsr = @import("vsr.zig");
13
13
  const IO = @import("io.zig").IO;
@@ -72,8 +72,8 @@ const usage = fmt.comptimePrint(
72
72
  \\ tigerbeetle version --verbose
73
73
  \\
74
74
  , .{
75
- .default_address = config.address,
76
- .default_port = config.port,
75
+ .default_address = constants.address,
76
+ .default_port = constants.port,
77
77
  });
78
78
 
79
79
  pub const Command = union(enum) {
@@ -206,17 +206,17 @@ pub fn parse_args(allocator: std.mem.Allocator) !Command {
206
206
  .cache_accounts = parse_size_to_count(
207
207
  tigerbeetle.Account,
208
208
  cache_accounts,
209
- config.cache_accounts_max,
209
+ constants.cache_accounts_max,
210
210
  ),
211
211
  .cache_transfers = parse_size_to_count(
212
212
  tigerbeetle.Transfer,
213
213
  cache_transfers,
214
- config.cache_transfers_max,
214
+ constants.cache_transfers_max,
215
215
  ),
216
216
  .cache_transfers_posted = parse_size_to_count(
217
217
  u256, // TODO(#264): Use actual type here, once exposed.
218
218
  cache_transfers_posted,
219
- config.cache_transfers_posted_max,
219
+ constants.cache_transfers_posted_max,
220
220
  ),
221
221
  .path = path orelse fatal("required: <path>", .{}),
222
222
  },
@@ -255,11 +255,11 @@ fn parse_cluster(raw_cluster: []const u8) u32 {
255
255
 
256
256
  /// Parse and allocate the addresses returning a slice into that array.
257
257
  fn parse_addresses(allocator: std.mem.Allocator, raw_addresses: []const u8) []net.Address {
258
- return vsr.parse_addresses(allocator, raw_addresses, config.replicas_max) catch |err| switch (err) {
258
+ return vsr.parse_addresses(allocator, raw_addresses, constants.replicas_max) catch |err| switch (err) {
259
259
  error.AddressHasTrailingComma => fatal("--addresses: invalid trailing comma", .{}),
260
260
  error.AddressLimitExceeded => {
261
261
  fatal("--addresses: too many addresses, at most {d} are allowed", .{
262
- config.replicas_max,
262
+ constants.replicas_max,
263
263
  });
264
264
  },
265
265
  error.AddressHasMoreThanOneColon => {
@@ -351,7 +351,7 @@ fn parse_size_to_count(comptime T: type, string_opt: ?[]const u8, comptime defau
351
351
  const count = math.cast(u32, count_u64) catch |err| switch (err) {
352
352
  error.Overflow => fatal("size value is too large: '{s}'", .{string}),
353
353
  };
354
- if (count < 2048) fatal("size value is too small: '{s}'", .{string});
354
+ if (count > 0 and count < 2048) fatal("size value is too small: '{s}'", .{string});
355
355
  assert(count * @sizeOf(T) <= byte_size);
356
356
 
357
357
  result = count;
@@ -359,14 +359,14 @@ fn parse_size_to_count(comptime T: type, string_opt: ?[]const u8, comptime defau
359
359
 
360
360
  // SetAssociativeCache requires a power-of-two cardinality and a minimal
361
361
  // size.
362
- assert(result >= 2048);
363
- assert(math.isPowerOfTwo(result));
362
+ assert(result == 0 or result >= 2048);
363
+ assert(result == 0 or math.isPowerOfTwo(result));
364
364
 
365
365
  return result;
366
366
  }
367
367
 
368
368
  fn parse_replica(raw_replica: []const u8) u8 {
369
- comptime assert(config.replicas_max <= std.math.maxInt(u8));
369
+ comptime assert(constants.replicas_max <= std.math.maxInt(u8));
370
370
  const replica = fmt.parseUnsigned(u8, raw_replica, 10) catch |err| switch (err) {
371
371
  error.Overflow => fatal("--replica: value exceeds an 8-bit unsigned integer", .{}),
372
372
  error.InvalidCharacter => fatal("--replica: value contains an invalid character", .{}),