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.
Files changed (109) hide show
  1. package/README.md +302 -101
  2. package/dist/index.d.ts +70 -72
  3. package/dist/index.js +70 -72
  4. package/dist/index.js.map +1 -1
  5. package/package.json +9 -8
  6. package/scripts/download_node_headers.sh +14 -7
  7. package/src/index.ts +6 -10
  8. package/src/node.zig +6 -3
  9. package/src/tigerbeetle/scripts/benchmark.sh +4 -4
  10. package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
  11. package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
  12. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
  13. package/src/tigerbeetle/scripts/install.sh +19 -4
  14. package/src/tigerbeetle/scripts/install_zig.bat +5 -1
  15. package/src/tigerbeetle/scripts/install_zig.sh +24 -14
  16. package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
  17. package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
  18. package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
  19. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
  20. package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
  21. package/src/tigerbeetle/src/benchmark.zig +29 -13
  22. package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
  23. package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
  24. package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
  25. package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
  26. package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -257
  27. package/src/tigerbeetle/src/c/tb_client.h +118 -84
  28. package/src/tigerbeetle/src/c/tb_client.zig +88 -23
  29. package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
  30. package/src/tigerbeetle/src/c/test.zig +371 -1
  31. package/src/tigerbeetle/src/cli.zig +37 -7
  32. package/src/tigerbeetle/src/config.zig +58 -17
  33. package/src/tigerbeetle/src/demo.zig +5 -2
  34. package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
  35. package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
  36. package/src/tigerbeetle/src/ewah.zig +11 -33
  37. package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
  38. package/src/tigerbeetle/src/io/linux.zig +1 -1
  39. package/src/tigerbeetle/src/lsm/README.md +308 -0
  40. package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
  41. package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
  42. package/src/tigerbeetle/src/lsm/compaction.zig +376 -397
  43. package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
  44. package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
  45. package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
  46. package/src/tigerbeetle/src/lsm/forest.zig +21 -447
  47. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +414 -0
  48. package/src/tigerbeetle/src/lsm/grid.zig +170 -76
  49. package/src/tigerbeetle/src/lsm/groove.zig +197 -133
  50. package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
  51. package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
  52. package/src/tigerbeetle/src/lsm/manifest.zig +93 -180
  53. package/src/tigerbeetle/src/lsm/manifest_level.zig +161 -454
  54. package/src/tigerbeetle/src/lsm/manifest_log.zig +243 -356
  55. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
  56. package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
  57. package/src/tigerbeetle/src/lsm/posted_groove.zig +65 -76
  58. package/src/tigerbeetle/src/lsm/segmented_array.zig +580 -251
  59. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
  60. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
  61. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
  62. package/src/tigerbeetle/src/lsm/table.zig +115 -68
  63. package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
  64. package/src/tigerbeetle/src/lsm/table_iterator.zig +27 -17
  65. package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
  66. package/src/tigerbeetle/src/lsm/test.zig +61 -56
  67. package/src/tigerbeetle/src/lsm/tree.zig +450 -407
  68. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +461 -0
  69. package/src/tigerbeetle/src/main.zig +83 -8
  70. package/src/tigerbeetle/src/message_bus.zig +20 -9
  71. package/src/tigerbeetle/src/message_pool.zig +22 -19
  72. package/src/tigerbeetle/src/ring_buffer.zig +7 -3
  73. package/src/tigerbeetle/src/simulator.zig +179 -119
  74. package/src/tigerbeetle/src/state_machine.zig +381 -246
  75. package/src/tigerbeetle/src/static_allocator.zig +65 -0
  76. package/src/tigerbeetle/src/storage.zig +3 -7
  77. package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
  78. package/src/tigerbeetle/src/test/accounting/workload.zig +823 -0
  79. package/src/tigerbeetle/src/test/cluster.zig +33 -81
  80. package/src/tigerbeetle/src/test/conductor.zig +366 -0
  81. package/src/tigerbeetle/src/test/fuzz.zig +121 -0
  82. package/src/tigerbeetle/src/test/id.zig +89 -0
  83. package/src/tigerbeetle/src/test/network.zig +45 -19
  84. package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
  85. package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
  86. package/src/tigerbeetle/src/test/state_checker.zig +91 -69
  87. package/src/tigerbeetle/src/test/state_machine.zig +11 -35
  88. package/src/tigerbeetle/src/test/storage.zig +470 -106
  89. package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
  90. package/src/tigerbeetle/src/tigerbeetle.zig +15 -16
  91. package/src/tigerbeetle/src/unit_tests.zig +13 -1
  92. package/src/tigerbeetle/src/util.zig +97 -11
  93. package/src/tigerbeetle/src/vopr.zig +495 -0
  94. package/src/tigerbeetle/src/vsr/client.zig +21 -3
  95. package/src/tigerbeetle/src/vsr/journal.zig +293 -212
  96. package/src/tigerbeetle/src/vsr/replica.zig +1086 -515
  97. package/src/tigerbeetle/src/vsr/superblock.zig +382 -637
  98. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +14 -16
  99. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +416 -153
  100. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
  101. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
  102. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +62 -12
  103. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
  104. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
  105. package/src/tigerbeetle/src/vsr.zig +94 -60
  106. package/src/tigerbeetle/scripts/vopr.bat +0 -48
  107. package/src/tigerbeetle/scripts/vopr.sh +0 -33
  108. package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
  109. package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
