tigerbeetle-node 0.10.0 → 0.11.1
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 +302 -101
- package/dist/index.d.ts +70 -72
- package/dist/index.js +70 -72
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/scripts/download_node_headers.sh +14 -7
- package/src/index.ts +6 -10
- package/src/node.zig +6 -3
- package/src/tigerbeetle/scripts/benchmark.sh +4 -4
- package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
- package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
- package/src/tigerbeetle/scripts/install.sh +19 -4
- package/src/tigerbeetle/scripts/install_zig.bat +5 -1
- package/src/tigerbeetle/scripts/install_zig.sh +24 -14
- package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
- package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
- package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
- package/src/tigerbeetle/src/benchmark.zig +29 -13
- package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
- package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
- package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
- package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
- package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -257
- package/src/tigerbeetle/src/c/tb_client.h +118 -84
- package/src/tigerbeetle/src/c/tb_client.zig +88 -23
- package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
- package/src/tigerbeetle/src/c/test.zig +371 -1
- package/src/tigerbeetle/src/cli.zig +37 -7
- package/src/tigerbeetle/src/config.zig +58 -17
- package/src/tigerbeetle/src/demo.zig +5 -2
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
- package/src/tigerbeetle/src/ewah.zig +11 -33
- package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
- package/src/tigerbeetle/src/io/linux.zig +1 -1
- package/src/tigerbeetle/src/lsm/README.md +308 -0
- package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +376 -397
- package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
- package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
- package/src/tigerbeetle/src/lsm/forest.zig +21 -447
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +414 -0
- package/src/tigerbeetle/src/lsm/grid.zig +170 -76
- package/src/tigerbeetle/src/lsm/groove.zig +197 -133
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
- package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
- package/src/tigerbeetle/src/lsm/manifest.zig +93 -180
- package/src/tigerbeetle/src/lsm/manifest_level.zig +161 -454
- package/src/tigerbeetle/src/lsm/manifest_log.zig +243 -356
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
- package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +65 -76
- package/src/tigerbeetle/src/lsm/segmented_array.zig +580 -251
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
- package/src/tigerbeetle/src/lsm/table.zig +115 -68
- package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
- package/src/tigerbeetle/src/lsm/table_iterator.zig +27 -17
- package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
- package/src/tigerbeetle/src/lsm/test.zig +61 -56
- package/src/tigerbeetle/src/lsm/tree.zig +450 -407
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +461 -0
- package/src/tigerbeetle/src/main.zig +83 -8
- package/src/tigerbeetle/src/message_bus.zig +20 -9
- package/src/tigerbeetle/src/message_pool.zig +22 -19
- package/src/tigerbeetle/src/ring_buffer.zig +7 -3
- package/src/tigerbeetle/src/simulator.zig +179 -119
- package/src/tigerbeetle/src/state_machine.zig +381 -246
- package/src/tigerbeetle/src/static_allocator.zig +65 -0
- package/src/tigerbeetle/src/storage.zig +3 -7
- package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
- package/src/tigerbeetle/src/test/accounting/workload.zig +823 -0
- package/src/tigerbeetle/src/test/cluster.zig +33 -81
- package/src/tigerbeetle/src/test/conductor.zig +366 -0
- package/src/tigerbeetle/src/test/fuzz.zig +121 -0
- package/src/tigerbeetle/src/test/id.zig +89 -0
- package/src/tigerbeetle/src/test/network.zig +45 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
- package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
- package/src/tigerbeetle/src/test/state_checker.zig +91 -69
- package/src/tigerbeetle/src/test/state_machine.zig +11 -35
- package/src/tigerbeetle/src/test/storage.zig +470 -106
- package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +15 -16
- package/src/tigerbeetle/src/unit_tests.zig +13 -1
- package/src/tigerbeetle/src/util.zig +97 -11
- package/src/tigerbeetle/src/vopr.zig +495 -0
- package/src/tigerbeetle/src/vsr/client.zig +21 -3
- package/src/tigerbeetle/src/vsr/journal.zig +293 -212
- package/src/tigerbeetle/src/vsr/replica.zig +1086 -515
- package/src/tigerbeetle/src/vsr/superblock.zig +382 -637
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +14 -16
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +416 -153
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +62 -12
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
- package/src/tigerbeetle/src/vsr.zig +94 -60
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
- package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
- package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#!/bin/
|
|
2
|
-
set -
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
3
|
|
|
4
4
|
ZIG_RELEASE_DEFAULT="0.9.1"
|
|
5
5
|
# Default to the release build, or allow the latest dev build, or an explicit release version:
|
|
6
6
|
ZIG_RELEASE=${1:-$ZIG_RELEASE_DEFAULT}
|
|
7
|
-
if [ "$ZIG_RELEASE"
|
|
7
|
+
if [ "$ZIG_RELEASE" = "latest" ]; then
|
|
8
8
|
ZIG_RELEASE="builds"
|
|
9
9
|
fi
|
|
10
10
|
|
|
11
11
|
# Validate the release version explicitly:
|
|
12
|
-
if
|
|
12
|
+
if echo "$ZIG_RELEASE" | grep -q '^builds$'; then
|
|
13
13
|
echo "Installing Zig latest build..."
|
|
14
|
-
elif
|
|
14
|
+
elif echo "$ZIG_RELEASE" | grep -q '^[0-9]\+.[0-9]\+.[0-9]\+$'; then
|
|
15
15
|
echo "Installing Zig $ZIG_RELEASE release build..."
|
|
16
16
|
else
|
|
17
17
|
echo "Release version invalid"
|
|
@@ -19,26 +19,31 @@ else
|
|
|
19
19
|
fi
|
|
20
20
|
|
|
21
21
|
# Determine the architecture:
|
|
22
|
-
if [
|
|
22
|
+
if [ "$(uname -m)" = 'arm64' ] || [ "$(uname -m)" = 'aarch64' ]; then
|
|
23
23
|
ZIG_ARCH="aarch64"
|
|
24
24
|
else
|
|
25
25
|
ZIG_ARCH="x86_64"
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
28
|
# Determine the operating system:
|
|
29
|
-
if [
|
|
30
|
-
ZIG_OS="macos"
|
|
31
|
-
else
|
|
29
|
+
if [ "$(uname)" = "Linux" ]; then
|
|
32
30
|
ZIG_OS="linux"
|
|
31
|
+
else
|
|
32
|
+
ZIG_OS="macos"
|
|
33
33
|
fi
|
|
34
34
|
|
|
35
35
|
ZIG_TARGET="zig-$ZIG_OS-$ZIG_ARCH"
|
|
36
36
|
|
|
37
37
|
# Determine the build, split the JSON line on whitespace and extract the 2nd field, then remove quotes and commas:
|
|
38
|
-
if command -v wget
|
|
38
|
+
if command -v wget; then
|
|
39
39
|
# -4 forces `wget` to connect to ipv4 addresses, as ipv6 fails to resolve on certain distros.
|
|
40
40
|
# Only A records (for ipv4) are used in DNS:
|
|
41
|
-
|
|
41
|
+
ipv4="-4"
|
|
42
|
+
# But Alpine doesn't support this argument
|
|
43
|
+
if [ -f /etc/alpine-release ]; then
|
|
44
|
+
ipv4=""
|
|
45
|
+
fi
|
|
46
|
+
ZIG_URL=$(wget $ipv4 --quiet -O - https://ziglang.org/download/index.json | grep -F "$ZIG_TARGET" | grep -F "$ZIG_RELEASE" | awk '{print $2}' | sed 's/[",]//g')
|
|
42
47
|
else
|
|
43
48
|
ZIG_URL=$(curl --silent https://ziglang.org/download/index.json | grep -F "$ZIG_TARGET" | grep -F "$ZIG_RELEASE" | awk '{print $2}' | sed 's/[",]//g')
|
|
44
49
|
fi
|
|
@@ -55,12 +60,17 @@ ZIG_DIRECTORY=$(basename "$ZIG_TARBALL" .tar.xz)
|
|
|
55
60
|
|
|
56
61
|
# Download, making sure we download to the same output document, without wget adding "-1" etc. if the file was previously partially downloaded:
|
|
57
62
|
echo "Downloading $ZIG_URL..."
|
|
58
|
-
if command -v wget
|
|
63
|
+
if command -v wget; then
|
|
59
64
|
# -4 forces `wget` to connect to ipv4 addresses, as ipv6 fails to resolve on certain distros.
|
|
60
65
|
# Only A records (for ipv4) are used in DNS:
|
|
61
|
-
|
|
66
|
+
ipv4="-4"
|
|
67
|
+
# But Alpine doesn't support this argument
|
|
68
|
+
if [ -f /etc/alpine-release ]; then
|
|
69
|
+
ipv4=""
|
|
70
|
+
fi
|
|
71
|
+
wget $ipv4 --quiet --output-document="$ZIG_TARBALL" "$ZIG_URL"
|
|
62
72
|
else
|
|
63
|
-
curl --silent --
|
|
73
|
+
curl --silent --output "$ZIG_TARBALL" "$ZIG_URL"
|
|
64
74
|
fi
|
|
65
75
|
|
|
66
76
|
# Extract and then remove the downloaded tarball:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# This script builds the docs website for the currently checked out
|
|
6
|
+
# branch.
|
|
7
|
+
|
|
8
|
+
git clone https://github.com/tigerbeetledb/docs docs_website
|
|
9
|
+
# Try to grab branch from Github Actions CI.
|
|
10
|
+
# See also: https://docs.github.com/en/actions/learn-github-actions/environment-variables.
|
|
11
|
+
BRANCH="$GITHUB_HEAD_REF"
|
|
12
|
+
if [[ -z "$BRANCH" ]]; then
|
|
13
|
+
# Otherwise fall back to git rev-parse
|
|
14
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
15
|
+
fi
|
|
16
|
+
( cd docs_website && npm install && ./scripts/build.sh "$BRANCH" )
|
|
17
|
+
rm -rf docs_website
|
|
@@ -9,10 +9,13 @@ pub const log_level: std.log.Level = .err;
|
|
|
9
9
|
const cli = @import("cli.zig");
|
|
10
10
|
const IO = @import("io.zig").IO;
|
|
11
11
|
|
|
12
|
+
const util = @import("util.zig");
|
|
12
13
|
const Storage = @import("storage.zig").Storage;
|
|
13
14
|
const MessagePool = @import("message_pool.zig").MessagePool;
|
|
14
15
|
const MessageBus = @import("message_bus.zig").MessageBusClient;
|
|
15
|
-
const StateMachine = @import("state_machine.zig").StateMachineType(Storage
|
|
16
|
+
const StateMachine = @import("state_machine.zig").StateMachineType(Storage, .{
|
|
17
|
+
.message_body_size_max = config.message_body_size_max,
|
|
18
|
+
});
|
|
16
19
|
const RingBuffer = @import("ring_buffer.zig").RingBuffer;
|
|
17
20
|
|
|
18
21
|
const vsr = @import("vsr.zig");
|
|
@@ -38,7 +41,7 @@ var accounts = [_]tb.Account{
|
|
|
38
41
|
.user_data = 0,
|
|
39
42
|
.reserved = [_]u8{0} ** 48,
|
|
40
43
|
.ledger = 2,
|
|
41
|
-
.code =
|
|
44
|
+
.code = 1,
|
|
42
45
|
.flags = .{},
|
|
43
46
|
.debits_pending = 0,
|
|
44
47
|
.debits_posted = 0,
|
|
@@ -50,7 +53,7 @@ var accounts = [_]tb.Account{
|
|
|
50
53
|
.user_data = 0,
|
|
51
54
|
.reserved = [_]u8{0} ** 48,
|
|
52
55
|
.ledger = 2,
|
|
53
|
-
.code =
|
|
56
|
+
.code = 1,
|
|
54
57
|
.flags = .{},
|
|
55
58
|
.debits_pending = 0,
|
|
56
59
|
.debits_posted = 0,
|
|
@@ -99,20 +102,21 @@ pub fn main() !void {
|
|
|
99
102
|
// Pre-allocate a million transfers:
|
|
100
103
|
const transfers = try allocator.alloc(tb.Transfer, transfers_max);
|
|
101
104
|
defer allocator.free(transfers);
|
|
102
|
-
|
|
105
|
+
|
|
103
106
|
for (transfers) |*transfer, index| {
|
|
104
107
|
transfer.* = .{
|
|
105
|
-
.id = index,
|
|
108
|
+
.id = index + 1,
|
|
106
109
|
.debit_account_id = accounts[0].id,
|
|
107
110
|
.credit_account_id = accounts[1].id,
|
|
108
|
-
.pending_id = 0,
|
|
109
111
|
.user_data = 0,
|
|
110
112
|
.reserved = 0,
|
|
111
|
-
.
|
|
113
|
+
.pending_id = 0,
|
|
114
|
+
.timeout = 0,
|
|
112
115
|
.ledger = 2,
|
|
116
|
+
.code = 1,
|
|
113
117
|
.flags = .{},
|
|
114
118
|
.amount = 1,
|
|
115
|
-
.
|
|
119
|
+
.timestamp = 0,
|
|
116
120
|
};
|
|
117
121
|
}
|
|
118
122
|
|
|
@@ -207,7 +211,8 @@ const TimedQueue = struct {
|
|
|
207
211
|
const message = self.client.get_message();
|
|
208
212
|
defer self.client.unref(message);
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
util.copy_disjoint(
|
|
215
|
+
.inexact,
|
|
211
216
|
u8,
|
|
212
217
|
message.buffer[@sizeOf(vsr.Header)..],
|
|
213
218
|
std.mem.sliceAsBytes(starting_batch.data),
|
|
@@ -238,8 +243,6 @@ const TimedQueue = struct {
|
|
|
238
243
|
@panic("Client returned error during benchmarking.");
|
|
239
244
|
};
|
|
240
245
|
|
|
241
|
-
log.debug("response={s}", .{std.mem.bytesAsSlice(tb.CreateAccountsResult, value)});
|
|
242
|
-
|
|
243
246
|
const self: *TimedQueue = @intToPtr(*TimedQueue, @intCast(usize, user_data));
|
|
244
247
|
const completed_batch: ?Batch = self.batches.pop();
|
|
245
248
|
assert(completed_batch != null);
|
|
@@ -251,8 +254,20 @@ const TimedQueue = struct {
|
|
|
251
254
|
});
|
|
252
255
|
const latency = now - self.batch_start.?;
|
|
253
256
|
switch (operation) {
|
|
254
|
-
.create_accounts => {
|
|
257
|
+
.create_accounts => {
|
|
258
|
+
const create_accounts_results = std.mem.bytesAsSlice(tb.CreateAccountsResult, value);
|
|
259
|
+
if (create_accounts_results.len > 0) {
|
|
260
|
+
log.err("CreateAccountsResults={any}", .{create_accounts_results});
|
|
261
|
+
@panic("Unexpected result creating accounts.");
|
|
262
|
+
}
|
|
263
|
+
},
|
|
255
264
|
.create_transfers => {
|
|
265
|
+
const create_transfers_results = std.mem.bytesAsSlice(tb.CreateTransfersResult, value);
|
|
266
|
+
if (create_transfers_results.len > 0) {
|
|
267
|
+
log.err("CreateTransfersResults={any}", .{create_transfers_results});
|
|
268
|
+
@panic("Unexpected result creating transfers.");
|
|
269
|
+
}
|
|
270
|
+
|
|
256
271
|
if (latency > self.transfers_latency_max) {
|
|
257
272
|
self.transfers_latency_max = latency;
|
|
258
273
|
}
|
|
@@ -264,7 +279,8 @@ const TimedQueue = struct {
|
|
|
264
279
|
const message = self.client.get_message();
|
|
265
280
|
defer self.client.unref(message);
|
|
266
281
|
|
|
267
|
-
|
|
282
|
+
util.copy_disjoint(
|
|
283
|
+
.inexact,
|
|
268
284
|
u8,
|
|
269
285
|
message.buffer[@sizeOf(vsr.Header)..],
|
|
270
286
|
std.mem.sliceAsBytes(next_batch.data),
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const os = std.os;
|
|
3
|
+
const assert = std.debug.assert;
|
|
4
|
+
|
|
5
|
+
const config = @import("../../config.zig");
|
|
6
|
+
const log = std.log.scoped(.tb_client_context);
|
|
7
|
+
|
|
8
|
+
const util = @import("../../util.zig");
|
|
9
|
+
const vsr = @import("../../vsr.zig");
|
|
10
|
+
const Header = vsr.Header;
|
|
11
|
+
|
|
12
|
+
const IO = @import("../../io.zig").IO;
|
|
13
|
+
const message_pool = @import("../../message_pool.zig");
|
|
14
|
+
|
|
15
|
+
const MessagePool = message_pool.MessagePool;
|
|
16
|
+
const Message = MessagePool.Message;
|
|
17
|
+
|
|
18
|
+
const Packet = @import("packet.zig").Packet;
|
|
19
|
+
const Signal = @import("signal.zig").Signal;
|
|
2
20
|
const ThreadType = @import("thread.zig").ThreadType;
|
|
3
21
|
|
|
4
22
|
const api = @import("../tb_client.zig");
|
|
@@ -13,15 +31,64 @@ pub const ContextImplementation = struct {
|
|
|
13
31
|
deinit_fn: fn (*ContextImplementation) void,
|
|
14
32
|
};
|
|
15
33
|
|
|
34
|
+
pub const Error = std.mem.Allocator.Error || error{
|
|
35
|
+
Unexpected,
|
|
36
|
+
AddressInvalid,
|
|
37
|
+
AddressLimitExceeded,
|
|
38
|
+
PacketsCountInvalid,
|
|
39
|
+
SystemResources,
|
|
40
|
+
NetworkSubsystemFailed,
|
|
41
|
+
};
|
|
42
|
+
|
|
16
43
|
pub fn ContextType(
|
|
17
|
-
comptime
|
|
18
|
-
comptime MessageBus: type,
|
|
44
|
+
comptime Client: type,
|
|
19
45
|
) type {
|
|
20
46
|
return struct {
|
|
21
47
|
const Context = @This();
|
|
22
|
-
const Thread = ThreadType(
|
|
48
|
+
const Thread = ThreadType(Context);
|
|
49
|
+
|
|
50
|
+
const UserData = extern struct {
|
|
51
|
+
self: *Context,
|
|
52
|
+
packet: *Packet,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
comptime {
|
|
56
|
+
assert(@sizeOf(UserData) == @sizeOf(u128));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn operation_event_size(op: u8) ?usize {
|
|
60
|
+
const allowed_operations = [_]Client.StateMachine.Operation{
|
|
61
|
+
.create_accounts,
|
|
62
|
+
.create_transfers,
|
|
63
|
+
.lookup_accounts,
|
|
64
|
+
.lookup_transfers,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
inline for (allowed_operations) |operation| {
|
|
68
|
+
if (op == @enumToInt(operation)) {
|
|
69
|
+
return @sizeOf(Client.StateMachine.Event(operation));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const PacketError = Client.Error || error{
|
|
77
|
+
TooMuchData,
|
|
78
|
+
InvalidOperation,
|
|
79
|
+
InvalidDataSize,
|
|
80
|
+
};
|
|
23
81
|
|
|
24
82
|
allocator: std.mem.Allocator,
|
|
83
|
+
client_id: u128,
|
|
84
|
+
packets: []Packet,
|
|
85
|
+
|
|
86
|
+
addresses: []const std.net.Address,
|
|
87
|
+
io: IO,
|
|
88
|
+
message_pool: MessagePool,
|
|
89
|
+
messages_available: usize,
|
|
90
|
+
client: Client,
|
|
91
|
+
|
|
25
92
|
on_completion_ctx: usize,
|
|
26
93
|
on_completion_fn: tb_completion_t,
|
|
27
94
|
implementation: ContextImplementation,
|
|
@@ -29,51 +96,199 @@ pub fn ContextType(
|
|
|
29
96
|
|
|
30
97
|
pub fn init(
|
|
31
98
|
allocator: std.mem.Allocator,
|
|
32
|
-
out_tb_client: *tb_client_t,
|
|
33
|
-
out_packets: *tb_packet_list_t,
|
|
34
99
|
cluster_id: u32,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
num_packets: u32,
|
|
100
|
+
addresses: []const u8,
|
|
101
|
+
packets_count: u32,
|
|
38
102
|
on_completion_ctx: usize,
|
|
39
103
|
on_completion_fn: tb_completion_t,
|
|
40
|
-
)
|
|
41
|
-
|
|
104
|
+
) Error!*Context {
|
|
105
|
+
var context = try allocator.create(Context);
|
|
106
|
+
errdefer allocator.destroy(context);
|
|
107
|
+
|
|
42
108
|
context.allocator = allocator;
|
|
109
|
+
context.client_id = std.crypto.random.int(u128);
|
|
110
|
+
log.debug("{}: init: initializing", .{context.client_id});
|
|
111
|
+
|
|
112
|
+
const packets_count_max = 4096;
|
|
113
|
+
if (packets_count > packets_count_max) {
|
|
114
|
+
return error.PacketsCountInvalid;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
log.debug("{}: init: allocating tb_packets", .{context.client_id});
|
|
118
|
+
context.packets = try context.allocator.alloc(Packet, packets_count);
|
|
119
|
+
errdefer context.allocator.free(context.packets);
|
|
120
|
+
|
|
121
|
+
log.debug("{}: init: parsing vsr addresses: {s}", .{ context.client_id, addresses });
|
|
122
|
+
context.addresses = vsr.parse_addresses(
|
|
123
|
+
context.allocator,
|
|
124
|
+
addresses,
|
|
125
|
+
config.replicas_max,
|
|
126
|
+
) catch |err| return switch (err) {
|
|
127
|
+
error.AddressLimitExceeded => error.AddressLimitExceeded,
|
|
128
|
+
else => error.AddressInvalid,
|
|
129
|
+
};
|
|
130
|
+
errdefer context.allocator.free(context.addresses);
|
|
131
|
+
|
|
132
|
+
log.debug("{}: init: initializing IO", .{context.client_id});
|
|
133
|
+
context.io = IO.init(32, 0) catch |err| {
|
|
134
|
+
log.err("{}: failed to initialize IO: {s}", .{
|
|
135
|
+
context.client_id,
|
|
136
|
+
@errorName(err),
|
|
137
|
+
});
|
|
138
|
+
return switch (err) {
|
|
139
|
+
error.ProcessFdQuotaExceeded => error.SystemResources,
|
|
140
|
+
error.Unexpected => error.Unexpected,
|
|
141
|
+
else => unreachable,
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
errdefer context.io.deinit();
|
|
145
|
+
|
|
146
|
+
log.debug("{}: init: initializing MessagePool", .{context.client_id});
|
|
147
|
+
context.message_pool = try MessagePool.init(allocator, .client);
|
|
148
|
+
errdefer context.message_pool.deinit(context.allocator);
|
|
149
|
+
|
|
150
|
+
log.debug("{}: init: initializing client (cluster_id={}, addresses={any})", .{
|
|
151
|
+
context.client_id,
|
|
152
|
+
cluster_id,
|
|
153
|
+
context.addresses,
|
|
154
|
+
});
|
|
155
|
+
context.client = try Client.init(
|
|
156
|
+
allocator,
|
|
157
|
+
context.client_id,
|
|
158
|
+
cluster_id,
|
|
159
|
+
@intCast(u8, context.addresses.len),
|
|
160
|
+
&context.message_pool,
|
|
161
|
+
.{
|
|
162
|
+
.configuration = context.addresses,
|
|
163
|
+
.io = &context.io,
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
errdefer context.client.deinit(context.allocator);
|
|
167
|
+
|
|
168
|
+
context.messages_available = config.client_request_queue_max;
|
|
43
169
|
context.on_completion_ctx = on_completion_ctx;
|
|
44
170
|
context.on_completion_fn = on_completion_fn;
|
|
45
|
-
|
|
46
|
-
out_tb_client.* = api.context_to_client(&context.implementation);
|
|
47
171
|
context.implementation = .{
|
|
48
172
|
.submit_fn = Context.on_submit,
|
|
49
173
|
.deinit_fn = Context.on_deinit,
|
|
50
174
|
};
|
|
51
175
|
|
|
52
|
-
|
|
53
|
-
context.thread.init(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
176
|
+
log.debug("{}: init: initializing thread", .{context.client_id});
|
|
177
|
+
try context.thread.init(context);
|
|
178
|
+
errdefer context.thread.deinit(context.allocator);
|
|
179
|
+
|
|
180
|
+
return context;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pub fn deinit(self: *Context) void {
|
|
184
|
+
self.thread.deinit();
|
|
185
|
+
|
|
186
|
+
self.client.deinit(self.allocator);
|
|
187
|
+
self.message_pool.deinit(self.allocator);
|
|
188
|
+
self.io.deinit();
|
|
189
|
+
|
|
190
|
+
self.allocator.free(self.addresses);
|
|
191
|
+
self.allocator.free(self.packets);
|
|
192
|
+
self.allocator.destroy(self);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
pub fn tick(self: *Context) void {
|
|
196
|
+
self.client.tick();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
pub fn run(self: *Context) void {
|
|
200
|
+
while (!self.thread.signal.is_shutdown()) {
|
|
201
|
+
self.tick();
|
|
202
|
+
self.io.run_for_ns(config.tick_ms * std.time.ns_per_ms) catch |err| {
|
|
203
|
+
log.err("{}: IO.run() failed: {s}", .{
|
|
204
|
+
self.client_id,
|
|
205
|
+
@errorName(err),
|
|
206
|
+
});
|
|
207
|
+
@panic("IO.run() failed");
|
|
67
208
|
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub fn request(self: *Context, packet: *Packet) void {
|
|
213
|
+
const message = self.message_pool.get_message();
|
|
214
|
+
defer self.message_pool.unref(message);
|
|
215
|
+
self.messages_available -= 1;
|
|
216
|
+
|
|
217
|
+
// Get the size of each request structure in the packet.data:
|
|
218
|
+
const event_size: usize = operation_event_size(packet.operation) orelse {
|
|
219
|
+
return self.on_complete(packet, error.InvalidOperation);
|
|
68
220
|
};
|
|
69
221
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
222
|
+
// Make sure the packet.data size is correct:
|
|
223
|
+
const readable = @ptrCast([*]const u8, packet.data)[0..packet.data_size];
|
|
224
|
+
if (readable.len == 0 or readable.len % event_size != 0) {
|
|
225
|
+
return self.on_complete(packet, error.InvalidDataSize);
|
|
73
226
|
}
|
|
74
227
|
|
|
75
|
-
|
|
76
|
-
|
|
228
|
+
// Make sure the packet.data wouldn't overflow a message:
|
|
229
|
+
const writable = message.buffer[@sizeOf(Header)..][0..config.message_body_size_max];
|
|
230
|
+
if (readable.len > writable.len) {
|
|
231
|
+
return self.on_complete(packet, error.TooMuchData);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Write the packet data to the message:
|
|
235
|
+
util.copy_disjoint(.inexact, u8, writable, readable);
|
|
236
|
+
const wrote = readable.len;
|
|
237
|
+
|
|
238
|
+
// Submit the message for processing:
|
|
239
|
+
self.client.request(
|
|
240
|
+
@bitCast(u128, UserData{
|
|
241
|
+
.self = self,
|
|
242
|
+
.packet = packet,
|
|
243
|
+
}),
|
|
244
|
+
Context.on_result,
|
|
245
|
+
@intToEnum(Client.StateMachine.Operation, packet.operation),
|
|
246
|
+
message,
|
|
247
|
+
wrote,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn on_result(
|
|
252
|
+
raw_user_data: u128,
|
|
253
|
+
op: Client.StateMachine.Operation,
|
|
254
|
+
results: Client.Error![]const u8,
|
|
255
|
+
) void {
|
|
256
|
+
const user_data = @bitCast(UserData, raw_user_data);
|
|
257
|
+
const self = user_data.self;
|
|
258
|
+
const packet = user_data.packet;
|
|
259
|
+
|
|
260
|
+
assert(packet.operation == @enumToInt(op));
|
|
261
|
+
self.on_complete(packet, results);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fn on_complete(
|
|
265
|
+
self: *Context,
|
|
266
|
+
packet: *Packet,
|
|
267
|
+
result: PacketError![]const u8,
|
|
268
|
+
) void {
|
|
269
|
+
self.messages_available += 1;
|
|
270
|
+
assert(self.messages_available <= config.client_request_queue_max);
|
|
271
|
+
|
|
272
|
+
// Signal to resume sending requests that was waiting for available messages.
|
|
273
|
+
if (self.messages_available == 1) self.thread.signal.notify();
|
|
274
|
+
|
|
275
|
+
const tb_client = api.context_to_client(&self.implementation);
|
|
276
|
+
const bytes = result catch |err| {
|
|
277
|
+
packet.status = switch (err) {
|
|
278
|
+
// If there's too many requests, (re)try submitting the packet later.
|
|
279
|
+
error.TooManyOutstandingRequests => {
|
|
280
|
+
return self.thread.retry.push(Packet.List.from(packet));
|
|
281
|
+
},
|
|
282
|
+
error.TooMuchData => .too_much_data,
|
|
283
|
+
error.InvalidOperation => .invalid_operation,
|
|
284
|
+
error.InvalidDataSize => .invalid_data_size,
|
|
285
|
+
};
|
|
286
|
+
return (self.on_completion_fn)(self.on_completion_ctx, tb_client, packet, null, 0);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// The packet completed normally.
|
|
290
|
+
packet.status = .ok;
|
|
291
|
+
(self.on_completion_fn)(self.on_completion_ctx, tb_client, packet, bytes.ptr, @intCast(u32, bytes.len));
|
|
77
292
|
}
|
|
78
293
|
|
|
79
294
|
fn on_submit(implementation: *ContextImplementation, packets: *tb_packet_list_t) void {
|
|
@@ -83,21 +298,7 @@ pub fn ContextType(
|
|
|
83
298
|
|
|
84
299
|
fn on_deinit(implementation: *ContextImplementation) void {
|
|
85
300
|
const context = @fieldParentPtr(Context, "implementation", implementation);
|
|
86
|
-
context.
|
|
87
|
-
context.allocator.destroy(context);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
fn on_completion(thread: *Thread, packet: *tb_packet_t, result: ?[]const u8) void {
|
|
91
|
-
const context = @fieldParentPtr(Context, "thread", thread);
|
|
92
|
-
const tb_client = api.context_to_client(&context.implementation);
|
|
93
|
-
|
|
94
|
-
context.on_completion_fn(
|
|
95
|
-
context.on_completion_ctx,
|
|
96
|
-
tb_client,
|
|
97
|
-
packet,
|
|
98
|
-
if (result) |r| r.ptr else null,
|
|
99
|
-
if (result) |r| @intCast(u32, r.len) else 0,
|
|
100
|
-
);
|
|
301
|
+
context.deinit();
|
|
101
302
|
}
|
|
102
303
|
};
|
|
103
304
|
}
|