tigerbeetle-node 0.11.12 → 0.12.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 +212 -196
- package/dist/bin/aarch64-linux-gnu/client.node +0 -0
- package/dist/bin/aarch64-linux-musl/client.node +0 -0
- package/dist/bin/aarch64-macos/client.node +0 -0
- package/dist/bin/x86_64-linux-gnu/client.node +0 -0
- package/dist/bin/x86_64-linux-musl/client.node +0 -0
- package/dist/bin/x86_64-macos/client.node +0 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package-lock.json +66 -0
- package/package.json +8 -17
- package/src/index.ts +56 -1
- package/src/node.zig +10 -9
- package/dist/.client.node.sha256 +0 -1
- package/scripts/build_lib.sh +0 -61
- package/scripts/download_node_headers.sh +0 -32
- package/src/tigerbeetle/scripts/benchmark.bat +0 -48
- package/src/tigerbeetle/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
- package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -314
- package/src/tigerbeetle/src/config.zig +0 -234
- package/src/tigerbeetle/src/constants.zig +0 -436
- package/src/tigerbeetle/src/ewah.zig +0 -286
- package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
- package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
- package/src/tigerbeetle/src/fifo.zig +0 -120
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -814
- package/src/tigerbeetle/src/io/linux.zig +0 -1062
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1183
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/iops.zig +0 -107
- package/src/tigerbeetle/src/lsm/README.md +0 -308
- package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
- package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
- package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
- package/src/tigerbeetle/src/lsm/direction.zig +0 -11
- package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
- package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
- package/src/tigerbeetle/src/lsm/forest.zig +0 -204
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
- package/src/tigerbeetle/src/lsm/grid.zig +0 -573
- package/src/tigerbeetle/src/lsm/groove.zig +0 -972
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
- package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
- package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
- package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
- package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
- package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
- package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
- package/src/tigerbeetle/src/lsm/table.zig +0 -1031
- package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
- package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
- package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
- package/src/tigerbeetle/src/lsm/test.zig +0 -438
- package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
- package/src/tigerbeetle/src/message_bus.zig +0 -1012
- package/src/tigerbeetle/src/message_pool.zig +0 -156
- package/src/tigerbeetle/src/ring_buffer.zig +0 -399
- package/src/tigerbeetle/src/simulator.zig +0 -569
- package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
- package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
- package/src/tigerbeetle/src/state_machine.zig +0 -1881
- package/src/tigerbeetle/src/static_allocator.zig +0 -65
- package/src/tigerbeetle/src/stdx.zig +0 -162
- package/src/tigerbeetle/src/storage.zig +0 -393
- package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
- package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
- package/src/tigerbeetle/src/testing/cluster.zig +0 -443
- package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
- package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
- package/src/tigerbeetle/src/testing/id.zig +0 -99
- package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
- package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
- package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
- package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
- package/src/tigerbeetle/src/testing/storage.zig +0 -757
- package/src/tigerbeetle/src/testing/table.zig +0 -247
- package/src/tigerbeetle/src/testing/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
- package/src/tigerbeetle/src/time.zig +0 -112
- package/src/tigerbeetle/src/tracer.zig +0 -529
- package/src/tigerbeetle/src/unit_tests.zig +0 -42
- package/src/tigerbeetle/src/vopr.zig +0 -495
- package/src/tigerbeetle/src/vsr/README.md +0 -209
- package/src/tigerbeetle/src/vsr/client.zig +0 -544
- package/src/tigerbeetle/src/vsr/clock.zig +0 -853
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
- package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
- package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
- package/src/tigerbeetle/src/vsr.zig +0 -1352
|
@@ -1,577 +0,0 @@
|
|
|
1
|
-
//! The Auditor constructs the expected state of its corresponding StateMachine from requests and
|
|
2
|
-
//! replies. It validates replies against its local state.
|
|
3
|
-
//!
|
|
4
|
-
//! The Auditor expects replies in ascending commit order.
|
|
5
|
-
const std = @import("std");
|
|
6
|
-
const assert = std.debug.assert;
|
|
7
|
-
const log = std.log.scoped(.test_auditor);
|
|
8
|
-
|
|
9
|
-
const constants = @import("../constants.zig");
|
|
10
|
-
const tb = @import("../tigerbeetle.zig");
|
|
11
|
-
const vsr = @import("../vsr.zig");
|
|
12
|
-
const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
|
|
13
|
-
const IdPermutation = @import("../testing/id.zig").IdPermutation;
|
|
14
|
-
|
|
15
|
-
// TODO(zig) This won't be necessary in Zig 0.10.
|
|
16
|
-
const PriorityQueue = @import("../testing/priority_queue.zig").PriorityQueue;
|
|
17
|
-
const Storage = @import("../testing/storage.zig").Storage;
|
|
18
|
-
const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
|
|
19
|
-
.message_body_size_max = constants.message_body_size_max,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
pub const CreateAccountResultSet = std.enums.EnumSet(tb.CreateAccountResult);
|
|
23
|
-
pub const CreateTransferResultSet = std.enums.EnumSet(tb.CreateTransferResult);
|
|
24
|
-
|
|
25
|
-
/// Batch sizes apply to both `create` and `lookup` operations.
|
|
26
|
-
/// (More ids would fit in the `lookup` request, but then the response wouldn't fit.)
|
|
27
|
-
const accounts_batch_size_max = StateMachine.constants.batch_max.create_accounts;
|
|
28
|
-
const transfers_batch_size_max = StateMachine.constants.batch_max.create_transfers;
|
|
29
|
-
|
|
30
|
-
/// Store expected possible results for an in-flight request.
|
|
31
|
-
/// This reply validation takes advantage of the Workload's additional context about the request.
|
|
32
|
-
const InFlight = union(enum) {
|
|
33
|
-
create_accounts: [accounts_batch_size_max]CreateAccountResultSet,
|
|
34
|
-
create_transfers: [transfers_batch_size_max]CreateTransferResultSet,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const InFlightQueue = std.AutoHashMapUnmanaged(struct {
|
|
38
|
-
client_index: usize,
|
|
39
|
-
/// This index corresponds to Auditor.creates_sent/Auditor.creates_delivered.
|
|
40
|
-
client_request: usize,
|
|
41
|
-
}, InFlight);
|
|
42
|
-
|
|
43
|
-
const PendingTransfer = struct {
|
|
44
|
-
amount: u64,
|
|
45
|
-
debit_account_index: usize,
|
|
46
|
-
credit_account_index: usize,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const PendingExpiry = struct {
|
|
50
|
-
transfer: u128,
|
|
51
|
-
timestamp: u64,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const PendingExpiryQueue = PriorityQueue(PendingExpiry, void, struct {
|
|
55
|
-
/// Order by ascending timestamp.
|
|
56
|
-
fn compare(_: void, a: PendingExpiry, b: PendingExpiry) std.math.Order {
|
|
57
|
-
return std.math.order(a.timestamp, b.timestamp);
|
|
58
|
-
}
|
|
59
|
-
}.compare);
|
|
60
|
-
|
|
61
|
-
pub const AccountingAuditor = struct {
|
|
62
|
-
const Self = @This();
|
|
63
|
-
|
|
64
|
-
pub const Options = struct {
|
|
65
|
-
accounts_max: usize,
|
|
66
|
-
account_id_permutation: IdPermutation,
|
|
67
|
-
client_count: usize,
|
|
68
|
-
|
|
69
|
-
/// This is the maximum number of pending transfers, not counting those that have timed
|
|
70
|
-
/// out.
|
|
71
|
-
///
|
|
72
|
-
/// NOTE: Transfers that have posted/voided successfully (or not) that have _not_ yet
|
|
73
|
-
/// reached their expiry are still included in this count — see `pending_expiries`.
|
|
74
|
-
transfers_pending_max: usize,
|
|
75
|
-
|
|
76
|
-
/// From the Auditor's point-of-view, all stalled requests are still in-flight, even if
|
|
77
|
-
/// their reply has actually arrived at the ReplySequence.
|
|
78
|
-
///
|
|
79
|
-
/// A request stops being "in-flight" when `on_reply` is called.
|
|
80
|
-
///
|
|
81
|
-
/// This should equal the ReplySequence's `stalled_queue_capacity`.
|
|
82
|
-
in_flight_max: usize,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
random: std.rand.Random,
|
|
86
|
-
options: Options,
|
|
87
|
-
|
|
88
|
-
/// The timestamp of the last processed reply.
|
|
89
|
-
timestamp: u64 = 0,
|
|
90
|
-
|
|
91
|
-
/// The account configuration. Balances are in sync with the remote StateMachine for a
|
|
92
|
-
/// given commit (double-double entry accounting).
|
|
93
|
-
accounts: []tb.Account,
|
|
94
|
-
|
|
95
|
-
/// Set to true when `create_accounts` returns `.ok` for an account.
|
|
96
|
-
accounts_created: []bool,
|
|
97
|
-
|
|
98
|
-
/// Map pending transfers to the (pending) amount and accounts.
|
|
99
|
-
///
|
|
100
|
-
/// * Added in `on_create_transfers` for pending transfers.
|
|
101
|
-
/// * Removed after a transfer is posted, voided, or timed out.
|
|
102
|
-
///
|
|
103
|
-
/// All entries in `pending_transfers` have a corresponding entry in `pending_expiries`.
|
|
104
|
-
pending_transfers: std.AutoHashMapUnmanaged(u128, PendingTransfer),
|
|
105
|
-
|
|
106
|
-
/// After a transfer is posted/voided, the entry in `pending_expiries` is untouched.
|
|
107
|
-
/// The timeout will not impact account balances (because the `pending_transfers` entry is
|
|
108
|
-
/// removed), but until timeout the transfer still counts against `transfers_pending_max`.
|
|
109
|
-
pending_expiries: PendingExpiryQueue,
|
|
110
|
-
|
|
111
|
-
/// Track the expected result of the in-flight request for each client.
|
|
112
|
-
/// Each member queue corresponds to entries of the client's request queue, but omits
|
|
113
|
-
/// `register` messages.
|
|
114
|
-
in_flight: InFlightQueue,
|
|
115
|
-
|
|
116
|
-
/// The number of `create_accounts`/`create_transfers` sent, per client. Keyed by client index.
|
|
117
|
-
creates_sent: []usize,
|
|
118
|
-
|
|
119
|
-
/// The number of `create_accounts`/`create_transfers` delivered (i.e. replies received),
|
|
120
|
-
/// per client. Keyed by client index.
|
|
121
|
-
creates_delivered: []usize,
|
|
122
|
-
|
|
123
|
-
pub fn init(allocator: std.mem.Allocator, random: std.rand.Random, options: Options) !Self {
|
|
124
|
-
assert(options.accounts_max >= 2);
|
|
125
|
-
assert(options.client_count > 0);
|
|
126
|
-
|
|
127
|
-
const accounts = try allocator.alloc(tb.Account, options.accounts_max);
|
|
128
|
-
errdefer allocator.free(accounts);
|
|
129
|
-
std.mem.set(tb.Account, accounts, undefined);
|
|
130
|
-
|
|
131
|
-
const accounts_created = try allocator.alloc(bool, options.accounts_max);
|
|
132
|
-
errdefer allocator.free(accounts_created);
|
|
133
|
-
std.mem.set(bool, accounts_created, false);
|
|
134
|
-
|
|
135
|
-
var pending_transfers = std.AutoHashMapUnmanaged(u128, PendingTransfer){};
|
|
136
|
-
errdefer pending_transfers.deinit(allocator);
|
|
137
|
-
try pending_transfers.ensureTotalCapacity(allocator, @intCast(u32, options.transfers_pending_max));
|
|
138
|
-
|
|
139
|
-
var pending_expiries = PendingExpiryQueue.init(allocator, {});
|
|
140
|
-
errdefer pending_expiries.deinit();
|
|
141
|
-
try pending_expiries.ensureTotalCapacity(options.transfers_pending_max);
|
|
142
|
-
|
|
143
|
-
var in_flight = InFlightQueue{};
|
|
144
|
-
errdefer in_flight.deinit(allocator);
|
|
145
|
-
try in_flight.ensureTotalCapacity(allocator, @intCast(u32, options.in_flight_max));
|
|
146
|
-
|
|
147
|
-
var creates_sent = try allocator.alloc(usize, options.client_count);
|
|
148
|
-
errdefer allocator.free(creates_sent);
|
|
149
|
-
std.mem.set(usize, creates_sent, 0);
|
|
150
|
-
|
|
151
|
-
var creates_delivered = try allocator.alloc(usize, options.client_count);
|
|
152
|
-
errdefer allocator.free(creates_delivered);
|
|
153
|
-
std.mem.set(usize, creates_delivered, 0);
|
|
154
|
-
|
|
155
|
-
return Self{
|
|
156
|
-
.random = random,
|
|
157
|
-
.options = options,
|
|
158
|
-
.accounts = accounts,
|
|
159
|
-
.accounts_created = accounts_created,
|
|
160
|
-
.pending_transfers = pending_transfers,
|
|
161
|
-
.pending_expiries = pending_expiries,
|
|
162
|
-
.in_flight = in_flight,
|
|
163
|
-
.creates_sent = creates_sent,
|
|
164
|
-
.creates_delivered = creates_delivered,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
|
169
|
-
allocator.free(self.accounts);
|
|
170
|
-
allocator.free(self.accounts_created);
|
|
171
|
-
self.pending_transfers.deinit(allocator);
|
|
172
|
-
self.pending_expiries.deinit();
|
|
173
|
-
self.in_flight.deinit(allocator);
|
|
174
|
-
allocator.free(self.creates_sent);
|
|
175
|
-
allocator.free(self.creates_delivered);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
pub fn done(self: *const Self) bool {
|
|
179
|
-
if (self.in_flight.count() != 0) return false;
|
|
180
|
-
|
|
181
|
-
for (self.creates_sent) |sent, client_index| {
|
|
182
|
-
if (sent != self.creates_delivered[client_index]) return false;
|
|
183
|
-
}
|
|
184
|
-
// Don't check pending_transfers; the workload might not have posted/voided every transfer.
|
|
185
|
-
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
pub fn expect_create_accounts(self: *Self, client_index: usize) []CreateAccountResultSet {
|
|
190
|
-
const result = self.in_flight.getOrPutAssumeCapacity(.{
|
|
191
|
-
.client_index = client_index,
|
|
192
|
-
.client_request = self.creates_sent[client_index],
|
|
193
|
-
});
|
|
194
|
-
assert(!result.found_existing);
|
|
195
|
-
|
|
196
|
-
self.creates_sent[client_index] += 1;
|
|
197
|
-
result.value_ptr.* = .{ .create_accounts = undefined };
|
|
198
|
-
return result.value_ptr.*.create_accounts[0..];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
pub fn expect_create_transfers(self: *Self, client_index: usize) []CreateTransferResultSet {
|
|
202
|
-
const result = self.in_flight.getOrPutAssumeCapacity(.{
|
|
203
|
-
.client_index = client_index,
|
|
204
|
-
.client_request = self.creates_sent[client_index],
|
|
205
|
-
});
|
|
206
|
-
assert(!result.found_existing);
|
|
207
|
-
|
|
208
|
-
self.creates_sent[client_index] += 1;
|
|
209
|
-
result.value_ptr.* = .{ .create_transfers = undefined };
|
|
210
|
-
return result.value_ptr.*.create_transfers[0..];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/// Expire pending tranfers that have not been posted or voided.
|
|
214
|
-
fn tick_to_timestamp(self: *Self, timestamp: u64) void {
|
|
215
|
-
assert(self.timestamp < timestamp);
|
|
216
|
-
|
|
217
|
-
while (self.pending_expiries.peek()) |expiration| {
|
|
218
|
-
if (timestamp < expiration.timestamp) break;
|
|
219
|
-
defer _ = self.pending_expiries.remove();
|
|
220
|
-
|
|
221
|
-
// Ignore the transfer if it was already posted/voided.
|
|
222
|
-
const pending_transfer = self.pending_transfers.get(expiration.transfer) orelse continue;
|
|
223
|
-
assert(self.pending_transfers.remove(expiration.transfer));
|
|
224
|
-
assert(self.accounts_created[pending_transfer.debit_account_index]);
|
|
225
|
-
assert(self.accounts_created[pending_transfer.credit_account_index]);
|
|
226
|
-
|
|
227
|
-
const dr = &self.accounts[pending_transfer.debit_account_index];
|
|
228
|
-
const cr = &self.accounts[pending_transfer.credit_account_index];
|
|
229
|
-
dr.debits_pending -= pending_transfer.amount;
|
|
230
|
-
cr.credits_pending -= pending_transfer.amount;
|
|
231
|
-
|
|
232
|
-
assert(!dr.debits_exceed_credits(0));
|
|
233
|
-
assert(!dr.credits_exceed_debits(0));
|
|
234
|
-
assert(!cr.debits_exceed_credits(0));
|
|
235
|
-
assert(!cr.credits_exceed_debits(0));
|
|
236
|
-
|
|
237
|
-
// TODO(Timeouts): When timeouts are implemented in the StateMachine, remove this panic.
|
|
238
|
-
// In the mean time, if the VOPR hits this error, just ignore it.
|
|
239
|
-
@panic("tick_to_timestamp: timeouts are not implemented in the StateMachine yet");
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
self.timestamp = timestamp;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
pub fn on_create_accounts(
|
|
246
|
-
self: *Self,
|
|
247
|
-
client_index: usize,
|
|
248
|
-
timestamp: u64,
|
|
249
|
-
accounts: []const tb.Account,
|
|
250
|
-
results: []const tb.CreateAccountsResult,
|
|
251
|
-
) void {
|
|
252
|
-
assert(accounts.len >= results.len);
|
|
253
|
-
assert(self.timestamp < timestamp);
|
|
254
|
-
defer assert(self.timestamp == timestamp);
|
|
255
|
-
|
|
256
|
-
const results_expect = self.take_in_flight(client_index).create_accounts;
|
|
257
|
-
var results_iterator = IteratorForCreate(tb.CreateAccountsResult).init(results);
|
|
258
|
-
defer assert(results_iterator.results.len == 0);
|
|
259
|
-
|
|
260
|
-
for (accounts) |*account, i| {
|
|
261
|
-
const account_timestamp = timestamp - accounts.len + i + 1;
|
|
262
|
-
// TODO Should this be at the end of the loop? (If a timeout & post land on the same
|
|
263
|
-
// timestamp, which wins?)
|
|
264
|
-
self.tick_to_timestamp(account_timestamp);
|
|
265
|
-
|
|
266
|
-
const result_actual = results_iterator.take(i) orelse .ok;
|
|
267
|
-
if (!results_expect[i].contains(result_actual)) {
|
|
268
|
-
log.err("on_create_accounts: account={} expect={} result={}", .{
|
|
269
|
-
account.*,
|
|
270
|
-
results_expect[i],
|
|
271
|
-
result_actual,
|
|
272
|
-
});
|
|
273
|
-
@panic("on_create_accounts: unexpected result");
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const account_index = self.account_id_to_index(account.id);
|
|
277
|
-
if (result_actual == .ok) {
|
|
278
|
-
assert(!self.accounts_created[account_index]);
|
|
279
|
-
self.accounts_created[account_index] = true;
|
|
280
|
-
self.accounts[account_index] = account.*;
|
|
281
|
-
self.accounts[account_index].timestamp = account_timestamp;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (account_index >= self.accounts.len) {
|
|
285
|
-
assert(result_actual != .ok);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (accounts.len == 0) {
|
|
290
|
-
self.tick_to_timestamp(timestamp);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
pub fn on_create_transfers(
|
|
295
|
-
self: *Self,
|
|
296
|
-
client_index: usize,
|
|
297
|
-
timestamp: u64,
|
|
298
|
-
transfers: []const tb.Transfer,
|
|
299
|
-
results: []const tb.CreateTransfersResult,
|
|
300
|
-
) void {
|
|
301
|
-
assert(transfers.len >= results.len);
|
|
302
|
-
assert(self.timestamp < timestamp);
|
|
303
|
-
defer assert(self.timestamp == timestamp);
|
|
304
|
-
|
|
305
|
-
const results_expect = self.take_in_flight(client_index).create_transfers;
|
|
306
|
-
var results_iterator = IteratorForCreate(tb.CreateTransfersResult).init(results);
|
|
307
|
-
defer assert(results_iterator.results.len == 0);
|
|
308
|
-
|
|
309
|
-
for (transfers) |*transfer, i| {
|
|
310
|
-
const transfer_timestamp = timestamp - transfers.len + i + 1;
|
|
311
|
-
// TODO Should this be deferrred to the end of the loop? (If a timeout & post land on
|
|
312
|
-
// the same timestamp, which wins?)
|
|
313
|
-
self.tick_to_timestamp(transfer_timestamp);
|
|
314
|
-
|
|
315
|
-
const result_actual = results_iterator.take(i) orelse .ok;
|
|
316
|
-
if (!results_expect[i].contains(result_actual)) {
|
|
317
|
-
log.err("on_create_transfers: transfer={} expect={} result={}", .{
|
|
318
|
-
transfer.*,
|
|
319
|
-
results_expect[i],
|
|
320
|
-
result_actual,
|
|
321
|
-
});
|
|
322
|
-
@panic("on_create_transfers: unexpected result");
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (result_actual != .ok) continue;
|
|
326
|
-
|
|
327
|
-
if (transfer.flags.post_pending_transfer or transfer.flags.void_pending_transfer) {
|
|
328
|
-
if (self.pending_transfers.get(transfer.pending_id)) |p| {
|
|
329
|
-
const dr = &self.accounts[p.debit_account_index];
|
|
330
|
-
const cr = &self.accounts[p.credit_account_index];
|
|
331
|
-
assert(self.accounts_created[p.debit_account_index]);
|
|
332
|
-
assert(self.accounts_created[p.credit_account_index]);
|
|
333
|
-
|
|
334
|
-
assert(self.pending_transfers.remove(transfer.pending_id));
|
|
335
|
-
// The transfer may still be in `pending_expiries` — removal would be O(n),
|
|
336
|
-
// so don't bother.
|
|
337
|
-
|
|
338
|
-
dr.debits_pending -= p.amount;
|
|
339
|
-
cr.credits_pending -= p.amount;
|
|
340
|
-
if (transfer.flags.post_pending_transfer) {
|
|
341
|
-
const amount = if (transfer.amount > 0) transfer.amount else p.amount;
|
|
342
|
-
dr.debits_posted += amount;
|
|
343
|
-
cr.credits_posted += amount;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
assert(!dr.debits_exceed_credits(0));
|
|
347
|
-
assert(!dr.credits_exceed_debits(0));
|
|
348
|
-
assert(!cr.debits_exceed_credits(0));
|
|
349
|
-
assert(!cr.credits_exceed_debits(0));
|
|
350
|
-
} else {
|
|
351
|
-
// The transfer was already completed by another post/void or timeout.
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
const dr_index = self.account_id_to_index(transfer.debit_account_id);
|
|
355
|
-
const cr_index = self.account_id_to_index(transfer.credit_account_id);
|
|
356
|
-
const dr = &self.accounts[dr_index];
|
|
357
|
-
const cr = &self.accounts[cr_index];
|
|
358
|
-
assert(self.accounts_created[dr_index]);
|
|
359
|
-
assert(self.accounts_created[cr_index]);
|
|
360
|
-
|
|
361
|
-
if (transfer.flags.pending) {
|
|
362
|
-
self.pending_transfers.putAssumeCapacity(transfer.id, .{
|
|
363
|
-
.amount = transfer.amount,
|
|
364
|
-
.debit_account_index = dr_index,
|
|
365
|
-
.credit_account_index = cr_index,
|
|
366
|
-
});
|
|
367
|
-
self.pending_expiries.add(.{
|
|
368
|
-
.transfer = transfer.id,
|
|
369
|
-
.timestamp = transfer_timestamp + transfer.timeout,
|
|
370
|
-
}) catch unreachable;
|
|
371
|
-
// PriorityQueue lacks an "unmanaged" API, so verify that the workload hasn't
|
|
372
|
-
// created more pending transfers than permitted.
|
|
373
|
-
assert(self.pending_expiries.len <= self.options.transfers_pending_max);
|
|
374
|
-
|
|
375
|
-
dr.debits_pending += transfer.amount;
|
|
376
|
-
cr.credits_pending += transfer.amount;
|
|
377
|
-
} else {
|
|
378
|
-
dr.debits_posted += transfer.amount;
|
|
379
|
-
cr.credits_posted += transfer.amount;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
assert(!dr.debits_exceed_credits(0));
|
|
383
|
-
assert(!dr.credits_exceed_debits(0));
|
|
384
|
-
assert(!cr.debits_exceed_credits(0));
|
|
385
|
-
assert(!cr.credits_exceed_debits(0));
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (transfers.len == 0) {
|
|
390
|
-
self.tick_to_timestamp(timestamp);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
pub fn on_lookup_accounts(
|
|
395
|
-
self: *Self,
|
|
396
|
-
client_index: usize,
|
|
397
|
-
timestamp: u64,
|
|
398
|
-
ids: []const u128,
|
|
399
|
-
results: []const tb.Account,
|
|
400
|
-
) void {
|
|
401
|
-
_ = client_index;
|
|
402
|
-
assert(ids.len >= results.len);
|
|
403
|
-
assert(self.timestamp < timestamp);
|
|
404
|
-
defer assert(self.timestamp == timestamp);
|
|
405
|
-
|
|
406
|
-
var results_iterator = IteratorForLookup(tb.Account).init(results);
|
|
407
|
-
defer assert(results_iterator.results.len == 0);
|
|
408
|
-
|
|
409
|
-
for (ids) |account_id| {
|
|
410
|
-
const account_index = self.account_id_to_index(account_id);
|
|
411
|
-
const account_lookup = results_iterator.take(account_id);
|
|
412
|
-
|
|
413
|
-
if (account_index < self.accounts.len and self.accounts_created[account_index]) {
|
|
414
|
-
// If this assertion fails, `lookup_accounts` didn't return an account when it
|
|
415
|
-
// should have.
|
|
416
|
-
assert(account_lookup != null);
|
|
417
|
-
assert(!account_lookup.?.debits_exceed_credits(0));
|
|
418
|
-
assert(!account_lookup.?.credits_exceed_debits(0));
|
|
419
|
-
|
|
420
|
-
const account_expect = &self.accounts[account_index];
|
|
421
|
-
if (!std.mem.eql(
|
|
422
|
-
u8,
|
|
423
|
-
std.mem.asBytes(account_lookup.?),
|
|
424
|
-
std.mem.asBytes(account_expect),
|
|
425
|
-
)) {
|
|
426
|
-
log.err("on_lookup_accounts: account data mismatch " ++
|
|
427
|
-
"account_id={} expect={} lookup={}", .{
|
|
428
|
-
account_id,
|
|
429
|
-
account_expect,
|
|
430
|
-
account_lookup.?,
|
|
431
|
-
});
|
|
432
|
-
@panic("on_lookup_accounts: account data mismatch");
|
|
433
|
-
}
|
|
434
|
-
} else {
|
|
435
|
-
// If this assertion fails, `lookup_accounts` returned an account when it shouldn't.
|
|
436
|
-
assert(account_lookup == null);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
self.tick_to_timestamp(timestamp);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/// Most `lookup_transfers` validation is handled by Workload.
|
|
443
|
-
/// (Workload has more context around transfers, so it can be much stricter.)
|
|
444
|
-
pub fn on_lookup_transfers(
|
|
445
|
-
self: *Self,
|
|
446
|
-
client_index: usize,
|
|
447
|
-
timestamp: u64,
|
|
448
|
-
ids: []const u128,
|
|
449
|
-
results: []const tb.Transfer,
|
|
450
|
-
) void {
|
|
451
|
-
_ = client_index;
|
|
452
|
-
assert(ids.len >= results.len);
|
|
453
|
-
assert(self.timestamp < timestamp);
|
|
454
|
-
defer assert(self.timestamp == timestamp);
|
|
455
|
-
|
|
456
|
-
var results_iterator = IteratorForLookup(tb.Transfer).init(results);
|
|
457
|
-
defer assert(results_iterator.results.len == 0);
|
|
458
|
-
|
|
459
|
-
for (ids) |id| {
|
|
460
|
-
const result = results_iterator.take(id);
|
|
461
|
-
assert(result == null or result.?.id == id);
|
|
462
|
-
}
|
|
463
|
-
self.tick_to_timestamp(timestamp);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/// Returns a random account matching the given criteria.
|
|
467
|
-
/// Returns null when no account matches the given criteria.
|
|
468
|
-
pub fn pick_account(
|
|
469
|
-
self: *const Self,
|
|
470
|
-
match: struct {
|
|
471
|
-
/// Whether the account is known to be created
|
|
472
|
-
/// (we have received an `ok` for the respective `create_accounts`).
|
|
473
|
-
created: ?bool,
|
|
474
|
-
debits_must_not_exceed_credits: ?bool,
|
|
475
|
-
credits_must_not_exceed_debits: ?bool,
|
|
476
|
-
/// Don't match this account.
|
|
477
|
-
exclude: ?u128 = null,
|
|
478
|
-
},
|
|
479
|
-
) ?*const tb.Account {
|
|
480
|
-
const offset = self.random.uintLessThanBiased(usize, self.accounts.len);
|
|
481
|
-
var i: usize = 0;
|
|
482
|
-
// Iterate `accounts`, starting from a random offset.
|
|
483
|
-
while (i < self.accounts.len) : (i += 1) {
|
|
484
|
-
const account_index = (offset + i) % self.accounts.len;
|
|
485
|
-
if (match.created) |expect_created| {
|
|
486
|
-
if (self.accounts_created[account_index]) {
|
|
487
|
-
if (!expect_created) continue;
|
|
488
|
-
} else {
|
|
489
|
-
if (expect_created) continue;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const account = &self.accounts[account_index];
|
|
494
|
-
if (match.debits_must_not_exceed_credits) |b| {
|
|
495
|
-
if (account.flags.debits_must_not_exceed_credits != b) continue;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (match.credits_must_not_exceed_debits) |b| {
|
|
499
|
-
if (account.flags.credits_must_not_exceed_debits != b) continue;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (match.exclude) |exclude_id| {
|
|
503
|
-
if (account.id == exclude_id) continue;
|
|
504
|
-
}
|
|
505
|
-
return account;
|
|
506
|
-
}
|
|
507
|
-
return null;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
pub fn account_id_to_index(self: *const Self, id: u128) usize {
|
|
511
|
-
// -1 because id=0 is not valid, so index=0→id=1.
|
|
512
|
-
return @intCast(usize, self.options.account_id_permutation.decode(id)) - 1;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
pub fn account_index_to_id(self: *const Self, index: usize) u128 {
|
|
516
|
-
// +1 so that index=0 is encoded as a valid id.
|
|
517
|
-
return self.options.account_id_permutation.encode(index + 1);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
fn take_in_flight(self: *Self, client_index: usize) InFlight {
|
|
521
|
-
const key = .{
|
|
522
|
-
.client_index = client_index,
|
|
523
|
-
.client_request = self.creates_delivered[client_index],
|
|
524
|
-
};
|
|
525
|
-
self.creates_delivered[client_index] += 1;
|
|
526
|
-
|
|
527
|
-
const in_flight = self.in_flight.get(key).?;
|
|
528
|
-
assert(self.in_flight.remove(key));
|
|
529
|
-
return in_flight;
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
pub fn IteratorForCreate(comptime Result: type) type {
|
|
534
|
-
assert(Result == tb.CreateAccountsResult or Result == tb.CreateTransfersResult);
|
|
535
|
-
|
|
536
|
-
return struct {
|
|
537
|
-
const Self = @This();
|
|
538
|
-
|
|
539
|
-
results: []const Result,
|
|
540
|
-
|
|
541
|
-
pub fn init(results: []const Result) Self {
|
|
542
|
-
return .{ .results = results };
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
pub fn take(self: *Self, event_index: usize) ?std.meta.fieldInfo(Result, .result).field_type {
|
|
546
|
-
if (self.results.len > 0 and self.results[0].index == event_index) {
|
|
547
|
-
defer self.results = self.results[1..];
|
|
548
|
-
return self.results[0].result;
|
|
549
|
-
} else {
|
|
550
|
-
return null;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
pub fn IteratorForLookup(comptime Result: type) type {
|
|
557
|
-
assert(Result == tb.Account or Result == tb.Transfer);
|
|
558
|
-
|
|
559
|
-
return struct {
|
|
560
|
-
const Self = @This();
|
|
561
|
-
|
|
562
|
-
results: []const Result,
|
|
563
|
-
|
|
564
|
-
pub fn init(results: []const Result) Self {
|
|
565
|
-
return .{ .results = results };
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
pub fn take(self: *Self, id: u128) ?*const Result {
|
|
569
|
-
if (self.results.len > 0 and self.results[0].id == id) {
|
|
570
|
-
defer self.results = self.results[1..];
|
|
571
|
-
return &self.results[0];
|
|
572
|
-
} else {
|
|
573
|
-
return null;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
}
|