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.
- package/README.md +3 -4
- package/package.json +2 -2
- package/scripts/postinstall.sh +2 -2
- package/src/node.zig +19 -27
- package/src/tigerbeetle/scripts/benchmark.bat +46 -0
- package/src/tigerbeetle/scripts/install.sh +1 -1
- package/src/tigerbeetle/scripts/install_zig.bat +4 -4
- package/src/tigerbeetle/scripts/install_zig.sh +4 -2
- package/src/tigerbeetle/scripts/lint.zig +8 -2
- package/src/tigerbeetle/scripts/vopr.sh +2 -2
- package/src/tigerbeetle/src/benchmark.zig +10 -12
- package/src/tigerbeetle/src/cli.zig +43 -20
- package/src/tigerbeetle/src/config.zig +26 -11
- package/src/tigerbeetle/src/demo.zig +119 -97
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
- package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +2 -3
- package/src/tigerbeetle/src/io/benchmark.zig +213 -0
- package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +259 -167
- package/src/tigerbeetle/src/io/linux.zig +1038 -0
- package/src/tigerbeetle/src/io/test.zig +643 -0
- package/src/tigerbeetle/src/io/windows.zig +1161 -0
- package/src/tigerbeetle/src/io.zig +9 -1328
- package/src/tigerbeetle/src/main.zig +28 -15
- package/src/tigerbeetle/src/message_bus.zig +78 -107
- package/src/tigerbeetle/src/message_pool.zig +65 -58
- package/src/tigerbeetle/src/ring_buffer.zig +7 -0
- package/src/tigerbeetle/src/simulator.zig +44 -40
- package/src/tigerbeetle/src/state_machine.zig +58 -27
- package/src/tigerbeetle/src/storage.zig +7 -234
- package/src/tigerbeetle/src/test/cluster.zig +5 -8
- package/src/tigerbeetle/src/test/message_bus.zig +10 -9
- package/src/tigerbeetle/src/test/network.zig +16 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
- package/src/tigerbeetle/src/test/state_checker.zig +4 -3
- package/src/tigerbeetle/src/test/state_machine.zig +4 -0
- package/src/tigerbeetle/src/test/storage.zig +23 -19
- package/src/tigerbeetle/src/test/time.zig +2 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +8 -128
- package/src/tigerbeetle/src/time.zig +61 -13
- package/src/tigerbeetle/src/vsr/client.zig +23 -37
- package/src/tigerbeetle/src/vsr/clock.zig +27 -44
- package/src/tigerbeetle/src/vsr/journal.zig +9 -12
- package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
- package/src/tigerbeetle/src/vsr/replica.zig +184 -204
- package/src/tigerbeetle/src/vsr.zig +287 -25
- 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
|
-
|
|
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` | `
|
|
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.
|
|
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 &&
|
|
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",
|
package/scripts/postinstall.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -e
|
|
3
3
|
|
|
4
|
-
./src/tigerbeetle/scripts/install_zig.sh
|
|
4
|
+
./src/tigerbeetle/scripts/install_zig.sh
|
|
5
5
|
./scripts/download_node_headers.sh
|
|
6
|
-
mkdir -p dist &&
|
|
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.
|
|
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(*
|
|
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:
|
|
71
|
+
allocator: std.mem.Allocator,
|
|
72
72
|
io: IO,
|
|
73
73
|
napi_undefined: c.napi_value,
|
|
74
74
|
|
|
75
|
-
pub fn init(allocator:
|
|
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: ?*
|
|
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: *
|
|
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:
|
|
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: *
|
|
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()
|
|
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()
|
|
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,10 +1,10 @@
|
|
|
1
1
|
@echo off
|
|
2
2
|
|
|
3
|
-
set
|
|
3
|
+
set ZIG_RELEASE_DEFAULT=0.9.1
|
|
4
4
|
|
|
5
5
|
:: Determine the Zig build:
|
|
6
6
|
if "%~1"=="" (
|
|
7
|
-
set ZIG_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
|
-
::
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
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
|
|
4
|
+
# Install Zig if it does not already exist:
|
|
5
5
|
if [ ! -d "zig" ]; then
|
|
6
|
-
scripts/install_zig.sh
|
|
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 = .
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
|
143
|
-
.
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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:
|
|
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=
|
|
5
|
-
pub const log_level =
|
|
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
|
|
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
|
|
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
|
+
}
|