tigerbeetle-node 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) 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 +6 -6
  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/src/benchmark.zig +4 -2
  21. package/src/tigerbeetle/src/benchmark_array_search.zig +3 -3
  22. package/src/tigerbeetle/src/c/tb_client/thread.zig +8 -9
  23. package/src/tigerbeetle/src/c/tb_client.h +100 -80
  24. package/src/tigerbeetle/src/c/tb_client.zig +4 -1
  25. package/src/tigerbeetle/src/cli.zig +1 -1
  26. package/src/tigerbeetle/src/config.zig +48 -16
  27. package/src/tigerbeetle/src/demo.zig +3 -1
  28. package/src/tigerbeetle/src/eytzinger_benchmark.zig +3 -3
  29. package/src/tigerbeetle/src/io/linux.zig +1 -1
  30. package/src/tigerbeetle/src/lsm/README.md +214 -0
  31. package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
  32. package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
  33. package/src/tigerbeetle/src/lsm/compaction.zig +352 -398
  34. package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
  35. package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
  36. package/src/tigerbeetle/src/lsm/forest.zig +21 -447
  37. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +412 -0
  38. package/src/tigerbeetle/src/lsm/grid.zig +145 -69
  39. package/src/tigerbeetle/src/lsm/groove.zig +196 -133
  40. package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
  41. package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
  42. package/src/tigerbeetle/src/lsm/manifest.zig +81 -181
  43. package/src/tigerbeetle/src/lsm/manifest_level.zig +210 -454
  44. package/src/tigerbeetle/src/lsm/manifest_log.zig +77 -28
  45. package/src/tigerbeetle/src/lsm/posted_groove.zig +64 -76
  46. package/src/tigerbeetle/src/lsm/segmented_array.zig +561 -241
  47. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
  48. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
  49. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
  50. package/src/tigerbeetle/src/lsm/table.zig +83 -48
  51. package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
  52. package/src/tigerbeetle/src/lsm/table_iterator.zig +25 -14
  53. package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
  54. package/src/tigerbeetle/src/lsm/test.zig +49 -55
  55. package/src/tigerbeetle/src/lsm/tree.zig +407 -402
  56. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +457 -0
  57. package/src/tigerbeetle/src/main.zig +28 -6
  58. package/src/tigerbeetle/src/message_bus.zig +2 -2
  59. package/src/tigerbeetle/src/message_pool.zig +14 -17
  60. package/src/tigerbeetle/src/simulator.zig +145 -112
  61. package/src/tigerbeetle/src/state_machine.zig +338 -228
  62. package/src/tigerbeetle/src/static_allocator.zig +65 -0
  63. package/src/tigerbeetle/src/storage.zig +3 -7
  64. package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
  65. package/src/tigerbeetle/src/test/accounting/workload.zig +819 -0
  66. package/src/tigerbeetle/src/test/cluster.zig +18 -48
  67. package/src/tigerbeetle/src/test/conductor.zig +365 -0
  68. package/src/tigerbeetle/src/test/fuzz.zig +121 -0
  69. package/src/tigerbeetle/src/test/id.zig +89 -0
  70. package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
  71. package/src/tigerbeetle/src/test/state_checker.zig +93 -69
  72. package/src/tigerbeetle/src/test/state_machine.zig +11 -35
  73. package/src/tigerbeetle/src/test/storage.zig +29 -8
  74. package/src/tigerbeetle/src/tigerbeetle.zig +14 -16
  75. package/src/tigerbeetle/src/unit_tests.zig +7 -0
  76. package/src/tigerbeetle/src/vopr.zig +494 -0
  77. package/src/tigerbeetle/src/vopr_hub/README.md +58 -0
  78. package/src/tigerbeetle/src/vopr_hub/SETUP.md +199 -0
  79. package/src/tigerbeetle/src/vopr_hub/go.mod +3 -0
  80. package/src/tigerbeetle/src/vopr_hub/main.go +1022 -0
  81. package/src/tigerbeetle/src/vopr_hub/scheduler/go.mod +3 -0
  82. package/src/tigerbeetle/src/vopr_hub/scheduler/main.go +403 -0
  83. package/src/tigerbeetle/src/vsr/client.zig +13 -0
  84. package/src/tigerbeetle/src/vsr/journal.zig +16 -13
  85. package/src/tigerbeetle/src/vsr/replica.zig +924 -491
  86. package/src/tigerbeetle/src/vsr/superblock.zig +55 -37
  87. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -10
  88. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +2 -2
  89. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +18 -3
  90. package/src/tigerbeetle/src/vsr.zig +75 -55
  91. package/src/tigerbeetle/scripts/vopr.bat +0 -48
  92. package/src/tigerbeetle/scripts/vopr.sh +0 -33
