tigerbeetle-node 0.5.2 → 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 +1 -1
- package/src/node.zig +2 -12
- package/src/tigerbeetle/scripts/benchmark.bat +46 -0
- package/src/tigerbeetle/scripts/install_zig.bat +2 -2
- package/src/tigerbeetle/scripts/install_zig.sh +1 -1
- package/src/tigerbeetle/scripts/vopr.sh +2 -2
- package/src/tigerbeetle/src/benchmark.zig +2 -6
- package/src/tigerbeetle/src/cli.zig +39 -18
- package/src/tigerbeetle/src/config.zig +24 -9
- package/src/tigerbeetle/src/demo.zig +1 -1
- package/src/tigerbeetle/src/io/benchmark.zig +24 -49
- package/src/tigerbeetle/src/io/darwin.zig +175 -44
- package/src/tigerbeetle/src/io/linux.zig +177 -72
- package/src/tigerbeetle/src/io/test.zig +61 -39
- package/src/tigerbeetle/src/io/windows.zig +1161 -0
- package/src/tigerbeetle/src/io.zig +2 -0
- package/src/tigerbeetle/src/main.zig +13 -8
- package/src/tigerbeetle/src/message_bus.zig +49 -61
- package/src/tigerbeetle/src/message_pool.zig +63 -57
- package/src/tigerbeetle/src/ring_buffer.zig +7 -0
- package/src/tigerbeetle/src/simulator.zig +4 -4
- package/src/tigerbeetle/src/storage.zig +0 -230
- package/src/tigerbeetle/src/test/cluster.zig +3 -6
- package/src/tigerbeetle/src/test/message_bus.zig +4 -3
- package/src/tigerbeetle/src/test/network.zig +13 -16
- package/src/tigerbeetle/src/test/state_checker.zig +3 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +5 -3
- package/src/tigerbeetle/src/time.zig +58 -11
- package/src/tigerbeetle/src/vsr/client.zig +18 -32
- package/src/tigerbeetle/src/vsr/clock.zig +1 -1
- package/src/tigerbeetle/src/vsr/journal.zig +2 -6
- package/src/tigerbeetle/src/vsr/replica.zig +146 -169
- package/src/tigerbeetle/src/vsr.zig +263 -5
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
package/src/node.zig
CHANGED
|
@@ -579,12 +579,7 @@ fn request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_valu
|
|
|
579
579
|
translate.throw(env, "Unknown operation.") catch return null;
|
|
580
580
|
}
|
|
581
581
|
|
|
582
|
-
const message = context.client.get_message()
|
|
583
|
-
translate.throw(
|
|
584
|
-
env,
|
|
585
|
-
"Too many requests outstanding.",
|
|
586
|
-
) catch return null;
|
|
587
|
-
};
|
|
582
|
+
const message = context.client.get_message();
|
|
588
583
|
defer context.client.unref(message);
|
|
589
584
|
|
|
590
585
|
const operation = @intToEnum(Operation, @intCast(u8, operation_int));
|
|
@@ -634,12 +629,7 @@ fn raw_request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_
|
|
|
634
629
|
}
|
|
635
630
|
const operation = @intToEnum(Operation, @intCast(u8, operation_int));
|
|
636
631
|
|
|
637
|
-
const message = context.client.get_message()
|
|
638
|
-
translate.throw(
|
|
639
|
-
env,
|
|
640
|
-
"Too many requests outstanding.",
|
|
641
|
-
) catch return null;
|
|
642
|
-
};
|
|
632
|
+
const message = context.client.get_message();
|
|
643
633
|
defer context.client.unref(message);
|
|
644
634
|
|
|
645
635
|
const body_length = translate.bytes_from_buffer(
|
|
@@ -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
|
@echo off
|
|
2
2
|
|
|
3
|
-
set ZIG_RELEASE_DEFAULT=0.9.
|
|
3
|
+
set ZIG_RELEASE_DEFAULT=0.9.1
|
|
4
4
|
|
|
5
5
|
:: Determine the Zig build:
|
|
6
6
|
if "%~1"=="" (
|
|
@@ -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
|
|
@@ -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"
|
|
@@ -228,9 +228,7 @@ const TimedQueue = struct {
|
|
|
228
228
|
if (self.batches.head_ptr()) |starting_batch| {
|
|
229
229
|
log.debug("sending first batch...", .{});
|
|
230
230
|
self.batch_start = now;
|
|
231
|
-
|
|
232
|
-
@panic("Client message pool has been exhausted. Cannot execute batch.");
|
|
233
|
-
};
|
|
231
|
+
const message = self.client.get_message();
|
|
234
232
|
defer self.client.unref(message);
|
|
235
233
|
|
|
236
234
|
std.mem.copy(
|
|
@@ -288,9 +286,7 @@ const TimedQueue = struct {
|
|
|
288
286
|
}
|
|
289
287
|
|
|
290
288
|
if (self.batches.head_ptr()) |next_batch| {
|
|
291
|
-
|
|
292
|
-
@panic("Client message pool has been exhausted.");
|
|
293
|
-
};
|
|
289
|
+
const message = self.client.get_message();
|
|
294
290
|
defer self.client.unref(message);
|
|
295
291
|
|
|
296
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.
|
|
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")) {
|
|
@@ -132,7 +149,7 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
|
|
|
132
149
|
const replica = parse_replica(raw_replica);
|
|
133
150
|
|
|
134
151
|
const dir_path = maybe_directory orelse config.directory;
|
|
135
|
-
const dir_fd =
|
|
152
|
+
const dir_fd = IO.open_dir(dir_path) catch |err|
|
|
136
153
|
fatal("failed to open directory '{s}': {}", .{ dir_path, err });
|
|
137
154
|
|
|
138
155
|
switch (command) {
|
|
@@ -141,11 +158,13 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
|
|
|
141
158
|
fatal("--addresses: supported only by 'start' command", .{});
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
return
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
161
|
+
return Command{
|
|
162
|
+
.init = .{
|
|
163
|
+
.cluster = cluster,
|
|
164
|
+
.replica = replica,
|
|
165
|
+
.dir_fd = dir_fd,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
149
168
|
},
|
|
150
169
|
.start => {
|
|
151
170
|
const raw_addresses = maybe_addresses orelse
|
|
@@ -156,12 +175,14 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
|
|
|
156
175
|
fatal("--replica: value greater than length of --addresses array", .{});
|
|
157
176
|
}
|
|
158
177
|
|
|
159
|
-
return
|
|
160
|
-
.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
return Command{
|
|
179
|
+
.start = .{
|
|
180
|
+
.cluster = cluster,
|
|
181
|
+
.replica = replica,
|
|
182
|
+
.addresses = addresses,
|
|
183
|
+
.dir_fd = dir_fd,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
165
186
|
},
|
|
166
187
|
}
|
|
167
188
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
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
|
|
|
@@ -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
|
+
}
|
|
@@ -53,7 +53,7 @@ pub fn request(
|
|
|
53
53
|
|
|
54
54
|
message_bus.set_on_message(*Client, &client, Client.on_message);
|
|
55
55
|
|
|
56
|
-
var message = client.get_message()
|
|
56
|
+
var message = client.get_message();
|
|
57
57
|
defer client.unref(message);
|
|
58
58
|
|
|
59
59
|
const body = std.mem.asBytes(&batch);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const os = std.os;
|
|
3
3
|
const assert = std.debug.assert;
|
|
4
|
+
const log = std.log.scoped(.io_benchmark);
|
|
4
5
|
|
|
5
6
|
const Time = @import("../time.zig").Time;
|
|
6
7
|
const IO = @import("../io.zig").IO;
|
|
@@ -14,7 +15,7 @@ const run_duration = 1 * std.time.ns_per_s;
|
|
|
14
15
|
|
|
15
16
|
pub fn main() !void {
|
|
16
17
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
17
|
-
const allocator =
|
|
18
|
+
const allocator = gpa.allocator();
|
|
18
19
|
defer {
|
|
19
20
|
const leaks = gpa.deinit();
|
|
20
21
|
assert(!leaks);
|
|
@@ -24,37 +25,34 @@ pub fn main() !void {
|
|
|
24
25
|
defer allocator.free(buffer);
|
|
25
26
|
std.mem.set(u8, buffer, 0);
|
|
26
27
|
|
|
27
|
-
var timer = Time{};
|
|
28
|
-
const started = timer.monotonic();
|
|
29
28
|
var self = Context{
|
|
30
29
|
.io = try IO.init(32, 0),
|
|
31
|
-
.timer = &timer,
|
|
32
|
-
.started = started,
|
|
33
|
-
.current = started,
|
|
34
30
|
.tx = .{ .buffer = buffer[0 * buffer_size ..][0..buffer_size] },
|
|
35
31
|
.rx = .{ .buffer = buffer[1 * buffer_size ..][0..buffer_size] },
|
|
36
32
|
};
|
|
33
|
+
defer self.io.deinit();
|
|
37
34
|
|
|
35
|
+
var timer = Time{};
|
|
36
|
+
const started = timer.monotonic();
|
|
38
37
|
defer {
|
|
39
|
-
|
|
40
|
-
const elapsed_ns = self.current - started;
|
|
38
|
+
const elapsed_ns = timer.monotonic() - started;
|
|
41
39
|
const transferred_mb = @intToFloat(f64, self.transferred) / 1024 / 1024;
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
log.info("took {}ms @ {d:.2} MB/s\n", .{
|
|
44
42
|
elapsed_ns / std.time.ns_per_ms,
|
|
45
43
|
transferred_mb / (@intToFloat(f64, elapsed_ns) / std.time.ns_per_s),
|
|
46
44
|
});
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
// Setup the server socket
|
|
50
|
-
self.server.fd = try
|
|
48
|
+
self.server.fd = try self.io.open_socket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
51
49
|
defer os.closeSocket(self.server.fd);
|
|
52
50
|
|
|
53
51
|
const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
|
|
54
52
|
try os.setsockopt(
|
|
55
53
|
self.server.fd,
|
|
56
|
-
os.
|
|
57
|
-
os.
|
|
54
|
+
os.SOL.SOCKET,
|
|
55
|
+
os.SO.REUSEADDR,
|
|
58
56
|
&std.mem.toBytes(@as(c_int, 1)),
|
|
59
57
|
);
|
|
60
58
|
try os.bind(self.server.fd, &address.any, address.getOsSockLen());
|
|
@@ -70,7 +68,7 @@ pub fn main() !void {
|
|
|
70
68
|
);
|
|
71
69
|
|
|
72
70
|
// Setup the client connection
|
|
73
|
-
self.tx.socket.fd = try
|
|
71
|
+
self.tx.socket.fd = try self.io.open_socket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
74
72
|
defer os.closeSocket(self.tx.socket.fd);
|
|
75
73
|
|
|
76
74
|
self.io.connect(
|
|
@@ -82,22 +80,14 @@ pub fn main() !void {
|
|
|
82
80
|
address,
|
|
83
81
|
);
|
|
84
82
|
|
|
85
|
-
// Run the IO loop
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
while (self.is_running()) : (tick +%= 1) {
|
|
89
|
-
if (tick % 61 == 0) {
|
|
90
|
-
const timeout_ns = tick % (10 * std.time.ns_per_ms);
|
|
91
|
-
try self.io.run_for_ns(@intCast(u63, timeout_ns));
|
|
92
|
-
} else {
|
|
93
|
-
try self.io.tick();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
83
|
+
// Run the IO loop for the duration of the benchmark
|
|
84
|
+
log.info("running for {}", .{std.fmt.fmtDuration(run_duration)});
|
|
85
|
+
try self.io.run_for_ns(run_duration);
|
|
96
86
|
|
|
97
87
|
// Assert that everything is connected
|
|
98
|
-
assert(self.server.fd !=
|
|
99
|
-
assert(self.tx.socket.fd !=
|
|
100
|
-
assert(self.rx.socket.fd !=
|
|
88
|
+
assert(self.server.fd != IO.INVALID_SOCKET);
|
|
89
|
+
assert(self.tx.socket.fd != IO.INVALID_SOCKET);
|
|
90
|
+
assert(self.rx.socket.fd != IO.INVALID_SOCKET);
|
|
101
91
|
|
|
102
92
|
// Close the accepted client socket.
|
|
103
93
|
// The actual client socket + server socket are closed by defer
|
|
@@ -108,14 +98,11 @@ const Context = struct {
|
|
|
108
98
|
io: IO,
|
|
109
99
|
tx: Pipe,
|
|
110
100
|
rx: Pipe,
|
|
111
|
-
timer: *Time,
|
|
112
|
-
started: u64,
|
|
113
|
-
current: u64,
|
|
114
101
|
server: Socket = .{},
|
|
115
102
|
transferred: u64 = 0,
|
|
116
103
|
|
|
117
104
|
const Socket = struct {
|
|
118
|
-
fd: os.socket_t =
|
|
105
|
+
fd: os.socket_t = IO.INVALID_SOCKET,
|
|
119
106
|
completion: IO.Completion = undefined,
|
|
120
107
|
};
|
|
121
108
|
const Pipe = struct {
|
|
@@ -124,21 +111,12 @@ const Context = struct {
|
|
|
124
111
|
transferred: usize = 0,
|
|
125
112
|
};
|
|
126
113
|
|
|
127
|
-
fn is_running(self: Context) bool {
|
|
128
|
-
// Make sure that we're connected
|
|
129
|
-
if (self.rx.socket.fd == -1) return true;
|
|
130
|
-
|
|
131
|
-
// Make sure that we haven't run too long as configured
|
|
132
|
-
const elapsed = self.current - self.started;
|
|
133
|
-
return elapsed < run_duration;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
114
|
fn on_accept(
|
|
137
115
|
self: *Context,
|
|
138
116
|
completion: *IO.Completion,
|
|
139
117
|
result: IO.AcceptError!os.socket_t,
|
|
140
118
|
) void {
|
|
141
|
-
assert(self.rx.socket.fd ==
|
|
119
|
+
assert(self.rx.socket.fd == IO.INVALID_SOCKET);
|
|
142
120
|
assert(&self.server.completion == completion);
|
|
143
121
|
self.rx.socket.fd = result catch |err| std.debug.panic("accept error {}", .{err});
|
|
144
122
|
|
|
@@ -152,7 +130,9 @@ const Context = struct {
|
|
|
152
130
|
completion: *IO.Completion,
|
|
153
131
|
result: IO.ConnectError!void,
|
|
154
132
|
) void {
|
|
155
|
-
|
|
133
|
+
_ = result catch unreachable;
|
|
134
|
+
|
|
135
|
+
assert(self.tx.socket.fd != IO.INVALID_SOCKET);
|
|
156
136
|
assert(&self.tx.socket.completion == completion);
|
|
157
137
|
|
|
158
138
|
// Start sending data to the server's accepted client
|
|
@@ -161,8 +141,8 @@ const Context = struct {
|
|
|
161
141
|
}
|
|
162
142
|
|
|
163
143
|
const TransferType = enum {
|
|
164
|
-
read
|
|
165
|
-
write
|
|
144
|
+
read,
|
|
145
|
+
write,
|
|
166
146
|
};
|
|
167
147
|
|
|
168
148
|
fn do_transfer(
|
|
@@ -188,11 +168,6 @@ const Context = struct {
|
|
|
188
168
|
assert(bytes <= buffer_size);
|
|
189
169
|
self.transferred += bytes;
|
|
190
170
|
|
|
191
|
-
// Check in with the benchmark timer to stop sending/receiving data
|
|
192
|
-
self.current = self.timer.monotonic();
|
|
193
|
-
if (!self.is_running())
|
|
194
|
-
return;
|
|
195
|
-
|
|
196
171
|
// Select which connection (tx or rx) depending on the type of transfer
|
|
197
172
|
const pipe = &@field(self, pipe_name);
|
|
198
173
|
pipe.transferred += bytes;
|