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
package/README.md CHANGED
@@ -9,13 +9,12 @@ The following steps will install the `tigerbeetle-node` module to your current w
9
9
  * NodeJS >= `14.0.0`. _(If the correct version is not installed, an installation error will occur)_
10
10
 
11
11
  > Your operating system should be Linux (kernel >= v5.6) or macOS. Windows support is not yet available but is in the works.
12
-
13
- > Ensure you are not currently in the project (tigerbeetle-node) directory, but the `parent` directory instead;
14
12
 
15
13
  ### YARN Package Manager
16
14
 
17
15
  ```sh
18
- yarn add tigerbeetle-node
16
+ # Run the following from this directory
17
+ yarn add
19
18
  ```
20
19
  or
21
20
 
@@ -146,7 +145,7 @@ const errors = await client.createTransfers([transfer])
146
145
  Two-phase transfers are supported natively by toggling the appropriate flag. TigerBeetle will then adjust the `credits_reserved` and `debits_reserved` fields of the appropriate accounts. A corresponding commit transfer then needs to be sent to accept or reject the transfer.
147
146
  | bit 0 | bit 1 | bit 2 |
148
147
  |----------|--------------------|------------------|
149
- | `linked` | `two_phase_commit` | `condition` |
148
+ | `linked` | `posting` | `condition` |
150
149
 