@@ -1,17 +1,17 @@
1
- #!/bin/bash
2
- set -eEuo pipefail
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" == "latest" ]; then
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 [[ $ZIG_RELEASE =~ ^builds$ ]]; then
12
+ if echo "$ZIG_RELEASE" | grep -q '^builds$'; then
13
13
  echo "Installing Zig latest build..."
14
- elif [[ $ZIG_RELEASE =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
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 [[ $(uname -m) == 'arm64' ]] || [[ $(uname -m) == 'aarch64' ]]; then
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 [[ "$OSTYPE" == "darwin"* ]]; then
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 &> /dev/null; then
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
- ZIG_URL=$(wget -4 --quiet -O - https://ziglang.org/download/index.json | grep -F "$ZIG_TARGET" | grep -F "$ZIG_RELEASE" | awk '{print $2}' | sed 's/[",]//g')
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 &> /dev/null; then
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
- wget -4 --quiet --show-progress --output-document="$ZIG_TARBALL" "$ZIG_URL"
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 --progress-bar --output "$ZIG_TARBALL" "$ZIG_URL"
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,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Usage:
4
+ # ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
5
+
6
+ set -euo pipefail
7
+ cd "$(git rev-parse --show-toplevel)"
8
+
9
+ zig fmt --check .
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ docker run -v "$(pwd)":/wrk -w /wrk koalaman/shellcheck scripts/*.sh
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ docker run --entrypoint sh -v "$(pwd)":/wrk -w /wrk alpine -c "
6
+ set -e
7
+ ./scripts/install_zig.sh
8
+ zig/zig build test
9
+ ./scripts/install.sh
10
+ "
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ docker run --entrypoint sh -v "$(pwd)":/wrk -w /wrk ubuntu -c "
6
+ set -e
7
+
8
+ apt-get update -y
9
+ apt-get install -y curl xz-utils
10
+
11
+ ./scripts/install_zig.sh
12
+ zig/zig build test
13
+ ./scripts/install.sh
14
+ "
@@ -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 = 0,
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 = 0,
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
- .code = 0,
113
+ .pending_id = 0,
114
+ .timeout = 0,
112
115
  .ledger = 2,
116
+ .code = 1,
113
117
  .flags = .{},
114
118
  .amount = 1,
115
- .timeout = 0,
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
- std.mem.copy(
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
- std.mem.copy(
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 StateMachine: type,
18
- comptime MessageBus: type,
44
+ comptime Client: type,
19
45
  ) type {
20
46
  return struct {
21
47
  const Context = @This();
22
- const Thread = ThreadType(StateMachine, MessageBus);
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
- addresses_ptr: [*:0]const u8,
36
- addresses_len: u32,
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
- ) tb_status_t {
41
- const context = allocator.create(Context) catch return .out_of_memory;
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
- const addresses = @ptrCast([*]const u8, addresses_ptr)[0..addresses_len];
53
- context.thread.init(
54
- allocator,
55
- cluster_id,
56
- addresses,
57
- num_packets,
58
- Context.on_completion,
59
- ) catch |err| {
60
- allocator.destroy(context);
61
- return switch (err) {
62
- error.Unexpected => .unexpected,
63
- error.OutOfMemory => .out_of_memory,
64
- error.InvalidAddress => .invalid_address,
65
- error.SystemResources => .system_resources,
66
- error.NetworkSubsystemFailed => .network_subsystem,
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
- var list = tb_packet_list_t{};
71
- for (context.thread.packets) |*packet| {
72
- list.push(tb_packet_list_t.from(packet));
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
- out_packets.* = list;
76
- return .success;
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.thread.deinit();
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
  }