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.
- 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 +6 -6
- 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/src/benchmark.zig +4 -2
- package/src/tigerbeetle/src/benchmark_array_search.zig +3 -3
- package/src/tigerbeetle/src/c/tb_client/thread.zig +8 -9
- package/src/tigerbeetle/src/c/tb_client.h +100 -80
- package/src/tigerbeetle/src/c/tb_client.zig +4 -1
- package/src/tigerbeetle/src/cli.zig +1 -1
- package/src/tigerbeetle/src/config.zig +48 -16
- package/src/tigerbeetle/src/demo.zig +3 -1
- package/src/tigerbeetle/src/eytzinger_benchmark.zig +3 -3
- package/src/tigerbeetle/src/io/linux.zig +1 -1
- package/src/tigerbeetle/src/lsm/README.md +214 -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 +352 -398
- package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
- package/src/tigerbeetle/src/lsm/forest.zig +21 -447
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +412 -0
- package/src/tigerbeetle/src/lsm/grid.zig +145 -69
- package/src/tigerbeetle/src/lsm/groove.zig +196 -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 +81 -181
- package/src/tigerbeetle/src/lsm/manifest_level.zig +210 -454
- package/src/tigerbeetle/src/lsm/manifest_log.zig +77 -28
- package/src/tigerbeetle/src/lsm/posted_groove.zig +64 -76
- package/src/tigerbeetle/src/lsm/segmented_array.zig +561 -241
- 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 +83 -48
- package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
- package/src/tigerbeetle/src/lsm/table_iterator.zig +25 -14
- package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
- package/src/tigerbeetle/src/lsm/test.zig +49 -55
- package/src/tigerbeetle/src/lsm/tree.zig +407 -402
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +457 -0
- package/src/tigerbeetle/src/main.zig +28 -6
- package/src/tigerbeetle/src/message_bus.zig +2 -2
- package/src/tigerbeetle/src/message_pool.zig +14 -17
- package/src/tigerbeetle/src/simulator.zig +145 -112
- package/src/tigerbeetle/src/state_machine.zig +338 -228
- 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 +819 -0
- package/src/tigerbeetle/src/test/cluster.zig +18 -48
- package/src/tigerbeetle/src/test/conductor.zig +365 -0
- package/src/tigerbeetle/src/test/fuzz.zig +121 -0
- package/src/tigerbeetle/src/test/id.zig +89 -0
- package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
- package/src/tigerbeetle/src/test/state_checker.zig +93 -69
- package/src/tigerbeetle/src/test/state_machine.zig +11 -35
- package/src/tigerbeetle/src/test/storage.zig +29 -8
- package/src/tigerbeetle/src/tigerbeetle.zig +14 -16
- package/src/tigerbeetle/src/unit_tests.zig +7 -0
- package/src/tigerbeetle/src/vopr.zig +494 -0
- package/src/tigerbeetle/src/vopr_hub/README.md +58 -0
- package/src/tigerbeetle/src/vopr_hub/SETUP.md +199 -0
- package/src/tigerbeetle/src/vopr_hub/go.mod +3 -0
- package/src/tigerbeetle/src/vopr_hub/main.go +1022 -0
- package/src/tigerbeetle/src/vopr_hub/scheduler/go.mod +3 -0
- package/src/tigerbeetle/src/vopr_hub/scheduler/main.go +403 -0
- package/src/tigerbeetle/src/vsr/client.zig +13 -0
- package/src/tigerbeetle/src/vsr/journal.zig +16 -13
- package/src/tigerbeetle/src/vsr/replica.zig +924 -491
- package/src/tigerbeetle/src/vsr/superblock.zig +55 -37
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -10
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +2 -2
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +18 -3
- package/src/tigerbeetle/src/vsr.zig +75 -55
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
|
@@ -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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
20
|
+
uint32_t ledger;
|
|
22
21
|
uint16_t code;
|
|
23
|
-
|
|
24
|
-
uint64_t
|
|
25
|
-
uint64_t
|
|
26
|
-
uint64_t
|
|
27
|
-
uint64_t
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
42
|
+
tb_uint128_t reserved;
|
|
43
|
+
tb_uint128_t pending_id;
|
|
43
44
|
uint64_t timeout;
|
|
44
|
-
uint32_t
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
|
52
|
-
.production =>
|
|
53
|
-
else =>
|
|
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
|
|
60
|
-
.production =>
|
|
61
|
-
else =>
|
|
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
|
|
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 =>
|
|
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 * (
|
|
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
|
-
|
|
98
|
-
|
|
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 *
|
|
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
|
-
|
|
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
|
|
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
|
|