151
150
  The `condition` flag signals to TigerBeetle that a 256-bit cryptographic condition will be supplied in the `reserved` field. This will be validated against a supplied pre-image when the transfer is committed. Transfers within a batch may also be linked (see [linked events](#linked-events)).
152
151
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tigerbeetle-node",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "TigerBeetle Node.js client",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "benchmark": "./scripts/benchmark.sh",
38
38
  "build": "yarn build:tsc && yarn build:zig",
39
39
  "build:tsc": "tsc",
40
- "build:zig": "mkdir -p dist && ZIG_SYSTEM_LINKER_HACK=1 zig/zig build-lib -mcpu=baseline -OReleaseSafe -dynamic -lc -isystem build/node-$(node --version)/include/node src/node.zig -fallow-shlib-undefined -femit-bin=dist/client.node",
40
+ "build:zig": "mkdir -p dist && zig/zig build-lib -mcpu=baseline -OReleaseSafe -dynamic -lc -isystem build/node-$(node --version)/include/node src/node.zig -fallow-shlib-undefined -femit-bin=dist/client.node",
41
41
  "clean": "rm -rf build dist node_modules src/zig-cache zig",
42
42
  "install:zig": "./src/tigerbeetle/scripts/install_zig.sh",
43
43
  "postinstall": "./scripts/postinstall.sh",
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  set -e
3
3
 
4
- ./src/tigerbeetle/scripts/install_zig.sh 0.8.1
4
+ ./src/tigerbeetle/scripts/install_zig.sh
5
5
  ./scripts/download_node_headers.sh
6
- mkdir -p dist && ZIG_SYSTEM_LINKER_HACK=1 zig/zig build-lib -mcpu=baseline -OReleaseSafe -dynamic -lc -isystem build/node-$(node --version)/include/node src/node.zig -fallow-shlib-undefined -femit-bin=dist/client.node
6
+ mkdir -p dist && zig/zig build-lib -mcpu=baseline -OReleaseSafe -dynamic -lc -isystem build/node-$(node --version)/include/node src/node.zig -fallow-shlib-undefined -femit-bin=dist/client.node
package/src/node.zig CHANGED
@@ -45,7 +45,7 @@ export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi
45
45
 
46
46
  const allocator = std.heap.c_allocator;
47
47
  var global = Globals.init(allocator, env) catch {
48
- std.log.emerg("Failed to initialise environment.\n", .{});
48
+ std.log.err("Failed to initialise environment.\n", .{});
49
49
  return null;
50
50
  };
51
51
  errdefer global.deinit();
@@ -57,7 +57,7 @@ export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi
57
57
  // state.
58
58
  translate.set_instance_data(
59
59
  env,
60
- @ptrCast(*c_void, @alignCast(@alignOf(u8), global)),
60
+ @ptrCast(*anyopaque, @alignCast(@alignOf(u8), global)),
61
61
  Globals.destroy,
62
62
  ) catch {
63
63
  global.deinit();
@@ -68,11 +68,11 @@ export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi
68
68
  }
69
69
 
70
70
  const Globals = struct {
71
- allocator: *std.mem.Allocator,
71
+ allocator: std.mem.Allocator,
72
72
  io: IO,
73
73
  napi_undefined: c.napi_value,
74
74
 
75
- pub fn init(allocator: *std.mem.Allocator, env: c.napi_env) !*Globals {
75
+ pub fn init(allocator: std.mem.Allocator, env: c.napi_env) !*Globals {
76
76
  const self = try allocator.create(Globals);
77
77
  errdefer allocator.destroy(self);
78
78
 
@@ -93,7 +93,7 @@ const Globals = struct {
93
93
  };
94
94
  errdefer self.io.deinit();
95
95
 
96
- if (c.napi_get_undefined(env, &self.napi_undefined) != .napi_ok) {
96
+ if (c.napi_get_undefined(env, &self.napi_undefined) != c.napi_ok) {
97
97
  return translate.throw(env, "Failed to capture the value of \"undefined\".");
98
98
  }
99
99
 
@@ -105,13 +105,16 @@ const Globals = struct {
105
105
  self.allocator.destroy(self);
106
106
  }
107
107
 
108
- pub fn destroy(env: c.napi_env, data: ?*c_void, hint: ?*c_void) callconv(.C) void {
108
+ pub fn destroy(env: c.napi_env, data: ?*anyopaque, hint: ?*anyopaque) callconv(.C) void {
109
+ _ = env;
110
+ _ = hint;
111
+
109
112
  const self = globalsCast(data.?);
110
113
  self.deinit();
111
114
  }
112
115
  };
113
116
 
114
- fn globalsCast(globals_raw: *c_void) *Globals {
117
+ fn globalsCast(globals_raw: *anyopaque) *Globals {
115
118
  return @ptrCast(*Globals, @alignCast(@alignOf(Globals), globals_raw));
116
119
  }
117
120
 
@@ -123,7 +126,7 @@ const Context = struct {
123
126
 
124
127
  fn create(
125
128
  env: c.napi_env,
126
- allocator: *std.mem.Allocator,
129
+ allocator: std.mem.Allocator,
127
130
  io: *IO,
128
131
  cluster: u32,
129
132
  addresses_raw: []const u8,
@@ -163,7 +166,7 @@ const Context = struct {
163
166
  }
164
167
  };
165
168
 
166
- fn contextCast(context_raw: *c_void) !*Context {
169
+ fn contextCast(context_raw: *anyopaque) !*Context {
167
170
  return @ptrCast(*Context, @alignCast(@alignOf(Context), context_raw));
168
171
  }
169
172
 
@@ -522,7 +525,7 @@ fn encode_napi_results_array(
522
525
  fn init(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
523
526
  var argc: usize = 1;
524
527
  var argv: [1]c.napi_value = undefined;
525
- if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != .napi_ok) {
528
+ if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != c.napi_ok) {
526
529
  translate.throw(env, "Failed to get args.") catch return null;
527
530
  }
528
531
  if (argc != 1) translate.throw(
@@ -555,7 +558,7 @@ fn init(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
555
558
  fn request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
556
559
  var argc: usize = 4;
557
560
  var argv: [4]c.napi_value = undefined;
558
- if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != .napi_ok) {
561
+ if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != c.napi_ok) {
559
562
  translate.throw(env, "Failed to get args.") catch return null;
560
563
  }
561
564
 
@@ -576,12 +579,7 @@ fn request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_valu
576
579
  translate.throw(env, "Unknown operation.") catch return null;
577
580
  }
578
581
 
579
- const message = context.client.get_message() orelse {
580
- translate.throw(
581
- env,
582
- "Too many requests outstanding.",
583
- ) catch return null;
584
- };
582
+ const message = context.client.get_message();
585
583
  defer context.client.unref(message);
586
584
 
587
585
  const operation = @intToEnum(Operation, @intCast(u8, operation_int));
@@ -609,7 +607,7 @@ fn request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_valu
609
607
  fn raw_request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
610
608
  var argc: usize = 4;
611
609
  var argv: [4]c.napi_value = undefined;
612
- if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != .napi_ok) {
610
+ if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != c.napi_ok) {
613
611
  translate.throw(env, "Failed to get args.") catch return null;
614
612
  }
615
613
 
@@ -631,12 +629,7 @@ fn raw_request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_
631
629
  }
632
630
  const operation = @intToEnum(Operation, @intCast(u8, operation_int));
633
631
 
634
- const message = context.client.get_message() orelse {
635
- translate.throw(
636
- env,
637
- "Too many requests outstanding.",
638
- ) catch return null;
639
- };
632
+ const message = context.client.get_message();
640
633
  defer context.client.unref(message);
641
634
 
642
635
  const body_length = translate.bytes_from_buffer(
@@ -730,11 +723,10 @@ fn on_result(user_data: u128, operation: Operation, results: Client.Error![]cons
730
723
  fn tick(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
731
724
  var argc: usize = 1;
732
725
  var argv: [1]c.napi_value = undefined;
733
- if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != .napi_ok) {
726
+ if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != c.napi_ok) {
734
727
  translate.throw(env, "Failed to get args.") catch return null;
735
728
  }
736
729
 
737
- const allocator = std.heap.c_allocator;
738
730
  if (argc != 1) translate.throw(
739
731
  env,
740
732
  "Function tick() requires 1 argument exactly.",
@@ -760,7 +752,7 @@ fn tick(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
760
752
  fn deinit(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
761
753
  var argc: usize = 1;
762
754
  var argv: [1]c.napi_value = undefined;
763
- if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != .napi_ok) {
755
+ if (c.napi_get_cb_info(env, info, &argc, &argv, null, null) != c.napi_ok) {
764
756
  translate.throw(env, "Failed to get args.") catch return null;
765
757
  }
766
758
 
@@ -0,0 +1,46 @@
1
+ @echo off
2
+ setlocal enabledelayedexpansion
3
+
4
+ if "%~1" equ ":main" (
5
+ shift /1
6
+ goto main
7
+ )
8
+
9
+ cmd /d /c "%~f0" :main %*
10
+ set ZIG_RESULT=%ERRORLEVEL%
11
+ taskkill /F /IM tigerbeetle.exe >nul
12
+
13
+ if !ZIG_RESULT! equ 0 (
14
+ del /f benchmark.log
15
+ ) else (
16
+ echo.
17
+ echo Error running benchmark, here are more details
18
+ type benchmark.log
19
+ )
20
+
21
+ echo.
22
+ exit /b
23
+
24
+ :main
25
+ zig\zig.exe build -Drelease-safe
26
+ move zig-out\bin\tigerbeetle.exe . >nul
27
+
28
+ for /l %%i in (0, 1, 0) do (
29
+ echo Initializing replica %%i
30
+ set ZIG_FILE=.\cluster_0000000000_replica_00%%i.tigerbeetle
31
+ if exist "!ZIG_FILE!" DEL /F "!ZIG_FILE!"
32
+ .\tigerbeetle.exe init --directory=. --cluster=0 --replica=%%i > benchmark.log 2>&1
33
+ )
34
+
35
+ for /l %%i in (0, 1, 0) do (
36
+ echo Starting replica %%i
37
+ start /B "tigerbeetle_%%i" .\tigerbeetle.exe start --directory=. --cluster=0 --addresses=3001 --replica=%%i > benchmark.log 2>&1
38
+ )
39
+
40
+ rem Wait for replicas to start, listen and connect:
41
+ timeout /t 2
42
+
43
+ echo.
44
+ echo Benchmarking...
45
+ zig\zig.exe run -OReleaseSafe src\benchmark.zig
46
+ exit /b %errorlevel%
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  set -e
3
- scripts/install_zig.sh 0.8.1
3
+ scripts/install_zig.sh
4
4
  echo "Building TigerBeetle..."
5
5
  zig/zig build -Dcpu=baseline -Drelease-safe
6
6
  mv zig-out/bin/tigerbeetle .
@@ -1,10 +1,10 @@
1
1
  @echo off
2
2
 
3
- set DEFAULT_RELEASE=0.8.1
3
+ set ZIG_RELEASE_DEFAULT=0.9.1
4
4
 
5
5
  :: Determine the Zig build:
6
6
  if "%~1"=="" (
7
- set ZIG_RELEASE=%DEFAULT_RELEASE%
7
+ set ZIG_RELEASE=%ZIG_RELEASE_DEFAULT%
8
8
  ) else if "%~1"=="latest" (
9
9
  set ZIG_RELEASE=builds
10
10
  ) else (
@@ -39,7 +39,7 @@ if "%ZIG_RELEASE%"=="builds" (
39
39
  )
40
40
 
41
41
  :: Using variable modifiers to determine the directory and filename from the URL:
42
- :: %~ni Expands %i to a file name only and %~xi Expands %i to a file name extension only.
42
+ :: %%~ni Expands %%i to a file name only and %%~xi Expands %%i to a file name extension only.
43
43
  for /f %%i in ("%ZIG_URL%") do (
44
44
  set ZIG_DIRECTORY=%%~ni
45
45
  set ZIG_TARBALL=%%~nxi
@@ -106,4 +106,4 @@ if exist %ZIG_TARBALL% (
106
106
  exit 1
107
107
  )
108
108
 
109
- echo "Congratulations, you have successfully installed Zig version %ZIG_RELEASE%. Enjoy!"
109
+ echo "Congratulations, you have successfully installed Zig version %ZIG_RELEASE%. Enjoy!"
@@ -1,9 +1,11 @@
1
1
  #!/bin/bash
2
2
  set -e
3
3
 
4
- # Default to the 0.8.1 build, or allow the latest dev build, or an explicit release version:
4
+ ZIG_RELEASE_DEFAULT="0.9.1"
5
+
6
+ # Default to the release build, or allow the latest dev build, or an explicit release version:
5
7
  if [ -z "$1" ]; then
6
- ZIG_RELEASE="0.8.1"
8
+ ZIG_RELEASE=$ZIG_RELEASE_DEFAULT
7
9
  elif [ "$1" == "latest" ]; then
8
10
  ZIG_RELEASE="builds"
9
11
  else
@@ -23,7 +23,7 @@ var file_stats = std.ArrayListUnmanaged(Stats){};
23
23
  var seen = std.AutoArrayHashMapUnmanaged(fs.File.INode, void){};
24
24
 
25
25
  var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
26
- const gpa = &general_purpose_allocator.allocator;
26
+ const gpa = general_purpose_allocator.allocator();
27
27
 
28
28
  pub fn main() !void {
29
29
  const argv = std.os.argv;
@@ -119,7 +119,13 @@ fn lint_file(file_path: []const u8, dir: fs.Dir, sub_path: []const u8) LintError
119
119
  // Add to set after no longer possible to get error.IsDir.
120
120
  if (try seen.fetchPut(gpa, stat.inode, {})) |_| return;
121
121
 
122
- const source = try source_file.readToEndAlloc(gpa, math.maxInt(usize));
122
+ const source = try source_file.readToEndAllocOptions(
123
+ gpa,
124
+ math.maxInt(usize),
125
+ null,
126
+ @alignOf(u8),
127
+ 0,
128
+ );
123
129
  try check_line_length(source, file_path);
124
130
 
125
131
  var tree = try std.zig.parse(gpa, source);
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
- # Install Zig 0.8.0 if it does not already exist:
4
+ # Install Zig if it does not already exist:
5
5
  if [ ! -d "zig" ]; then
6
- scripts/install_zig.sh 0.8.0
6
+ scripts/install_zig.sh
7
7
  echo ""
8
8
  echo "Running the TigerBeetle VOPR for the first time..."
9
9
  echo "Visit https://www.tigerbeetle.com"
@@ -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 config = @import("config.zig");
4
5
 
@@ -30,7 +31,7 @@ const BATCHES: f32 = MAX_TRANSFERS / BATCH_SIZE;
30
31
  const TOTAL_BATCHES = @ceil(BATCHES);
31
32
 
32
33
  const log = std.log;
33
- pub const log_level: std.log.Level = .notice;
34
+ pub const log_level: std.log.Level = .info;
34
35
 
35
36
  var accounts = [_]Account{
36
37
  Account{
@@ -65,13 +66,14 @@ pub fn main() !void {
65
66
  const stdout = std.io.getStdOut().writer();
66
67
  const stderr = std.io.getStdErr().writer();
67
68
 
68
- if (std.builtin.mode != .ReleaseSafe and std.builtin.mode != .ReleaseFast) {
69
+ if (builtin.mode != .ReleaseSafe and builtin.mode != .ReleaseFast) {
69
70
  try stderr.print("Benchmark must be built as ReleaseSafe for minimum performance.\n", .{});
70
71
  }
71
72
 
72
73
  var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
73
74
  defer arena.deinit();
74
- const allocator = &arena.allocator;
75
+
76
+ const allocator = arena.allocator();
75
77
 
76
78
  const client_id = std.crypto.random.int(u128);
77
79
  const cluster_id: u32 = 0;
@@ -90,7 +92,7 @@ pub fn main() !void {
90
92
  message_bus.set_on_message(*Client, &client, Client.on_message);
91
93
 
92
94
  // Pre-allocate a million transfers:
93
- var transfers = try arena.allocator.alloc(Transfer, MAX_TRANSFERS);
95
+ const transfers = try arena.allocator().alloc(Transfer, MAX_TRANSFERS);
94
96
  for (transfers) |*transfer, index| {
95
97
  transfer.* = .{
96
98
  .id = index,
@@ -106,7 +108,7 @@ pub fn main() !void {
106
108
  }
107
109
 
108
110
  // Pre-allocate a million commits:
109
- var commits: ?[]Commit = if (IS_TWO_PHASE_COMMIT) try arena.allocator.alloc(Commit, MAX_TRANSFERS) else null;
111
+ const commits: ?[]Commit = if (IS_TWO_PHASE_COMMIT) try arena.allocator().alloc(Commit, MAX_TRANSFERS) else null;
110
112
  if (commits) |all_commits| {
111
113
  for (all_commits) |*commit, index| {
112
114
  commit.* = .{
@@ -226,9 +228,7 @@ const TimedQueue = struct {
226
228
  if (self.batches.head_ptr()) |starting_batch| {
227
229
  log.debug("sending first batch...", .{});
228
230
  self.batch_start = now;
229
- var message = self.client.get_message() orelse {
230
- @panic("Client message pool has been exhausted. Cannot execute batch.");
231
- };
231
+ const message = self.client.get_message();
232
232
  defer self.client.unref(message);
233
233
 
234
234
  std.mem.copy(
@@ -254,7 +254,7 @@ const TimedQueue = struct {
254
254
  pub fn lap(user_data: u128, operation: Operation, results: Client.Error![]const u8) void {
255
255
  const now = std.time.milliTimestamp();
256
256
  const value = results catch |err| {
257
- log.emerg("Client returned error={o}", .{@errorName(err)});
257
+ log.err("Client returned error={o}", .{@errorName(err)});
258
258
  @panic("Client returned error during benchmarking.");
259
259
  };
260
260
 
@@ -286,9 +286,7 @@ const TimedQueue = struct {
286
286
  }
287
287
 
288
288
  if (self.batches.head_ptr()) |next_batch| {
289
- var message = self.client.get_message() orelse {
290
- @panic("Client message pool has been exhausted.");
291
- };
289
+ const message = self.client.get_message();
292
290
  defer self.client.unref(message);
293
291
 
294
292
  std.mem.copy(
@@ -8,6 +8,7 @@ const os = std.os;
8
8
 
9
9
  const config = @import("config.zig");
10
10
  const vsr = @import("vsr.zig");
11
+ const IO = @import("io.zig").IO;
11
12
 
12
13
  const usage = fmt.comptimePrint(
13
14
  \\Usage:
@@ -87,18 +88,31 @@ pub const Command = union(enum) {
87
88
 
88
89
  /// Parse the command line arguments passed to the tigerbeetle binary.
89
90
  /// Exits the program with a non-zero exit code if an error is found.
90
- pub fn parse_args(allocator: *std.mem.Allocator) Command {
91
+ pub fn parse_args(allocator: std.mem.Allocator) !Command {
91
92
  var maybe_cluster: ?[]const u8 = null;
92
93
  var maybe_replica: ?[]const u8 = null;
93
94
  var maybe_addresses: ?[]const u8 = null;
94
95
  var maybe_directory: ?[:0]const u8 = null;
95
96
 
96
- var args = std.process.args();
97
+ var args = try std.process.argsWithAllocator(allocator);
98
+ defer args.deinit();
99
+
100
+ // Keep track of the args from the ArgIterator above that were allocated
101
+ // then free them all at the end of the scope.
102
+ var args_allocated = std.ArrayList([:0]const u8).init(allocator);
103
+ defer {
104
+ for (args_allocated.items) |arg| allocator.free(arg);
105
+ args_allocated.deinit();
106
+ }
107
+
97
108
  // Skip argv[0] which is the name of this executable
98
- _ = args.nextPosix();
109
+ const did_skip = args.skip();
110
+ assert(did_skip);
111
+
112
+ const raw_command = try (args.next(allocator) orelse
113
+ fatal("no command provided, expected 'start' or 'init'", .{}));
114
+ defer allocator.free(raw_command);
99
115
 
100
- const raw_command = args.nextPosix() orelse
101
- fatal("no command provided, expected 'start' or 'init'", .{});
102
116
  if (mem.eql(u8, raw_command, "-h") or mem.eql(u8, raw_command, "--help")) {
103
117
  std.io.getStdOut().writeAll(usage) catch os.exit(1);
104
118
  os.exit(0);
@@ -106,7 +120,10 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
106
120
  const command = meta.stringToEnum(meta.Tag(Command), raw_command) orelse
107
121
  fatal("unknown command '{s}', expected 'start' or 'init'", .{raw_command});
108
122
 
109
- while (args.nextPosix()) |arg| {
123
+ while (args.next(allocator)) |parsed_arg| {
124
+ const arg = try parsed_arg;
125
+ try args_allocated.append(arg);
126
+
110
127
  if (mem.startsWith(u8, arg, "--cluster")) {
111
128
  maybe_cluster = parse_flag("--cluster", arg);
112
129
  } else if (mem.startsWith(u8, arg, "--replica")) {
@@ -118,8 +135,10 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
118
135
  } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
119
136
  std.io.getStdOut().writeAll(usage) catch os.exit(1);
120
137
  os.exit(0);
121
- } else {
138
+ } else if (mem.startsWith(u8, arg, "--")) {
122
139
  fatal("unexpected argument: '{s}'", .{arg});
140
+ } else {
141
+ fatal("unexpected argument: '{s}' (must start with '--')", .{arg});
123
142
  }
124
143
  }
125
144
 
@@ -130,7 +149,7 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
130
149
  const replica = parse_replica(raw_replica);
131
150
 
132
151
  const dir_path = maybe_directory orelse config.directory;
133
- const dir_fd = os.openZ(dir_path, os.O_CLOEXEC | os.O_RDONLY, 0) catch |err|
152
+ const dir_fd = IO.open_dir(dir_path) catch |err|
134
153
  fatal("failed to open directory '{s}': {}", .{ dir_path, err });
135
154
 
136
155
  switch (command) {
@@ -139,11 +158,13 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
139
158
  fatal("--addresses: supported only by 'start' command", .{});
140
159
  }
141
160
 
142
- return .{ .init = .{
143
- .cluster = cluster,
144
- .replica = replica,
145
- .dir_fd = dir_fd,
146
- } };
161
+ return Command{
162
+ .init = .{
163
+ .cluster = cluster,
164
+ .replica = replica,
165
+ .dir_fd = dir_fd,
166
+ },
167
+ };
147
168
  },
148
169
  .start => {
149
170
  const raw_addresses = maybe_addresses orelse
@@ -154,12 +175,14 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
154
175
  fatal("--replica: value greater than length of --addresses array", .{});
155
176
  }
156
177
 
157
- return .{ .start = .{
158
- .cluster = cluster,
159
- .replica = replica,
160
- .addresses = addresses,
161
- .dir_fd = dir_fd,
162
- } };
178
+ return Command{
179
+ .start = .{
180
+ .cluster = cluster,
181
+ .replica = replica,
182
+ .addresses = addresses,
183
+ .dir_fd = dir_fd,
184
+ },
185
+ };
163
186
  },
164
187
  }
165
188
  }
@@ -193,7 +216,7 @@ fn parse_cluster(raw_cluster: []const u8) u32 {
193
216
  }
194
217
 
195
218
  /// Parse and allocate the addresses returning a slice into that array.
196
- fn parse_addresses(allocator: *std.mem.Allocator, raw_addresses: []const u8) []net.Address {
219
+ fn parse_addresses(allocator: std.mem.Allocator, raw_addresses: []const u8) []net.Address {
197
220
  return vsr.parse_addresses(allocator, raw_addresses) catch |err| switch (err) {
198
221
  error.AddressHasTrailingComma => fatal("--addresses: invalid trailing comma", .{}),
199
222
  error.AddressLimitExceeded => {
@@ -1,8 +1,11 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
1
4
  /// Whether development or production:
2
5
  pub const deployment_environment = .development;
3
6
 
4
- /// The maximum log level in increasing order of verbosity (emergency=0, debug=7):
5
- pub const log_level = 6;
7
+ /// The maximum log level in increasing order of verbosity (emergency=0, debug=3):
8
+ pub const log_level = 2;
6
9
 
7
10
  /// The maximum number of replicas allowed in a cluster.
8
11
  pub const replicas_max = 6;
@@ -86,12 +89,6 @@ pub const connections_max = replicas_max + clients_max;
86
89
  /// This impacts the amount of memory allocated at initialization by the server.
87
90
  pub const message_size_max = 1 * 1024 * 1024;
88
91
 
89
- /// The number of full-sized messages allocated at initialization by the message bus.
90
- pub const message_bus_messages_max = connections_max * 4;
91
- /// The number of header-sized messages allocated at initialization by the message bus.
92
- /// These are much smaller/cheaper and we can therefore have many of them.
93
- pub const message_bus_headers_max = connections_max * connection_send_queue_max * 2;
94
-
95
92
  /// The maximum number of Viewstamped Replication prepare messages that can be inflight at a time.
96
93
  /// This is immutable once assigned per cluster, as replicas need to know how many operations might
97
94
  /// possibly be uncommitted during a view change, and this must be constant for all replicas.
@@ -102,8 +99,15 @@ pub const pipelining_max = clients_max;
102
99
  pub const connection_delay_min_ms = 50;
103
100
  pub const connection_delay_max_ms = 1000;
104
101
 
105
- /// The maximum number of outgoing messages that may be queued on a connection.
106
- pub const connection_send_queue_max = pipelining_max;
102
+ /// The maximum number of outgoing messages that may be queued on a replica connection.
103
+ pub const connection_send_queue_max_replica = std.math.max(std.math.min(clients_max, 4), 2);
104
+
105
+ /// The maximum number of outgoing messages that may be queued on a client connection.
106
+ /// The client has one in-flight request, and occasionally a ping.
107
+ pub const connection_send_queue_max_client = 2;
108
+
109
+ /// The maximum number of outgoing requests that may be queued on a client (including the in-flight request).
110
+ pub const client_request_queue_max = 32;
107
111
 
108
112
  /// The maximum number of connections in the kernel's complete connection queue pending an accept():
109
113
  /// If the backlog argument is greater than the value in `/proc/sys/net/core/somaxconn`, then it is
@@ -124,7 +128,8 @@ pub const tcp_rcvbuf = 4 * 1024 * 1024;
124
128
  /// This sets SO_SNDBUF as an alternative to the auto-tuning range in /proc/sys/net/ipv4/tcp_wmem.
125
129
  /// The value is limited by /proc/sys/net/core/wmem_max, unless the CAP_NET_ADMIN privilege exists.
126
130
  /// The kernel doubles this value to allow space for packet bookkeeping overhead.
127
- pub const tcp_sndbuf = 4 * 1024 * 1024;
131
+ pub const tcp_sndbuf_replica = connection_send_queue_max_replica * message_size_max;
132
+ pub const tcp_sndbuf_client = connection_send_queue_max_client * message_size_max;
128
133
 
129
134
  /// Whether to enable TCP keepalive:
130
135
  pub const tcp_keepalive = true;
@@ -218,3 +223,13 @@ pub const clock_synchronization_window_min_ms = 2000;
218
223
  /// This eliminates the impact of gradual clock drift on our clock offset (clock skew) measurements.
219
224
  /// If a window expires because of this then it is likely that the clock epoch will also be expired.
220
225
  pub const clock_synchronization_window_max_ms = 20000;
226
+
227
+ comptime {
228
+ // vsr.parse_address assumes that config.address/config.port are valid.
229
+ _ = std.net.Address.parseIp4(address, 0) catch unreachable;
230
+ _ = @as(u16, port);
231
+
232
+ // Avoid latency issues from a too-large sndbuf.
233
+ assert(tcp_sndbuf_replica <= 4 * 1024 * 1024);
234
+ assert(tcp_sndbuf_client <= 4 * 1024 * 1024);
235
+ }