@@ -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 "$(dirname "$0")/../.."
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
+ "
@@ -12,7 +12,9 @@ const IO = @import("io.zig").IO;
12
12
  const Storage = @import("storage.zig").Storage;
13
13
  const MessagePool = @import("message_pool.zig").MessagePool;
14
14
  const MessageBus = @import("message_bus.zig").MessageBusClient;
15
- const StateMachine = @import("state_machine.zig").StateMachineType(Storage);
15
+ const StateMachine = @import("state_machine.zig").StateMachineType(Storage, .{
16
+ .message_body_size_max = config.message_body_size_max,
17
+ });
16
18
  const RingBuffer = @import("ring_buffer.zig").RingBuffer;
17
19
 
18
20
  const vsr = @import("vsr.zig");
@@ -99,7 +101,7 @@ pub fn main() !void {
99
101
  // Pre-allocate a million transfers:
100
102
  const transfers = try allocator.alloc(tb.Transfer, transfers_max);
101
103
  defer allocator.free(transfers);
102
-
104
+
103
105
  for (transfers) |*transfer, index| {
104
106
  transfer.* = .{
105
107
  .id = index,
@@ -105,7 +105,7 @@ fn run_benchmark(comptime layout: Layout, blob: []u8, random: *std.rand.Random)
105
105
  const target = value_picker[v % value_picker.len];
106
106
  const page = &pages[page_index];
107
107
  const bounds = Eytzinger.search_values(K, V, V.key_compare, &page.keys, &page.values, target);
108
- const hit = bounds[binary_search(K, V, V.key_from_value, V.key_compare, bounds, target)];
108
+ const hit = bounds[binary_search(K, V, V.key_from_value, V.key_compare, bounds, target, .{})];
109
109
 
110
110
  assert(hit.key == target);
111
111
  if (i % pages.len == 0) v += 1;
@@ -136,7 +136,7 @@ fn run_benchmark(comptime layout: Layout, blob: []u8, random: *std.rand.Random)
136
136
  while (i < layout.searches) : (i += 1) {
137
137
  const target = value_picker[v % value_picker.len];
138
138
  const page = &pages[page_picker[i % page_picker.len]];
139
- const hit = page.values[binary_search(K, V, V.key_from_value, V.key_compare, page.values[0..], target)];
139
+ const hit = page.values[binary_search(K, V, V.key_from_value, V.key_compare, page.values[0..], target, .{})];
140
140
 
141
141
  assert(hit.key == target);
142
142
  if (i % pages.len == 0) v += 1;
@@ -307,7 +307,7 @@ fn binary_search_keys(
307
307
  assert(keys.len == layout.keys_count);
308
308
  assert(values.len == layout.values_count);
309
309
 
310
- const key_index = binary_search(Key, Key, V.key_from_key, compare_keys, keys, key);
310
+ const key_index = binary_search(Key, Key, V.key_from_key, compare_keys, keys, key, .{});
311
311
  const key_stride = layout.values_count / layout.keys_count;
312
312
  const high = key_index * key_stride;
313
313
  if (key_index < keys.len and keys[key_index] == key) {
@@ -98,7 +98,8 @@ pub fn ThreadType(
98
98
  errdefer self.allocator.free(self.packets);
99
99
 
100
100
  log.debug("init: parsing vsr addresses.", .{});
101
- self.addresses = vsr.parse_addresses(self.allocator, addresses) catch |err| {
101
+ const address_limit = std.mem.count(u8, addresses, ",") + 1;
102
+ self.addresses = vsr.parse_addresses(self.allocator, addresses, address_limit) catch |err| {
102
103
  log.err("failed to parse addresses: {}.", .{err});
103
104
  return Error.InvalidAddress;
104
105
  };
@@ -236,6 +237,8 @@ pub fn ThreadType(
236
237
  while (self.available_messages > 0) {
237
238
  const packet = pending.pop() orelse self.submitted.pop() orelse break;
238
239
  const message = self.client.get_message();
240
+ defer self.client.unref(message);
241
+
239
242
  self.available_messages -= 1;
240
243
  self.request(packet, message);
241
244
  }
@@ -244,19 +247,19 @@ pub fn ThreadType(
244
247
  fn request(self: *Self, packet: *Packet, message: *Message) void {
245
248
  // Get the size of each request structure in the packet.data
246
249
  const request_size: usize = operation_size_of(packet.operation) orelse {
247
- return self.on_complete(packet, message, error.InvalidOperation);
250
+ return self.on_complete(packet, error.InvalidOperation);
248
251
  };
249
252
 
250
253
  // Make sure the packet.data size is correct.
251
254
  const readable = packet.data[0..packet.data_size];
252
255
  if (readable.len == 0 or readable.len % request_size != 0) {
253
- return self.on_complete(packet, message, error.InvalidDataSize);
256
+ return self.on_complete(packet, error.InvalidDataSize);
254
257
  }
255
258
 
256
259
  // Make sure the packet.data wouldn't overflow a message.
257
260
  const writable = message.buffer[@sizeOf(Header)..];
258
261
  if (readable.len > writable.len) {
259
- return self.on_complete(packet, message, error.TooMuchData);
262
+ return self.on_complete(packet, error.TooMuchData);
260
263
  }
261
264
 
262
265
  // Write the packet data to the message
@@ -286,9 +289,8 @@ pub fn ThreadType(
286
289
  const self = user_data.self;
287
290
  const packet = user_data.packet;
288
291
 
289
- // Complete the packet without a message as it's already unref()'s by the Client.
290
292
  assert(packet.operation == @enumToInt(op));
291
- self.on_complete(packet, null, results);
293
+ self.on_complete(packet, results);
292
294
  }
293
295
 
294
296
  const PacketError = Client.Error || error{
@@ -300,11 +302,8 @@ pub fn ThreadType(
300
302
  fn on_complete(
301
303
  self: *Self,
302
304
  packet: *Packet,
303
- message: ?*Message,
304
305
  result: PacketError![]const u8,
305
306
  ) void {
306
- // Mark the message as completed
307
- if (message) |m| self.client.unref(m);
308
307
  assert(self.available_messages < message_pool.messages_max_client);
309
308
  self.available_messages += 1;
310
309
 
@@ -7,7 +7,6 @@
7
7
 
8
8
  typedef __uint128_t tb_uint128_t;
9
9
 
10
-
11
10
  typedef enum TB_ACCOUNT_FLAGS {
12
11
  TB_ACCOUNT_LINKED = 1 << 0,
13
12
  TB_ACCOUNT_DEBITS_MUST_NOT_EXCEED_CREDITS = 1 << 1,
@@ -18,20 +17,21 @@ typedef struct tb_account_t {
18
17
  tb_uint128_t id;
19
18
  tb_uint128_t user_data;
20
19
  uint8_t reserved[48];
21
- uint16_t unit;
20
+ uint32_t ledger;
22
21
  uint16_t code;
23
- uint32_t flags;
24
- uint64_t debits_reserved;
25
- uint64_t debits_accepted;
26
- uint64_t credits_reserved;
27
- uint64_t credits_accepted;
22
+ uint16_t flags;
23
+ uint64_t debits_pending;
24
+ uint64_t debits_posted;
25
+ uint64_t credits_pending;
26
+ uint64_t credits_posted;
28
27
  uint64_t timestamp;
29
28
  } tb_account_t;
30
29
 
31
30
  typedef enum TB_TRANSFER_FLAGS {
32
31
  TB_TRANSFER_LINKED = 1 << 0,
33
- TB_TRANSFER_TWO_PHASE_COMMIT = 1 << 1,
34
- TB_TRANSFER_CONDITION = 1 << 2,
32
+ TB_TRANSFER_PENDING = 1 << 1,
33
+ TB_TRANSFER_POST_PENDING_TRANSFER = 1 << 2,
34
+ TB_TRANSFER_VOID_PENDING_TRANSFER = 1 << 3,
35
35
  } TB_TRANSFER_FLAGS;
36
36
 
37
37
  typedef struct tb_transfer_t {
@@ -39,111 +39,131 @@ typedef struct tb_transfer_t {
39
39
  tb_uint128_t debit_account_id;
40
40
  tb_uint128_t credit_account_id;
41
41
  tb_uint128_t user_data;
42
- uint8_t reserved[32];
42
+ tb_uint128_t reserved;
43
+ tb_uint128_t pending_id;
43
44
  uint64_t timeout;
44
- uint32_t code;
45
- uint32_t flags;
45
+ uint32_t ledger;
46
+ uint16_t code;
47
+ uint16_t flags;
46
48
  uint64_t amount;
47
49
  uint64_t timestamp;
48
50
  } tb_transfer_t;
49
51
 
50
- typedef enum TB_COMMIT_FLAGS {
51
- TB_COMMIT_LINKED = 1 << 0,
52
- TB_COMMIT_REJECT = 1 << 1,
53
- TB_COMMIT_PREIMAGE = 1 << 2,
54
- } TB_COMMIT_FLAGS;
55
-
56
- typedef struct tb_commit_t {
57
- tb_uint128_t id;
58
- uint8_t reserved[32];
59
- uint32_t code;
60
- uint32_t flags;
61
- uint64_t timestamp;
62
- } tb_commit_t;
63
-
64
52
  typedef enum TB_CREATE_ACCOUNT_RESULT {
65
53
  TB_CREATE_ACCOUNT_OK,
66
54
  TB_CREATE_ACCOUNT_LINKED_EVENT_FAILED,
67
- TB_CREATE_ACCOUNT_EXISTS,
55
+ TB_CREATE_ACCOUNT_LINKED_EVENT_CHAIN_OPEN,
56
+
57
+ TB_CREATE_ACCOUNT_RESERVED_FLAG,
58
+ TB_CREATE_ACCOUNT_RESERVED_FIELD,
59
+
60
+ TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_ZERO,
61
+ TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_INT_MAX,
62
+ TB_CREATE_ACCOUNT_LEDGER_MUST_NOT_BE_ZERO,
63
+ TB_CREATE_ACCOUNT_CODE_MUST_NOT_BE_ZERO,
64
+ TB_CREATE_ACCOUNT_DEBITS_PENDING_MUST_BE_ZERO,
65
+ TB_CREATE_ACCOUNT_DEBITS_POSTED_MUST_BE_ZERO,
66
+ TB_CREATE_ACCOUNT_CREDITS_PENDING_MUST_BE_ZERO,
67
+ TB_CREATE_ACCOUNT_CREDITS_POSTED_MUST_BE_ZERO,
68
+
69
+ TB_CREATE_ACCOUNT_MUTUALLY_EXCLUSIVE_FLAGS,
70
+
71
+ TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_FLAGS,
68
72
  TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA,
69
- TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_RESERVED_FIELD,
70
- TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_UNIT,
73
+ TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_LEDGER,
71
74
  TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_CODE,
72
- TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_FLAGS,
73
- TB_CREATE_ACCOUNT_EXCEEDS_CREDITS,
74
- TB_CREATE_ACCOUNT_EXCEEDS_DEBITS,
75
- TB_CREATE_ACCOUNT_RESERVE_FAILED,
76
- TB_CREATE_ACCOUNT_RESERVE_FLAG_PADDING
75
+ TB_CREATE_ACCOUNT_EXISTS
77
76
  } TB_CREATE_ACCOUNT_RESULT;
78
77
 
79
78
  typedef enum TB_CREATE_TRANSFER_RESULT {
80
79
  TB_CREATE_TRANSFER_OK,
81
80
  TB_CREATE_TRANSFER_LINKED_EVENT_FAILED,
82
- TB_CREATE_TRANSFER_EXISTS,
81
+ TB_CREATE_TRANSFER_LINKED_EVENT_CHAIN_OPEN,
82
+
83
+ TB_CREATE_TRANSFER_RESERVED_FLAG,
84
+ TB_CREATE_TRANSFER_RESERVED_FIELD,
85
+
86
+ TB_CREATE_TRANSFER_ID_MUST_NOT_BE_ZERO,
87
+ TB_CREATE_TRANSFER_ID_MUST_NOT_BE_INT_MAX,
88
+ TB_CREATE_TRANSFER_DEBIT_ACCOUNT_ID_MUST_NOT_BE_ZERO,
89
+ TB_CREATE_TRANSFER_DEBIT_ACCOUNT_ID_MUST_NOT_BE_INT_MAX,
90
+ TB_CREATE_TRANSFER_CREDIT_ACCOUNT_ID_MUST_NOT_BE_ZERO,
91
+ TB_CREATE_TRANSFER_CREDIT_ACCOUNT_ID_MUST_NOT_BE_INT_MAX,
92
+ TB_CREATE_TRANSFER_ACCOUNTS_MUST_BE_DIFFERENT,
93
+
94
+ TB_CREATE_TRANSFER_PENDING_ID_MUST_BE_ZERO,
95
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_MUST_TIMEOUT,
96
+
97
+ TB_CREATE_TRANSFER_LEDGER_MUST_NOT_BE_ZERO,
98
+ TB_CREATE_TRANSFER_CODE_MUST_NOT_BE_ZERO,
99
+ TB_CREATE_TRANSFER_AMOUNT_MUST_NOT_BE_ZERO,
100
+
101
+ TB_CREATE_TRANSFER_DEBIT_ACCOUNT_NOT_FOUND,
102
+ TB_CREATE_TRANSFER_CREDIT_ACCOUNT_NOT_FOUND,
103
+
104
+ TB_CREATE_TRANSFER_ACCOUNTS_MUST_HAVE_THE_SAME_LEDGER,
105
+ TB_CREATE_TRANSFER_TRANSFER_MUST_HAVE_THE_SAME_LEDGER_AS_ACCOUNTS,
106
+
107
+ TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_FLAGS,
83
108
  TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_DEBIT_ACCOUNT_ID,
84
109
  TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_CREDIT_ACCOUNT_ID,
85
110
  TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_USER_DATA,
86
- TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_RESERVED_FIELD,
111
+ TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_PENDING_ID,
112
+ TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_TIMEOUT,
87
113
  TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_CODE,
88
114
  TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_AMOUNT,
89
- TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_TIMEOUT,
90
- TB_CREATE_TRANSFER_EXISTS_WITH_DIFFERENT_FLAGS,
91
- TB_CREATE_TRANSFER_EXISTS_AND_ALREADY_COMMITTED_AND_ACCEPTED,
92
- TB_CREATE_TRANSFER_EXISTS_AND_ALREADY_COMMITTED_AND_REJECTED,
93
- TB_CREATE_TRANSFER_RESERVED_FIELD,
94
- TB_CREATE_TRANSFER_RESERVED_FLAG_PADDING,
95
- TB_CREATE_TRANSFER_DEBIT_ACCOUNT_NOT_FOUND,
96
- TB_CREATE_TRANSFER_CREDIT_ACCOUNT_NOT_FOUND,
97
- TB_CREATE_TRANSFER_ACCOUNTS_ARE_THE_SAME,
98
- TB_CREATE_TRANSFER_ACCOUNTS_HAVE_DIFFERENT_UNITS,
99
- TB_CREATE_TRANSFER_AMOUNT_IS_ZERO,
115
+ TB_CREATE_TRANSFER_EXISTS,
116
+
117
+ TB_CREATE_TRANSFER_OVERFLOWS_DEBITS_PENDING,
118
+ TB_CREATE_TRANSFER_OVERFLOWS_CREDITS_PENDING,
119
+ TB_CREATE_TRANSFER_OVERFLOWS_DEBITS_POSTED,
120
+ TB_CREATE_TRANSFER_OVERFLOWS_CREDITS_POSTED,
121
+ TB_CREATE_TRANSFER_OVERFLOWS_DEBITS,
122
+ TB_CREATE_TRANSFER_OVERFLOWS_CREDITS,
123
+
100
124
  TB_CREATE_TRANSFER_EXCEEDS_CREDITS,
101
125
  TB_CREATE_TRANSFER_EXCEEDS_DEBITS,
102
- TB_CREATE_TRANSFER_TWO_PHASE_COMMIT_MUST_TIMEOUT,
103
- TB_CREATE_TRANSFER_TIMEOUT_RESERVED_FOR_TWO_PHASE_COMMIT
104
- } TB_CREATE_TRANSFER_RESULT;
105
126
 
106
- typedef enum TB_COMMIT_TRANSFER_RESULT {
107
- TB_COMMIT_TRANSFER_OK,
108
- TB_COMMIT_TRANSFER_LINKED_EVENT_FAILED,
109
- TB_COMMIT_TRANSFER_RESERVE_FAILED,
110
- TB_COMMIT_TRANSFER_RESERVE_FLAG_PADDING,
111
- TB_COMMIT_TRANSFER_NOT_FOUND,
112
- TB_COMMIT_TRANSFER_NOT_TWO_PHASE_COMMIT,
113
- TB_COMMIT_TRANSFER_EXPIRED,
114
- TB_COMMIT_TRANSFER_ALREADY_COMMITTED,
115
- TB_COMMIT_TRANSFER_ALREADY_COMMITTED_BUT_ACCEPTED,
116
- TB_COMMIT_TRANSFER_ALREADY_COMMITTED_BUT_REJECTED,
117
- TB_COMMIT_TRANSFER_DEBIT_ACCOUNT_NOT_FOUND,
118
- TB_COMMIT_TRANSFER_CREDIT_ACCOUNT_NOT_FOUND,
119
- TB_COMMIT_TRANSFER_EXCEEDS_CREDITS,
120
- TB_COMMIT_TRANSFER_EXCEEDS_DEBITS,
121
- TB_COMMIT_TRANSFER_CONDITION_REQUIRES_PREIMAGE,
122
- TB_COMMIT_TRANSFER_PREIMAGE_REQUIRES_CONDITION,
123
- TB_COMMIT_TRANSFER_PREIMAGE_INVALID
124
- } TB_COMMIT_TRANSFER_RESULT;
127
+ TB_CREATE_TRANSFER_CANNOT_POST_AND_VOID_PENDING_TRANSFER,
128
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_CANNOT_POST_OR_VOID_ANOTHER,
129
+ TB_CREATE_TRANSFER_TIMEOUT_RESERVED_FOR_PENDING_TRANSFER,
130
+
131
+ TB_CREATE_TRANSFER_PENDING_ID_MUST_NOT_BE_ZERO,
132
+ TB_CREATE_TRANSFER_PENDING_ID_MUST_NOT_BE_INT_MAX,
133
+ TB_CREATE_TRANSFER_PENDING_ID_MUST_BE_DIFFERENT,
134
+
135
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_NOT_FOUND,
136
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_NOT_PENDING,
137
+
138
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_HAS_DIFFERENT_DEBIT_ACCOUNT_ID,
139
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_HAS_DIFFERENT_CREDIT_ACCOUNT_ID,
140
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_HAS_DIFFERENT_LEDGER,
141
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_HAS_DIFFERENT_CODE,
142
+
143
+ TB_CREATE_TRANSFER_EXCEEDS_PENDING_TRANSFER_AMOUNT,
144
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_HAS_DIFFERENT_AMOUNT,
145
+
146
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_ALREADY_POSTED,
147
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_ALREADY_VOIDED,
148
+
149
+ TB_CREATE_TRANSFER_PENDING_TRANSFER_EXPIRED,
150
+ } TB_CREATE_TRANSFER_RESULT;
125
151
 
126
152
  typedef struct tb_create_accounts_result_t {
127
153
  uint32_t index;
128
- TB_CREATE_ACCOUNT_RESULT result;
154
+ uint32_t result;
129
155
  } tb_create_accounts_result_t;
130
156
 
131
157
  typedef struct tb_create_transfers_result_t {
132
158
  uint32_t index;
133
- TB_CREATE_TRANSFER_RESULT result;
159
+ uint32_t result;
134
160
  } tb_create_transfers_result_t;
135
161
 
136
- typedef struct tb_commit_transfers_result_t {
137
- uint32_t index;
138
- TB_COMMIT_TRANSFER_RESULT result;
139
- } tb_commit_transfers_result_t;
140
-
141
162
  typedef enum TB_OPERATION {
142
163
  TB_OP_CREATE_ACCOUNTS = 3,
143
164
  TB_OP_CREATE_TRANSFERS = 4,
144
- TB_OP_COMMIT_TRANSFERS = 5,
145
- TB_OP_LOOKUP_ACCOUNTS = 6,
146
- TB_OP_LOOKUP_TRANSFERS = 7
165
+ TB_OP_LOOKUP_ACCOUNTS = 5,
166
+ TB_OP_LOOKUP_TRANSFERS = 6
147
167
  } TB_OPERATION;
148
168
 
149
169
  typedef enum TB_PACKET_STATUS {
@@ -198,4 +218,4 @@ void tb_client_deinit(
198
218
  tb_client_t client
199
219
  );
200
220
 
201
- #endif // TB_CLIENT_C
221
+ #endif // TB_CLIENT_C
@@ -35,9 +35,12 @@ fn client_to_context(tb_client: tb_client_t) *ContextImplementation {
35
35
  }
36
36
 
37
37
  const DefaultContext = blk: {
38
+ const config = @import("../config.zig");
38
39
  const Storage = @import("../storage.zig").Storage;
39
40
  const MessageBus = @import("../message_bus.zig").MessageBusClient;
40
- const StateMachine = @import("../state_machine.zig").StateMachineType(Storage);
41
+ const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
42
+ .message_body_size_max = config.message_body_size_max,
43
+ });
41
44
  break :blk ContextType(StateMachine, MessageBus);
42
45
  };
43
46
 
@@ -207,7 +207,7 @@ fn parse_cluster(raw_cluster: []const u8) u32 {
207
207
 
208
208
  /// Parse and allocate the addresses returning a slice into that array.
209
209
  fn parse_addresses(allocator: std.mem.Allocator, raw_addresses: []const u8) []net.Address {
210
- return vsr.parse_addresses(allocator, raw_addresses) catch |err| switch (err) {
210
+ return vsr.parse_addresses(allocator, raw_addresses, config.replicas_max) catch |err| switch (err) {
211
211
  error.AddressHasTrailingComma => fatal("--addresses: invalid trailing comma", .{}),
212
212
  error.AddressLimitExceeded => {
213
213
  fatal("--addresses: too many addresses, at most {d} are allowed", .{
@@ -1,5 +1,7 @@
1
1
  const std = @import("std");
2
2
  const assert = std.debug.assert;
3
+ const tigerbeetle = @import("tigerbeetle.zig");
4
+ const vsr = @import("vsr.zig");
3
5
 
4
6
  const Environment = enum {
5
7
  development,
@@ -9,8 +11,7 @@ const Environment = enum {
9
11
 
10
12
  /// Whether development or production:
11
13
  pub const deployment_environment: Environment =
12
- if (@hasDecl(@import("root"), "deployment_environment")) @import("root").deployment_environment
13
- else .development;
14
+ if (@hasDecl(@import("root"), "deployment_environment")) @import("root").deployment_environment else .development;
14
15
 
15
16
  /// The maximum log level in increasing order of verbosity (emergency=0, debug=3):
16
17
  pub const log_level = 2;
@@ -48,22 +49,22 @@ pub const memory_size_max_default = 1024 * 1024 * 1024;
48
49
 
49
50
  /// The maximum number of accounts to store in memory:
50
51
  /// This impacts the amount of memory allocated at initialization by the server.
51
- pub const accounts_max = switch (deployment_environment) {
52
- .production => 1_000_000,
53
- else => 100_000,
52
+ pub const cache_accounts_max = switch (deployment_environment) {
53
+ .production => 64 * 1024,
54
+ else => 8 * 1024,
54
55
  };
55
56
 
56
57
  /// The maximum number of transfers to store in memory:
57
58
  /// This impacts the amount of memory allocated at initialization by the server.
58
59
  /// We allocate more capacity than the number of transfers for a safe hash table load factor.
59
- pub const transfers_max = switch (deployment_environment) {
60
- .production => 100_000_000,
61
- else => 1_000_000,
60
+ pub const cache_transfers_max = switch (deployment_environment) {
61
+ .production => 1024 * 1024,
62
+ else => 64 * 1024,
62
63
  };
63
64
 
64
65
  /// The maximum number of two-phase transfers to store in memory:
65
66
  /// This impacts the amount of memory allocated at initialization by the server.
66
- pub const transfers_pending_max = transfers_max;
67
+ pub const cache_transfers_pending_max = cache_transfers_max;
67
68
 
68
69
  /// The maximum number of batch entries in the journal file:
69
70
  /// A batch entry may contain many transfers, so this is not a limit on the number of transfers.
@@ -71,7 +72,7 @@ pub const transfers_pending_max = transfers_max;
71
72
  /// These header copies enable us to disentangle corruption from crashes and recover accordingly.
72
73
  pub const journal_slot_count = switch (deployment_environment) {
73
74
  .production => 1024,
74
- else => 128,
75
+ else => 1024,
75
76
  };
76
77
 
77
78
  /// The maximum size of the journal file:
@@ -80,7 +81,7 @@ pub const journal_slot_count = switch (deployment_environment) {
80
81
  /// This enables static allocation of disk space so that appends cannot fail with ENOSPC.
81
82
  /// This also enables us to detect filesystem inode corruption that would change the journal size.
82
83
  // TODO remove this; just allocate a part of the total storage for the journal
83
- pub const journal_size_max = journal_slot_count * (128 + message_size_max);
84
+ pub const journal_size_max = journal_slot_count * (@sizeOf(vsr.Header) + message_size_max);
84
85
 
85
86
  /// The maximum number of connections that can be held open by the server at any time:
86
87
  pub const connections_max = replicas_max + clients_max;
@@ -94,11 +95,22 @@ pub const connections_max = replicas_max + clients_max;
94
95
  /// For a 1 Gbps NIC = 125 MiB/s throughput: 2 MiB / 125 * 1000ms = 16ms for the next request.
95
96
  /// This impacts the amount of memory allocated at initialization by the server.
96
97
  pub const message_size_max = switch (deployment_environment) {
97
- // Use a small message size during the simulator for improved performance.
98
- .simulation => sector_size,
99
- else => 1 * 1024 * 1024
98
+ .simulation => message_size_max_min,
99
+ else => 1 * 1024 * 1024,
100
100
  };
101
101
 
102
+ pub const message_body_size_max = message_size_max - @sizeOf(vsr.Header);
103
+
104
+ /// The smallest possible message_size_max (for use in the simulator to improve performance).
105
+ /// The message body must have room for pipeline_max headers in the DVC.
106
+ const message_size_max_min = std.math.max(
107
+ sector_size,
108
+ std.mem.alignForward(
109
+ @sizeOf(vsr.Header) + pipeline_max * @sizeOf(vsr.Header),
110
+ sector_size,
111
+ ),
112
+ );
113
+
102
114
  /// The maximum number of Viewstamped Replication prepare messages that can be inflight at a time.
103
115
  /// This is immutable once assigned per cluster, as replicas need to know how many operations might
104
116
  /// possibly be uncommitted during a view change, and this must be constant for all replicas.
@@ -235,8 +247,15 @@ pub const block_count_max = @divExact(16 * 1024 * 1024 * 1024 * 1024, block_size
235
247
  pub const lsm_trees = 30;
236
248
 
237
249
  /// The number of levels in an LSM tree.
250
+ /// A higher number of levels increases read amplification, as well as total storage capacity.
238
251
  pub const lsm_levels = 7;
239
252
 
253
+ /// The number of tables at level i (0 ≤ i < lsm_levels) is `pow(lsm_growth_factor, i+1)`.
254
+ /// A higher growth factor increases write amplification (by increasing the number of tables in
255
+ /// level B that overlap a table in level A in a compaction), but decreases read amplification (by
256
+ /// reducing the height of the tree and thus the number of levels that must be probed). Since read
257
+ /// amplification can be optimized more easily (with filters and caching), we target a growth
258
+ /// factor of 8 for lower write amplification rather than the more typical growth factor of 10.
240
259
  pub const lsm_growth_factor = 8;
241
260
 
242
261
  /// The maximum key size for an LSM tree in bytes.
@@ -305,10 +324,10 @@ pub const clock_synchronization_window_max_ms = 20000;
305
324
  pub const verify = true;
306
325
 
307
326
  // TODO Move these to a separate "internal computed constants" file.
308
- pub const journal_size_headers = journal_slot_count * 128; // 128 == @sizeOf(Header)
327
+ pub const journal_size_headers = journal_slot_count * @sizeOf(vsr.Header);
309
328
  pub const journal_size_prepares = journal_slot_count * message_size_max;
310
329
 
311
- // TODO Move these into a separate `config_valid.zig` which we import here:
330
+ // TODO Move these into a separate `config_valid.zig` which we import here:
312
331
  comptime {
313
332
  // vsr.parse_address assumes that config.address/config.port are valid.
314
333
  _ = std.net.Address.parseIp4(address, 0) catch unreachable;
@@ -341,8 +360,21 @@ comptime {
341
360
  assert(message_size_max % sector_size == 0);
342
361
  assert(message_size_max >= sector_size);
343
362
 
363
+ // ManifestLog serializes the level as a u7.
364
+ assert(lsm_levels > 0);
365
+ assert(lsm_levels <= std.math.maxInt(u7));
366
+
367
+ assert(block_size % sector_size == 0);
368
+ assert(lsm_table_size_max % sector_size == 0);
369
+ assert(lsm_table_size_max % block_size == 0);
370
+
344
371
  // The LSM tree uses half-measures to balance compaction.
345
372
  assert(lsm_batch_multiple % 2 == 0);
373
+
374
+ // SetAssociativeCache requires a power-of-two cardinality.
375
+ assert(std.math.isPowerOfTwo(cache_accounts_max));
376
+ assert(std.math.isPowerOfTwo(cache_transfers_max));
377
+ assert(std.math.isPowerOfTwo(cache_transfers_pending_max));
346
378
  }
347
379
 
348
380
  pub const is_32_bit = @sizeOf(usize) == 4; // TODO Return a compile error if we are not 32-bit.
@@ -14,7 +14,9 @@ const IO = @import("io.zig").IO;
14
14
  const Storage = @import("storage.zig").Storage;
15
15
  const MessagePool = @import("message_pool.zig").MessagePool;
16
16
  const MessageBus = @import("message_bus.zig").MessageBusClient;
17
- const StateMachine = @import("state_machine.zig").StateMachineType(Storage);
17
+ const StateMachine = @import("state_machine.zig").StateMachineType(Storage, .{
18
+ .message_body_size_max = config.message_body_size_max,
19
+ });
18
20
 
19
21
  const vsr = @import("vsr.zig");
20
22
  const Header = vsr.Header;
@@ -105,7 +105,7 @@ fn run_benchmark(comptime layout: Layout, blob: []u8, random: *std.rand.Random)
105
105
  const target = value_picker[v % value_picker.len];
106
106
  const page = &pages[page_index];
107
107
  const bounds = Eytzinger.search_values(K, V, V.key_compare, &page.keys, &page.values, target);
108
- const hit = bounds[binary_search(K, V, V.key_from_value, V.key_compare, bounds, target)];
108
+ const hit = bounds[binary_search(K, V, V.key_from_value, V.key_compare, bounds, target, .{})];
109
109
 
110
110
  assert(hit.key == target);
111
111
  if (i % pages.len == 0) v += 1;
@@ -136,7 +136,7 @@ fn run_benchmark(comptime layout: Layout, blob: []u8, random: *std.rand.Random)
136
136
  while (i < layout.searches) : (i += 1) {
137
137
  const target = value_picker[v % value_picker.len];
138
138
  const page = &pages[page_picker[i % page_picker.len]];
139
- const hit = page.values[binary_search(K, V, V.key_from_value, V.key_compare, page.values[0..], target)];
139
+ const hit = page.values[binary_search(K, V, V.key_from_value, V.key_compare, page.values[0..], target, .{})];
140
140
 
141
141
  assert(hit.key == target);
142
142
  if (i % pages.len == 0) v += 1;
@@ -307,7 +307,7 @@ fn binary_search_keys(
307
307
  assert(keys.len == layout.keys_count);
308
308
  assert(values.len == layout.values_count);
309
309
 
310
- const key_index = binary_search(Key, Key, V.key_from_key, compare_keys, keys, key);
310
+ const key_index = binary_search(Key, Key, V.key_from_key, compare_keys, keys, key, .{});
311
311
  const key_stride = layout.values_count / layout.keys_count;
312
312
  const high = key_index * key_stride;
313
313
  if (key_index < keys.len and keys[key_index] == key) {
@@ -26,7 +26,7 @@ pub const IO = struct {
26
26
  const uts = std.os.uname();
27
27
  const release = std.mem.sliceTo(&uts.release, 0);
28
28
  const version = try std.builtin.Version.parse(release);
29
- if (version.major < 5 or version.minor < 5) {
29
+ if (version.order(std.builtin.Version{ .major = 5, .minor = 5 }) == .lt) {
30
30
  @panic("Linux kernel 5.5 or greater is required for io_uring OP_ACCEPT");
31
31
  }
32
32