tigerbeetle 0.0.38 → 0.0.39

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.
@@ -0,0 +1,115 @@
1
+ const std = @import("std");
2
+ const builtin = @import("builtin");
3
+ const stdx = @import("stdx.zig");
4
+ const Allocator = std.mem.Allocator;
5
+
6
+ const log = std.log.scoped(.allocator);
7
+
8
+ const page_allocator_vtable = std.heap.page_allocator.vtable;
9
+
10
+ /// Like `std.heap.page_allocator`, but on Linux applies `MADV_HUGEPAGE` to
11
+ /// allocated regions so that the kernel may back them with 2 MiB transparent
12
+ /// huge pages, reducing TLB pressure for large allocations.
13
+ ///
14
+ /// Only `alloc` is intercepted. `resize` and `remap` inherit the VMA flags
15
+ /// (including `VM_HUGEPAGE`) set on the original mapping, so they need no
16
+ /// additional `madvise` call.
17
+ ///
18
+ /// On non-Linux targets this is identical to `std.heap.page_allocator`.
19
+ pub const huge_page_allocator: Allocator = .{
20
+ .ptr = std.heap.page_allocator.ptr,
21
+ .vtable = if (builtin.target.os.tag == .linux) &vtable else page_allocator_vtable,
22
+ };
23
+
24
+ const vtable: Allocator.VTable = .{
25
+ .alloc = alloc,
26
+ .resize = page_allocator_vtable.resize,
27
+ .remap = page_allocator_vtable.remap,
28
+ .free = page_allocator_vtable.free,
29
+ };
30
+
31
+ fn alloc(context: *anyopaque, n: usize, alignment: std.mem.Alignment, ra: usize) ?[*]u8 {
32
+ const ptr = page_allocator_vtable.alloc(context, n, alignment, ra) orelse return null;
33
+ // This is just a hint, so if it fails we can safely ignore it.
34
+ std.posix.madvise(@alignCast(ptr), n, std.posix.MADV.HUGEPAGE) catch {
35
+ log.warn("Transparent Huge Pages (THP) are disabled.", .{});
36
+ };
37
+ return ptr;
38
+ }
39
+
40
+ const testing = std.testing;
41
+ const assert = std.debug.assert;
42
+
43
+ /// Checks /proc/self/smaps for the "hg" VmFlag on the mapping containing `ptr`.
44
+ fn verify_address_is_huge_page(ptr: [*]const u8) !bool {
45
+ assert(builtin.target.os.tag == .linux);
46
+ const addr = @intFromPtr(ptr);
47
+
48
+ var file = try std.fs.openFileAbsolute("/proc/self/smaps", .{});
49
+ defer file.close();
50
+
51
+ const content = try file.readToEndAlloc(testing.allocator, 10 * 1024 * 1024);
52
+ defer testing.allocator.free(content);
53
+
54
+ var lines = std.mem.splitScalar(u8, content, '\n');
55
+
56
+ // Find the mapping header that contains our address.
57
+ while (lines.next()) |line| {
58
+ if (parse_mapping_range(line)) |range| {
59
+ if (addr >= range.min and addr < range.max) {
60
+ // Scan subsequent lines for VmFlags within this mapping.
61
+ while (lines.next()) |detail| {
62
+ if (stdx.cut_prefix(detail, "VmFlags:")) |rest| {
63
+ return stdx.cut(rest, "hg") != null;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ return false;
70
+ }
71
+
72
+ fn parse_mapping_range(line: []const u8) ?struct { min: u64, max: u64 } {
73
+ const addr_range, _ = stdx.cut(line, " ") orelse return null;
74
+ const addr_hex_min, const addr_hex_max = stdx.cut(addr_range, "-") orelse return null;
75
+ const addr_min = std.fmt.parseUnsigned(u64, addr_hex_min, 16) catch return null;
76
+ const addr_max = std.fmt.parseUnsigned(u64, addr_hex_max, 16) catch return null;
77
+ return .{ .min = addr_min, .max = addr_max };
78
+ }
79
+
80
+ test "huge_page_allocator: basic alloc and free" {
81
+ const slice = try huge_page_allocator.alloc(u8, 4096);
82
+ defer huge_page_allocator.free(slice);
83
+
84
+ @memset(slice, 0xab);
85
+ try testing.expectEqual(@as(u8, 0xab), slice[0]);
86
+ }
87
+
88
+ test "huge_page_allocator: large THP-eligible allocation" {
89
+ // 4 MiB — large enough for THP promotion on Linux.
90
+ const size = 4 * 1024 * 1024;
91
+ const slice = try huge_page_allocator.alloc(u8, size);
92
+ defer huge_page_allocator.free(slice);
93
+
94
+ @memset(slice, 0xcd);
95
+ try testing.expectEqual(@as(u8, 0xcd), slice[size - 1]);
96
+
97
+ if (builtin.target.os.tag == .linux) {
98
+ // Verify that MADV_HUGEPAGE was applied by checking VmFlags in /proc/self/smaps.
99
+ // The "hg" flag means the process requested hugepages via madvise — this is
100
+ // deterministic regardless of whether the kernel actually promoted the pages.
101
+ try testing.expect(try verify_address_is_huge_page(slice.ptr));
102
+ }
103
+ }
104
+
105
+ test "huge_page_allocator: as ArenaAllocator backing" {
106
+ var arena = std.heap.ArenaAllocator.init(huge_page_allocator);
107
+ defer arena.deinit();
108
+
109
+ const alloc1 = try arena.allocator().alloc(u8, 1024);
110
+ const alloc2 = try arena.allocator().alloc(u8, 2048);
111
+ @memset(alloc1, 1);
112
+ @memset(alloc2, 2);
113
+ try testing.expectEqual(@as(u8, 1), alloc1[0]);
114
+ try testing.expectEqual(@as(u8, 2), alloc2[0]);
115
+ }
@@ -16,6 +16,8 @@ pub const Snap = @import("testing/snaptest.zig").Snap;
16
16
  pub const ZipfianGenerator = @import("zipfian.zig").ZipfianGenerator;
17
17
  pub const ZipfianShuffled = @import("zipfian.zig").ZipfianShuffled;
18
18
 
19
+ pub const huge_page_allocator = @import("huge_page_allocator.zig").huge_page_allocator;
20
+
19
21
  pub const aegis = @import("vendored/aegis.zig");
20
22
  pub const dbg = @import("debug.zig").dbg;
21
23
  pub const flags = @import("flags.zig").parse;
@@ -1144,6 +1146,7 @@ pub fn term_from_status(status: u32) std.process.Child.Term {
1144
1146
  }
1145
1147
 
1146
1148
  comptime {
1149
+ _ = @import("huge_page_allocator.zig");
1147
1150
  _ = @import("vendored/aegis.zig");
1148
1151
  _ = @import("bit_set.zig");
1149
1152
  _ = @import("bounded_array.zig");
@@ -5,10 +5,10 @@ pub const vsr = @import("../../constants.zig");
5
5
 
6
6
  pub const vortex = struct {
7
7
  pub const cluster_id = 0;
8
- pub const connections_count_max = @divFloor(
9
- constants.vsr.clients_max,
10
- constants.vsr.replicas_max,
11
- );
8
+ // Maximum number of connections *per replica*.
9
+ // -1 since replicas don't connect to themselves.
10
+ // +1 for the single driver/client.
11
+ pub const connections_count_max = (vsr.replicas_max - 1) + 1;
12
12
 
13
13
  // We allow the cluster to not make progress processing requests for this amount of time.
14
14
  // After that it's considered a test failure.
@@ -34,19 +34,17 @@ const Faults = struct {
34
34
  };
35
35
 
36
36
  delay: ?Delay = null,
37
- lose: ?Ratio = null,
38
37
  corrupt: ?Ratio = null,
39
38
 
40
39
  // Others not implemented: duplication, reordering, rate
41
40
 
42
41
  pub fn heal(faults: *Faults) void {
43
42
  faults.delay = null;
44
- faults.lose = null;
45
43
  faults.corrupt = null;
46
44
  }
47
45
 
48
46
  pub fn is_healed(faults: *const Faults) bool {
49
- return faults.delay == null and faults.lose == null and faults.corrupt == null;
47
+ return faults.delay == null and faults.corrupt == null;
50
48
  }
51
49
  };
52
50
 
@@ -70,11 +68,13 @@ const Pipe = struct {
70
68
  ) void {
71
69
  assert(pipe.connection.state == .proxying);
72
70
  assert(pipe.status == .idle);
71
+ assert(pipe.input == null);
72
+ assert(pipe.output == null);
73
+ assert(pipe.recv_size == 0);
74
+ assert(pipe.send_size == 0);
73
75
 
74
76
  pipe.input = input;
75
77
  pipe.output = output;
76
- pipe.recv_size = 0;
77
- pipe.send_size = 0;
78
78
 
79
79
  // Kick off the recv/send loop.
80
80
  pipe.recv();
@@ -94,16 +94,19 @@ const Pipe = struct {
94
94
  pipe.connection.io.recv(
95
95
  *Pipe,
96
96
  pipe,
97
- on_recv,
97
+ recv_callback,
98
98
  &pipe.recv_completion,
99
99
  pipe.input.?,
100
100
  pipe.buffer[0..],
101
101
  );
102
102
  }
103
103
 
104
- fn on_recv(pipe: *Pipe, _: *IO.Completion, result: IO.RecvError!usize) void {
104
+ fn recv_callback(pipe: *Pipe, _: *IO.Completion, result: IO.RecvError!usize) void {
105
105
  assert(pipe.recv_size == 0);
106
106
  assert(pipe.send_size == 0);
107
+ assert(pipe.connection.state != .free);
108
+ assert(pipe.connection.state != .accepting);
109
+ assert(pipe.connection.state != .connecting);
107
110
 
108
111
  assert(pipe.status == .recv);
109
112
  pipe.status = .idle;
@@ -125,18 +128,6 @@ const Pipe = struct {
125
128
  return pipe.connection.try_close();
126
129
  }
127
130
 
128
- if (pipe.connection.network.faults.lose) |lose| {
129
- if (pipe.connection.network.prng.chance(lose)) {
130
- log.debug("losing {d} bytes ({d},{d})", .{
131
- pipe.recv_size,
132
- pipe.connection.replica_index,
133
- pipe.connection.connection_index,
134
- });
135
- pipe.recv();
136
- return;
137
- }
138
- }
139
-
140
131
  if (pipe.connection.network.faults.corrupt) |corrupt| {
141
132
  if (pipe.connection.network.prng.chance(corrupt)) {
142
133
  switch (pipe.connection.network.prng.enum_uniform(enum { shuffle, zero })) {
@@ -180,14 +171,23 @@ const Pipe = struct {
180
171
 
181
172
  assert(pipe.status == .idle);
182
173
  pipe.status = .send_timeout;
183
- pipe.io.timeout(*Pipe, pipe, on_timeout, &pipe.send_completion, timeout_duration_ns);
174
+ pipe.io.timeout(
175
+ *Pipe,
176
+ pipe,
177
+ timeout_callback,
178
+ &pipe.send_completion,
179
+ timeout_duration_ns,
180
+ );
184
181
  } else {
185
182
  pipe.send();
186
183
  }
187
184
  }
188
185
 
189
- fn on_timeout(pipe: *Pipe, _: *IO.Completion, result: IO.TimeoutError!void) void {
186
+ fn timeout_callback(pipe: *Pipe, _: *IO.Completion, result: IO.TimeoutError!void) void {
190
187
  assert(pipe.status == .send_timeout);
188
+ assert(pipe.connection.state != .free);
189
+ assert(pipe.connection.state != .accepting);
190
+ assert(pipe.connection.state != .connecting);
191
191
  pipe.status = .idle;
192
192
 
193
193
  if (pipe.connection.state != .proxying) return;
@@ -206,15 +206,18 @@ const Pipe = struct {
206
206
  pipe.io.send(
207
207
  *Pipe,
208
208
  pipe,
209
- on_send,
209
+ send_callback,
210
210
  &pipe.send_completion,
211
211
  pipe.output.?,
212
212
  pipe.buffer[pipe.send_size..pipe.recv_size],
213
213
  );
214
214
  }
215
215
 
216
- fn on_send(pipe: *Pipe, _: *IO.Completion, result: IO.SendError!usize) void {
216
+ fn send_callback(pipe: *Pipe, _: *IO.Completion, result: IO.SendError!usize) void {
217
217
  assert(pipe.send_size < pipe.recv_size);
218
+ assert(pipe.connection.state != .free);
219
+ assert(pipe.connection.state != .accepting);
220
+ assert(pipe.connection.state != .connecting);
218
221
 
219
222
  assert(pipe.status == .send);
220
223
  pipe.status = .idle;
@@ -268,20 +271,14 @@ const Connection = struct {
268
271
  connect_completion: IO.Completion = undefined,
269
272
  close_completion: IO.Completion = undefined,
270
273
 
271
- fn on_accept(
274
+ fn accept_callback(
272
275
  connection: *Connection,
273
276
  _: *IO.Completion,
274
277
  result: IO.AcceptError!std.posix.socket_t,
275
278
  ) void {
276
279
  assert(connection.state == .accepting);
277
- defer assert(connection.state == .connecting);
278
-
279
280
  assert(connection.origin_fd == null);
280
- defer assert(connection.origin_fd != null);
281
-
282
281
  assert(connection.remote_fd == null);
283
- defer assert(connection.remote_fd != null);
284
-
285
282
  assert(connection.remote_address != null);
286
283
 
287
284
  const fd = result catch |err| {
@@ -305,21 +302,21 @@ const Connection = struct {
305
302
  });
306
303
  return connection.try_close();
307
304
  };
308
- connection.remote_fd = remote_fd;
309
305
 
306
+ connection.remote_fd = remote_fd;
310
307
  connection.state = .connecting;
311
308
 
312
309
  connection.io.connect(
313
310
  *Connection,
314
311
  connection,
315
- Connection.on_connect,
312
+ Connection.connect_callback,
316
313
  &connection.connect_completion,
317
314
  connection.remote_fd.?,
318
315
  connection.remote_address.?,
319
316
  );
320
317
  }
321
318
 
322
- fn on_connect(
319
+ fn connect_callback(
323
320
  connection: *Connection,
324
321
  _: *IO.Completion,
325
322
  result: IO.ConnectError!void,
@@ -357,41 +354,37 @@ const Connection = struct {
357
354
  connection.connection_index,
358
355
  });
359
356
  connection.state = .closing;
360
- std.posix.shutdown(connection.origin_fd.?, .both) catch |err| {
361
- switch (err) {
362
- error.SocketNotConnected => {},
363
- else => log.warn("shutdown origin_fd ({d},{d}) failed: {}", .{
364
- connection.replica_index, connection.connection_index, err,
365
- }),
366
- }
357
+ std.posix.shutdown(connection.origin_fd.?, .both) catch |err| switch (err) {
358
+ error.SocketNotConnected => {},
359
+ else => log.warn("shutdown origin_fd ({d},{d}) failed: {}", .{
360
+ connection.replica_index, connection.connection_index, err,
361
+ }),
367
362
  };
368
- std.posix.shutdown(connection.remote_fd.?, .both) catch |err| {
369
- switch (err) {
370
- error.SocketNotConnected => {},
371
- else => log.warn("shutdown remote_fd ({d},{d}) failed: {}", .{
372
- connection.replica_index, connection.connection_index, err,
373
- }),
374
- }
363
+ std.posix.shutdown(connection.remote_fd.?, .both) catch |err| switch (err) {
364
+ error.SocketNotConnected => {},
365
+ else => log.warn("shutdown remote_fd ({d},{d}) failed: {}", .{
366
+ connection.replica_index, connection.connection_index, err,
367
+ }),
375
368
  };
376
369
  }
377
370
 
378
- if (!has_inflight_operations) {
371
+ if (has_inflight_operations) {
372
+ // Network.tick() will keep calling try_close().
373
+ assert(connection.state == .closing);
374
+ } else {
379
375
  // Kick off the close sequence.
380
376
  connection.state = .closing_origin;
381
377
  connection.io.close(
382
378
  *Connection,
383
379
  connection,
384
- on_close_origin,
380
+ close_origin_callback,
385
381
  &connection.close_completion,
386
382
  connection.origin_fd.?,
387
383
  );
388
- return;
389
384
  }
390
-
391
- assert(connection.state == .closing);
392
385
  }
393
386
 
394
- fn on_close_origin(
387
+ fn close_origin_callback(
395
388
  connection: *Connection,
396
389
  _: *IO.Completion,
397
390
  result: IO.CloseError!void,
@@ -403,7 +396,7 @@ const Connection = struct {
403
396
  defer assert(connection.origin_fd == null);
404
397
 
405
398
  result catch |err| {
406
- log.warn("on_close_origin ({d},{d}) error: {any}", .{
399
+ log.warn("close_origin_callback ({d},{d}) error: {any}", .{
407
400
  connection.replica_index,
408
401
  connection.connection_index,
409
402
  err,
@@ -415,13 +408,13 @@ const Connection = struct {
415
408
  connection.io.close(
416
409
  *Connection,
417
410
  connection,
418
- on_close_remote,
411
+ close_remote_callback,
419
412
  &connection.close_completion,
420
413
  connection.remote_fd.?,
421
414
  );
422
415
  }
423
416
 
424
- fn on_close_remote(
417
+ fn close_remote_callback(
425
418
  connection: *Connection,
426
419
  _: *IO.Completion,
427
420
  result: IO.CloseError!void,
@@ -433,19 +426,22 @@ const Connection = struct {
433
426
  defer assert(connection.remote_fd == null);
434
427
 
435
428
  result catch |err| {
436
- log.warn("on_close_remote ({d},{d}) error: {any}", .{
429
+ log.warn("close_remote_callback ({d},{d}) error: {any}", .{
437
430
  connection.replica_index,
438
431
  connection.connection_index,
439
432
  err,
440
433
  });
441
434
  };
442
435
 
443
- log.debug("on_close_remote ({d},{d}): marking connection as free", .{
436
+ log.debug("close_remote_callback ({d},{d}): marking connection as free", .{
444
437
  connection.replica_index,
445
438
  connection.connection_index,
446
439
  });
447
440
  connection.state = .free;
448
441
  connection.remote_fd = null;
442
+ connection.remote_address = null;
443
+ connection.origin_to_remote_pipe = .{ .io = connection.io, .connection = connection };
444
+ connection.remote_to_origin_pipe = .{ .io = connection.io, .connection = connection };
449
445
  }
450
446
  };
451
447
 
@@ -543,8 +539,10 @@ pub const Network = struct {
543
539
  }
544
540
 
545
541
  pub fn tick(network: *Network) void {
546
- for (network.proxies) |*proxy| {
542
+ for (network.proxies, 0..) |*proxy, replica_index| {
547
543
  for (&proxy.connections) |*connection| {
544
+ assert(connection.replica_index == replica_index);
545
+
548
546
  if (connection.state == .closing) {
549
547
  connection.try_close();
550
548
  continue;
@@ -555,6 +553,9 @@ pub const Network = struct {
555
553
  if (connection.state == .free) {
556
554
  assert(connection.origin_to_remote_pipe.status == .idle);
557
555
  assert(connection.remote_to_origin_pipe.status == .idle);
556
+ assert(connection.origin_fd == null);
557
+ assert(connection.remote_fd == null);
558
+ assert(connection.remote_address == null);
558
559
 
559
560
  log.debug("accepting ({d},{d})", .{
560
561
  connection.replica_index,
@@ -563,13 +564,11 @@ pub const Network = struct {
563
564
 
564
565
  connection.state = .accepting;
565
566
  connection.remote_address = proxy.remote_address;
566
- connection.origin_fd = null;
567
- connection.remote_fd = null;
568
567
 
569
568
  network.io.accept(
570
569
  *Connection,
571
570
  connection,
572
- Connection.on_accept,
571
+ Connection.accept_callback,
573
572
  &connection.accept_completion,
574
573
  proxy.accept_fd,
575
574
  );
@@ -296,15 +296,13 @@ const Supervisor = struct {
296
296
  const too_slow_request = supervisor.workload.find_slow_request_since(start_ns);
297
297
 
298
298
  if (no_finished_requests) {
299
- log.err("liveness check: no finished requests after {d} seconds", .{
299
+ fatal(.liveness, "liveness check: no finished requests after {d} seconds", .{
300
300
  constants.vortex.liveness_requirement_seconds,
301
301
  });
302
- return error.TestFailed;
303
302
  }
304
303
 
305
304
  if (too_slow_request) |_| {
306
- log.err("liveness check: too slow request", .{});
307
- return error.TestFailed;
305
+ fatal(.request_slow, "liveness check: too slow request", .{});
308
306
  }
309
307
  }
310
308
 
@@ -335,7 +333,6 @@ const Supervisor = struct {
335
333
  replica_pause,
336
334
  replica_resume,
337
335
  network_delay,
338
- network_lose,
339
336
  network_corrupt,
340
337
  network_heal,
341
338
  quiesce,
@@ -348,7 +345,6 @@ const Supervisor = struct {
348
345
  .replica_pause = if (running_replicas.len > 0) 3 else 0,
349
346
  .replica_resume = if (paused_replicas.len > 0) 10 else 0,
350
347
  .network_delay = if (supervisor.network.faults.delay == null) 3 else 0,
351
- .network_lose = if (supervisor.network.faults.lose == null) 3 else 0,
352
348
  .network_corrupt = if (supervisor.network.faults.corrupt == null) 3 else 0,
353
349
  .network_heal = if (!supervisor.network.faults.is_healed()) 10 else 0,
354
350
  .quiesce = if (faulty_replica_count > 0 or
@@ -388,11 +384,6 @@ const Supervisor = struct {
388
384
  };
389
385
  log.info("injecting network delays: {any}", .{supervisor.network.faults});
390
386
  },
391
- .network_lose => {
392
- supervisor.network.faults.lose =
393
- ratio(supervisor.prng.range_inclusive(u8, 1, 10), 100);
394
- log.info("injecting network loss: {any}", .{supervisor.network.faults});
395
- },
396
387
  .network_corrupt => {
397
388
  supervisor.network.faults.corrupt =
398
389
  ratio(supervisor.prng.range_inclusive(u8, 1, 10), 100);
@@ -440,15 +431,18 @@ const Supervisor = struct {
440
431
  // success or failure.
441
432
  std.posix.exit(@intCast(128 + term.Signal));
442
433
  } else {
443
- return error.TestFailed;
434
+ fatal(.replica_exit_result, "replica exited with: {}", .{term});
444
435
  }
445
436
  }
446
437
  }
447
438
  }
448
439
 
449
440
  if (supervisor.workload.process.wait_nonblocking()) |code| {
450
- log.err("workload terminated by itself: code={}", .{code});
451
- return error.TestFailed;
441
+ fatal(
442
+ .workload_exit_early,
443
+ "workload terminated by itself: code={}",
444
+ .{code},
445
+ );
452
446
  }
453
447
  } else blk: {
454
448
  log.info("terminating workload due to max duration", .{});
@@ -460,14 +454,20 @@ const Supervisor = struct {
460
454
  switch (signal) {
461
455
  std.posix.SIG.KILL => log.info("workload terminated as requested", .{}),
462
456
  else => {
463
- log.err("workload exited unexpectedly with signal {d}", .{signal});
464
- return error.TestFailed;
457
+ fatal(
458
+ .workload_exit_result,
459
+ "workload exited unexpectedly with signal {d}",
460
+ .{signal},
461
+ );
465
462
  },
466
463
  }
467
464
  },
468
465
  else => {
469
- log.err("unexpected workload result: {any}", .{workload_result});
470
- return error.TestFailed;
466
+ fatal(
467
+ .workload_exit_result,
468
+ "unexpected workload result: {any}",
469
+ .{workload_result},
470
+ );
471
471
  },
472
472
  }
473
473
  }
@@ -724,8 +724,7 @@ const Workload = struct {
724
724
  if (workload.process.state != .running) return;
725
725
 
726
726
  const count = result catch |err| {
727
- log.err("couldn't read from workload stdout: {}", .{err});
728
- return;
727
+ fatal(.workload_read_error, "couldn't read from workload stdout: {}", .{err});
729
728
  };
730
729
 
731
730
  workload.read_progress += count;
@@ -764,3 +763,23 @@ const Workload = struct {
764
763
  return null;
765
764
  }
766
765
  };
766
+
767
+ const FatalReason = enum(u8) {
768
+ workload_exit_early = 10,
769
+ workload_exit_result = 11,
770
+ workload_read_error = 12,
771
+ replica_exit_result = 13,
772
+ liveness = 14,
773
+ request_slow = 15,
774
+
775
+ pub fn exit_status(reason: FatalReason) u8 {
776
+ return @intFromEnum(reason);
777
+ }
778
+ };
779
+
780
+ fn fatal(reason: FatalReason, comptime fmt: []const u8, args: anytype) noreturn {
781
+ log.err(fmt, args);
782
+ const status = reason.exit_status();
783
+ assert(status != 0);
784
+ std.process.exit(status);
785
+ }
@@ -1220,6 +1220,7 @@ const DeadFilesDetector = struct {
1220
1220
  "build_multiversion.zig",
1221
1221
  "build.zig",
1222
1222
  "dotnet_bindings.zig",
1223
+ "fetch.zig",
1223
1224
  "file_checker.zig",
1224
1225
  "fuzz_tests.zig",
1225
1226
  "go_bindings.zig",
@@ -1232,10 +1233,10 @@ const DeadFilesDetector = struct {
1232
1233
  "node.zig",
1233
1234
  "page_writer.zig",
1234
1235
  "python_bindings.zig",
1236
+ "rust_bindings.zig",
1235
1237
  "scripts.zig",
1236
1238
  "search_index_writer.zig",
1237
1239
  "service_worker_writer.zig",
1238
- "rust_bindings.zig",
1239
1240
  "single_page_writer.zig",
1240
1241
  "tb_client_header.zig",
1241
1242
  "unit_tests.zig",
@@ -63,7 +63,7 @@ pub const std_options: std.Options = .{
63
63
  pub fn main() !void {
64
64
  if (builtin.os.tag == .windows) try vsr.multiversion.wait_for_parent_to_exit();
65
65
 
66
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
66
+ var arena_instance = std.heap.ArenaAllocator.init(stdx.huge_page_allocator);
67
67
  defer arena_instance.deinit();
68
68
 
69
69
  // Arena is an implementation detail, all memory must be freed.
@@ -338,7 +338,7 @@ pub fn JournalType(comptime Replica: type, comptime Storage: type) type {
338
338
  ))[0..constants.journal_iops_write_max];
339
339
  errdefer allocator.free(write_headers_sectors);
340
340
 
341
- log.debug("{}: slot_count={} size={} headers_size={} prepares_size={}", .{
341
+ log.info("{}: slot_count={} size={} headers_size={} prepares_size={}", .{
342
342
  replica,
343
343
  slot_count,
344
344
  std.fmt.fmtIntSizeBin(write_ahead_log_zone_size),