tigerbeetle-node 0.5.1 → 0.8.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 +88 -69
- package/dist/benchmark.js +96 -94
- package/dist/benchmark.js.map +1 -1
- package/dist/index.d.ts +82 -82
- package/dist/index.js +74 -93
- package/dist/index.js.map +1 -1
- package/dist/test.js +134 -111
- package/dist/test.js.map +1 -1
- package/package.json +3 -2
- package/src/benchmark.ts +114 -118
- package/src/index.ts +102 -111
- package/src/node.zig +54 -62
- package/src/test.ts +146 -125
- package/src/tigerbeetle/scripts/benchmark.bat +46 -0
- package/src/tigerbeetle/scripts/benchmark.sh +5 -0
- package/src/tigerbeetle/scripts/install_zig.bat +2 -2
- package/src/tigerbeetle/scripts/install_zig.sh +3 -3
- package/src/tigerbeetle/scripts/vopr.sh +2 -2
- package/src/tigerbeetle/src/benchmark.zig +65 -102
- package/src/tigerbeetle/src/cli.zig +39 -18
- package/src/tigerbeetle/src/config.zig +27 -12
- package/src/tigerbeetle/src/demo.zig +1 -14
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +10 -10
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +3 -1
- package/src/tigerbeetle/src/{demo_04_create_transfers_two_phase_commit.zig → demo_04_create_pending_transfers.zig} +12 -6
- package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +37 -0
- package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +24 -0
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +1 -1
- package/src/tigerbeetle/src/io/benchmark.zig +24 -49
- package/src/tigerbeetle/src/io/darwin.zig +175 -44
- package/src/tigerbeetle/src/io/linux.zig +177 -72
- package/src/tigerbeetle/src/io/test.zig +61 -39
- package/src/tigerbeetle/src/io/windows.zig +1161 -0
- package/src/tigerbeetle/src/io.zig +2 -0
- package/src/tigerbeetle/src/main.zig +14 -9
- package/src/tigerbeetle/src/message_bus.zig +56 -74
- package/src/tigerbeetle/src/message_pool.zig +63 -57
- package/src/tigerbeetle/src/ring_buffer.zig +7 -0
- package/src/tigerbeetle/src/simulator.zig +4 -4
- package/src/tigerbeetle/src/state_machine.zig +1804 -797
- package/src/tigerbeetle/src/storage.zig +0 -230
- package/src/tigerbeetle/src/test/cluster.zig +3 -6
- package/src/tigerbeetle/src/test/message_bus.zig +4 -3
- package/src/tigerbeetle/src/test/network.zig +13 -16
- package/src/tigerbeetle/src/test/state_checker.zig +3 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +108 -101
- package/src/tigerbeetle/src/time.zig +58 -11
- package/src/tigerbeetle/src/vsr/client.zig +18 -32
- package/src/tigerbeetle/src/vsr/clock.zig +1 -1
- package/src/tigerbeetle/src/vsr/journal.zig +2 -6
- package/src/tigerbeetle/src/vsr/replica.zig +152 -175
- package/src/tigerbeetle/src/vsr.zig +263 -5
- package/src/translate.zig +10 -0
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +0 -23
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +0 -17
- package/src/tigerbeetle/src/format_test.zig +0 -69
|
@@ -6,18 +6,18 @@ pub const config = @import("config.zig");
|
|
|
6
6
|
|
|
7
7
|
pub const Account = packed struct {
|
|
8
8
|
id: u128,
|
|
9
|
-
/// Opaque third-party identifier to link this account (many-to-one) to an external entity
|
|
9
|
+
/// Opaque third-party identifier to link this account (many-to-one) to an external entity.
|
|
10
10
|
user_data: u128,
|
|
11
|
-
/// Reserved for accounting policy primitives
|
|
11
|
+
/// Reserved for accounting policy primitives.
|
|
12
12
|
reserved: [48]u8,
|
|
13
|
-
|
|
14
|
-
/// A chart of accounts code describing the type of account (e.g. clearing, settlement)
|
|
13
|
+
ledger: u32,
|
|
14
|
+
/// A chart of accounts code describing the type of account (e.g. clearing, settlement).
|
|
15
15
|
code: u16,
|
|
16
16
|
flags: AccountFlags,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
debits_pending: u64,
|
|
18
|
+
debits_posted: u64,
|
|
19
|
+
credits_pending: u64,
|
|
20
|
+
credits_posted: u64,
|
|
21
21
|
timestamp: u64 = 0,
|
|
22
22
|
|
|
23
23
|
comptime {
|
|
@@ -26,12 +26,12 @@ pub const Account = packed struct {
|
|
|
26
26
|
|
|
27
27
|
pub fn debits_exceed_credits(self: *const Account, amount: u64) bool {
|
|
28
28
|
return (self.flags.debits_must_not_exceed_credits and
|
|
29
|
-
self.
|
|
29
|
+
self.debits_pending + self.debits_posted + amount > self.credits_posted);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
pub fn credits_exceed_debits(self: *const Account, amount: u64) bool {
|
|
33
33
|
return (self.flags.credits_must_not_exceed_debits and
|
|
34
|
-
self.
|
|
34
|
+
self.credits_pending + self.credits_posted + amount > self.debits_posted);
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -49,10 +49,10 @@ pub const AccountFlags = packed struct {
|
|
|
49
49
|
linked: bool = false,
|
|
50
50
|
debits_must_not_exceed_credits: bool = false,
|
|
51
51
|
credits_must_not_exceed_debits: bool = false,
|
|
52
|
-
padding:
|
|
52
|
+
padding: u13 = 0,
|
|
53
53
|
|
|
54
54
|
comptime {
|
|
55
|
-
assert(@sizeOf(AccountFlags) == @sizeOf(
|
|
55
|
+
assert(@sizeOf(AccountFlags) == @sizeOf(u16));
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -60,13 +60,16 @@ pub const Transfer = packed struct {
|
|
|
60
60
|
id: u128,
|
|
61
61
|
debit_account_id: u128,
|
|
62
62
|
credit_account_id: u128,
|
|
63
|
-
/// Opaque third-party identifier to link this transfer (many-to-one) to an external entity
|
|
63
|
+
/// Opaque third-party identifier to link this transfer (many-to-one) to an external entity.
|
|
64
64
|
user_data: u128,
|
|
65
|
-
/// Reserved for accounting policy primitives
|
|
66
|
-
reserved:
|
|
65
|
+
/// Reserved for accounting policy primitives.
|
|
66
|
+
reserved: u128,
|
|
67
|
+
/// If this transfer will post or void a pending transfer, the id of that pending transfer.
|
|
68
|
+
pending_id: u128,
|
|
67
69
|
timeout: u64,
|
|
68
|
-
|
|
69
|
-
code
|
|
70
|
+
ledger: u32,
|
|
71
|
+
/// A chart of accounts code describing the reason for the transfer (e.g. deposit, settlement).
|
|
72
|
+
code: u16,
|
|
70
73
|
flags: TransferFlags,
|
|
71
74
|
amount: u64,
|
|
72
75
|
timestamp: u64 = 0,
|
|
@@ -78,102 +81,113 @@ pub const Transfer = packed struct {
|
|
|
78
81
|
|
|
79
82
|
pub const TransferFlags = packed struct {
|
|
80
83
|
linked: bool = false,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
pending: bool = false,
|
|
85
|
+
post_pending_transfer: bool = false,
|
|
86
|
+
void_pending_transfer: bool = false,
|
|
87
|
+
padding: u12 = 0,
|
|
84
88
|
|
|
85
89
|
comptime {
|
|
86
|
-
assert(@sizeOf(TransferFlags) == @sizeOf(
|
|
90
|
+
assert(@sizeOf(TransferFlags) == @sizeOf(u16));
|
|
87
91
|
}
|
|
88
92
|
};
|
|
89
93
|
|
|
90
|
-
pub const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
reserved: [32]u8,
|
|
94
|
-
/// A chart of accounts code describing the reason for the accept/reject:
|
|
95
|
-
code: u32,
|
|
96
|
-
flags: CommitFlags,
|
|
97
|
-
timestamp: u64 = 0,
|
|
94
|
+
pub const CreateAccountResult = enum(u32) {
|
|
95
|
+
ok,
|
|
96
|
+
linked_event_failed,
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
};
|
|
98
|
+
reserved_flag,
|
|
99
|
+
reserved_field,
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
preimage: bool = false,
|
|
108
|
-
padding: u29 = 0,
|
|
101
|
+
id_must_not_be_zero,
|
|
102
|
+
ledger_must_not_be_zero,
|
|
103
|
+
code_must_not_be_zero,
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
mutually_exclusive_flags,
|
|
106
|
+
|
|
107
|
+
overflows_debits,
|
|
108
|
+
overflows_credits,
|
|
114
109
|
|
|
115
|
-
pub const CreateAccountResult = enum(u32) {
|
|
116
|
-
ok,
|
|
117
|
-
linked_event_failed,
|
|
118
|
-
exists,
|
|
119
|
-
exists_with_different_user_data,
|
|
120
|
-
exists_with_different_reserved_field,
|
|
121
|
-
exists_with_different_unit,
|
|
122
|
-
exists_with_different_code,
|
|
123
|
-
exists_with_different_flags,
|
|
124
110
|
exceeds_credits,
|
|
125
111
|
exceeds_debits,
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
|
|
113
|
+
exists_with_different_flags,
|
|
114
|
+
exists_with_different_user_data,
|
|
115
|
+
exists_with_different_ledger,
|
|
116
|
+
exists_with_different_code,
|
|
117
|
+
exists_with_different_debits_pending,
|
|
118
|
+
exists_with_different_debits_posted,
|
|
119
|
+
exists_with_different_credits_pending,
|
|
120
|
+
exists_with_different_credits_posted,
|
|
121
|
+
exists,
|
|
128
122
|
};
|
|
129
123
|
|
|
130
124
|
pub const CreateTransferResult = enum(u32) {
|
|
131
125
|
ok,
|
|
132
126
|
linked_event_failed,
|
|
133
|
-
|
|
127
|
+
|
|
128
|
+
reserved_flag,
|
|
129
|
+
reserved_field,
|
|
130
|
+
|
|
131
|
+
id_must_not_be_zero,
|
|
132
|
+
debit_account_id_must_not_be_zero,
|
|
133
|
+
credit_account_id_must_not_be_zero,
|
|
134
|
+
accounts_must_be_different,
|
|
135
|
+
|
|
136
|
+
pending_id_must_be_zero,
|
|
137
|
+
pending_transfer_must_timeout,
|
|
138
|
+
|
|
139
|
+
ledger_must_not_be_zero,
|
|
140
|
+
code_must_not_be_zero,
|
|
141
|
+
amount_must_not_be_zero,
|
|
142
|
+
|
|
143
|
+
debit_account_not_found,
|
|
144
|
+
credit_account_not_found,
|
|
145
|
+
|
|
146
|
+
accounts_must_have_the_same_ledger,
|
|
147
|
+
transfer_must_have_the_same_ledger_as_accounts,
|
|
148
|
+
|
|
149
|
+
exists_with_different_flags,
|
|
134
150
|
exists_with_different_debit_account_id,
|
|
135
151
|
exists_with_different_credit_account_id,
|
|
136
152
|
exists_with_different_user_data,
|
|
137
|
-
|
|
153
|
+
exists_with_different_pending_id,
|
|
154
|
+
exists_with_different_timeout,
|
|
138
155
|
exists_with_different_code,
|
|
139
156
|
exists_with_different_amount,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
accounts_are_the_same,
|
|
149
|
-
accounts_have_different_units,
|
|
150
|
-
amount_is_zero,
|
|
151
|
-
exceeds_credits,
|
|
152
|
-
exceeds_debits,
|
|
153
|
-
two_phase_commit_must_timeout,
|
|
154
|
-
timeout_reserved_for_two_phase_commit,
|
|
155
|
-
};
|
|
157
|
+
exists,
|
|
158
|
+
|
|
159
|
+
overflows_debits_pending,
|
|
160
|
+
overflows_credits_pending,
|
|
161
|
+
overflows_debits_posted,
|
|
162
|
+
overflows_credits_posted,
|
|
163
|
+
overflows_debits,
|
|
164
|
+
overflows_credits,
|
|
156
165
|
|
|
157
|
-
pub const CommitTransferResult = enum(u32) {
|
|
158
|
-
ok,
|
|
159
|
-
linked_event_failed,
|
|
160
|
-
reserved_field,
|
|
161
|
-
reserved_flag_padding,
|
|
162
|
-
transfer_not_found,
|
|
163
|
-
transfer_not_two_phase_commit,
|
|
164
|
-
transfer_expired,
|
|
165
|
-
already_committed,
|
|
166
|
-
already_committed_but_accepted,
|
|
167
|
-
already_committed_but_rejected,
|
|
168
|
-
debit_account_not_found,
|
|
169
|
-
credit_account_not_found,
|
|
170
|
-
debit_amount_was_not_reserved,
|
|
171
|
-
credit_amount_was_not_reserved,
|
|
172
166
|
exceeds_credits,
|
|
173
167
|
exceeds_debits,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
168
|
+
|
|
169
|
+
cannot_post_and_void_pending_transfer,
|
|
170
|
+
pending_transfer_cannot_post_or_void_another,
|
|
171
|
+
timeout_reserved_for_pending_transfer,
|
|
172
|
+
|
|
173
|
+
pending_id_must_not_be_zero,
|
|
174
|
+
pending_id_must_be_different,
|
|
175
|
+
|
|
176
|
+
pending_transfer_not_found,
|
|
177
|
+
pending_transfer_not_pending,
|
|
178
|
+
|
|
179
|
+
pending_transfer_has_different_debit_account_id,
|
|
180
|
+
pending_transfer_has_different_credit_account_id,
|
|
181
|
+
pending_transfer_has_different_ledger,
|
|
182
|
+
pending_transfer_has_different_code,
|
|
183
|
+
|
|
184
|
+
exceeds_pending_transfer_amount,
|
|
185
|
+
pending_transfer_has_different_amount,
|
|
186
|
+
|
|
187
|
+
pending_transfer_already_posted,
|
|
188
|
+
pending_transfer_already_voided,
|
|
189
|
+
|
|
190
|
+
pending_transfer_expired,
|
|
177
191
|
};
|
|
178
192
|
|
|
179
193
|
pub const CreateAccountsResult = packed struct {
|
|
@@ -194,22 +208,15 @@ pub const CreateTransfersResult = packed struct {
|
|
|
194
208
|
}
|
|
195
209
|
};
|
|
196
210
|
|
|
197
|
-
pub const CommitTransfersResult = packed struct {
|
|
198
|
-
index: u32,
|
|
199
|
-
result: CommitTransferResult,
|
|
200
|
-
|
|
201
|
-
comptime {
|
|
202
|
-
assert(@sizeOf(CommitTransfersResult) == 8);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
211
|
comptime {
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
const target = builtin.target;
|
|
213
|
+
|
|
214
|
+
if (target.os.tag != .linux and !target.isDarwin() and target.os.tag != .windows) {
|
|
215
|
+
@compileError("linux, windows or macos is required for io");
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
// We require little-endian architectures everywhere for efficient network deserialization:
|
|
212
|
-
if (
|
|
219
|
+
if (target.cpu.arch.endian() != .Little) {
|
|
213
220
|
@compileError("big-endian systems not supported");
|
|
214
221
|
}
|
|
215
222
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const is_darwin = builtin.target.isDarwin();
|
|
5
3
|
const config = @import("./config.zig");
|
|
6
4
|
|
|
5
|
+
const os = std.os;
|
|
6
|
+
const assert = std.debug.assert;
|
|
7
|
+
const is_darwin = builtin.target.os.tag.isDarwin();
|
|
8
|
+
const is_windows = builtin.target.os.tag == .windows;
|
|
9
|
+
|
|
7
10
|
pub const Time = struct {
|
|
8
11
|
const Self = @This();
|
|
9
12
|
|
|
@@ -19,19 +22,44 @@ pub const Time = struct {
|
|
|
19
22
|
/// system administrator manually changes the clock.
|
|
20
23
|
pub fn monotonic(self: *Self) u64 {
|
|
21
24
|
const m = blk: {
|
|
25
|
+
// Uses QueryPerformanceCounter() on windows due to it being the highest precision timer
|
|
26
|
+
// available while also accounting for time spent suspended by default:
|
|
27
|
+
// https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryunbiasedinterrupttime#remarks
|
|
28
|
+
if (is_windows) {
|
|
29
|
+
// QPF need not be globally cached either as it ends up being a load from read-only
|
|
30
|
+
// memory mapped to all processed by the kernel called KUSER_SHARED_DATA (See "QpcFrequency")
|
|
31
|
+
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
|
|
32
|
+
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
|
|
33
|
+
const qpc = os.windows.QueryPerformanceCounter();
|
|
34
|
+
const qpf = os.windows.QueryPerformanceFrequency();
|
|
35
|
+
|
|
36
|
+
// 10Mhz (1 qpc tick every 100ns) is a common QPF on modern systems.
|
|
37
|
+
// We can optimize towards this by converting to ns via a single multiply.
|
|
38
|
+
// https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
|
|
39
|
+
const common_qpf = 10_000_000;
|
|
40
|
+
if (qpf == common_qpf) break :blk qpc * (std.time.ns_per_s / common_qpf);
|
|
41
|
+
|
|
42
|
+
// Convert qpc to nanos using fixed point to avoid expensive extra divs and overflow.
|
|
43
|
+
const scale = (std.time.ns_per_s << 32) / qpf;
|
|
44
|
+
break :blk @truncate(u64, (@as(u96, qpc) * scale) >> 32);
|
|
45
|
+
}
|
|
46
|
+
|
|
22
47
|
// Uses mach_continuous_time() instead of mach_absolute_time() as it counts while suspended.
|
|
23
48
|
// https://developer.apple.com/documentation/kernel/1646199-mach_continuous_time
|
|
24
49
|
// https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.c.auto.html
|
|
25
50
|
if (is_darwin) {
|
|
26
51
|
const darwin = struct {
|
|
27
|
-
const mach_timebase_info_t =
|
|
28
|
-
extern "c" fn mach_timebase_info(info: *mach_timebase_info_t)
|
|
52
|
+
const mach_timebase_info_t = os.darwin.mach_timebase_info_data;
|
|
53
|
+
extern "c" fn mach_timebase_info(info: *mach_timebase_info_t) os.darwin.kern_return_t;
|
|
29
54
|
extern "c" fn mach_continuous_time() u64;
|
|
30
55
|
};
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
// mach_timebase_info() called through libc already does global caching for us
|
|
58
|
+
// https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/wrappers/mach_timebase_info.c.auto.html
|
|
33
59
|
var info: darwin.mach_timebase_info_t = undefined;
|
|
34
60
|
if (darwin.mach_timebase_info(&info) != 0) @panic("mach_timebase_info() failed");
|
|
61
|
+
|
|
62
|
+
const now = darwin.mach_continuous_time();
|
|
35
63
|
return (now * info.numer) / info.denom;
|
|
36
64
|
}
|
|
37
65
|
|
|
@@ -40,8 +68,8 @@ pub const Time = struct {
|
|
|
40
68
|
// CLOCK_BOOTTIME is the same as CLOCK_MONOTONIC but includes elapsed time during a suspend.
|
|
41
69
|
// For more detail and why CLOCK_MONOTONIC_RAW is even worse than CLOCK_MONOTONIC,
|
|
42
70
|
// see https://github.com/ziglang/zig/pull/933#discussion_r656021295.
|
|
43
|
-
var ts:
|
|
44
|
-
|
|
71
|
+
var ts: os.timespec = undefined;
|
|
72
|
+
os.clock_gettime(os.CLOCK.BOOTTIME, &ts) catch @panic("CLOCK_BOOTTIME required");
|
|
45
73
|
break :blk @intCast(u64, ts.tv_sec) * std.time.ns_per_s + @intCast(u64, ts.tv_nsec);
|
|
46
74
|
};
|
|
47
75
|
|
|
@@ -54,11 +82,30 @@ pub const Time = struct {
|
|
|
54
82
|
/// A timestamp to measure real (i.e. wall clock) time, meaningful across systems, and reboots.
|
|
55
83
|
/// This clock is affected by discontinuous jumps in the system time.
|
|
56
84
|
pub fn realtime(_: *Self) i64 {
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
if (is_windows) {
|
|
86
|
+
const kernel32 = struct {
|
|
87
|
+
extern "kernel32" fn GetSystemTimePreciseAsFileTime(
|
|
88
|
+
lpFileTime: *os.windows.FILETIME,
|
|
89
|
+
) callconv(os.windows.WINAPI) void;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
var ft: os.windows.FILETIME = undefined;
|
|
93
|
+
kernel32.GetSystemTimePreciseAsFileTime(&ft);
|
|
94
|
+
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
|
95
|
+
|
|
96
|
+
// FileTime is in units of 100 nanoseconds
|
|
97
|
+
// and uses the NTFS/Windows epoch of 1601-01-01 instead of Unix Epoch 1970-01-01.
|
|
98
|
+
const epoch_adjust = std.time.epoch.windows * (std.time.ns_per_s / 100);
|
|
99
|
+
return (@bitCast(i64, ft64) + epoch_adjust) * 100;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (is_darwin) {
|
|
103
|
+
// macos has supported clock_gettime() since 10.12:
|
|
104
|
+
// https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.3.auto.html
|
|
105
|
+
}
|
|
59
106
|
|
|
60
|
-
var ts:
|
|
61
|
-
|
|
107
|
+
var ts: os.timespec = undefined;
|
|
108
|
+
os.clock_gettime(os.CLOCK.REALTIME, &ts) catch unreachable;
|
|
62
109
|
return @as(i64, ts.tv_sec) * std.time.ns_per_s + ts.tv_nsec;
|
|
63
110
|
}
|
|
64
111
|
|
|
@@ -7,7 +7,8 @@ const vsr = @import("../vsr.zig");
|
|
|
7
7
|
const Header = vsr.Header;
|
|
8
8
|
|
|
9
9
|
const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
|
|
10
|
-
const
|
|
10
|
+
const message_pool = @import("../message_pool.zig");
|
|
11
|
+
const Message = message_pool.MessagePool.Message;
|
|
11
12
|
|
|
12
13
|
const log = std.log.scoped(.client);
|
|
13
14
|
|
|
@@ -66,8 +67,7 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
66
67
|
|
|
67
68
|
/// A client is allowed at most one inflight request at a time at the protocol layer.
|
|
68
69
|
/// We therefore queue any further concurrent requests made by the application layer.
|
|
69
|
-
|
|
70
|
-
request_queue: RingBuffer(Request, config.message_bus_messages_max - 1) = .{},
|
|
70
|
+
request_queue: RingBuffer(Request, config.client_request_queue_max) = .{},
|
|
71
71
|
|
|
72
72
|
/// The number of ticks without a reply before the client resends the inflight request.
|
|
73
73
|
/// Dynamically adjusted as a function of recent request round-trip time.
|
|
@@ -188,25 +188,25 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
188
188
|
@tagName(operation),
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
if (self.request_queue.full()) {
|
|
192
|
+
callback(user_data, operation, error.TooManyOutstandingRequests);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
191
196
|
const was_empty = self.request_queue.empty();
|
|
192
197
|
|
|
193
|
-
self.request_queue.
|
|
198
|
+
self.request_queue.push_assume_capacity(.{
|
|
194
199
|
.user_data = user_data,
|
|
195
200
|
.callback = callback,
|
|
196
201
|
.message = message.ref(),
|
|
197
|
-
})
|
|
198
|
-
error.NoSpaceLeft => {
|
|
199
|
-
callback(user_data, operation, error.TooManyOutstandingRequests);
|
|
200
|
-
return;
|
|
201
|
-
},
|
|
202
|
-
};
|
|
202
|
+
});
|
|
203
203
|
|
|
204
204
|
// If the queue was empty, then there is no request inflight and we must send this one:
|
|
205
205
|
if (was_empty) self.send_request_for_the_first_time(message);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/// Acquires a message from the message bus if one is available.
|
|
209
|
-
pub fn get_message(self: *Self)
|
|
209
|
+
pub fn get_message(self: *Self) *Message {
|
|
210
210
|
return self.message_bus.get_message();
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -383,12 +383,12 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
/// The caller owns the returned message, if any, which has exactly 1 reference.
|
|
386
|
-
fn create_message_from_header(self: *Self, header: Header)
|
|
386
|
+
fn create_message_from_header(self: *Self, header: Header) *Message {
|
|
387
387
|
assert(header.client == self.id);
|
|
388
388
|
assert(header.cluster == self.cluster);
|
|
389
389
|
assert(header.size == @sizeOf(Header));
|
|
390
390
|
|
|
391
|
-
const message = self.message_bus.pool.
|
|
391
|
+
const message = self.message_bus.pool.get_message();
|
|
392
392
|
defer self.message_bus.unref(message);
|
|
393
393
|
|
|
394
394
|
message.header.* = header;
|
|
@@ -402,8 +402,7 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
402
402
|
fn register(self: *Self) void {
|
|
403
403
|
if (self.request_number > 0) return;
|
|
404
404
|
|
|
405
|
-
|
|
406
|
-
@panic("register: no message available to register a session with the cluster");
|
|
405
|
+
const message = self.message_bus.get_message();
|
|
407
406
|
defer self.message_bus.unref(message);
|
|
408
407
|
|
|
409
408
|
// We will set parent, context, view and checksums only when sending for the first time:
|
|
@@ -422,37 +421,24 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
422
421
|
|
|
423
422
|
assert(self.request_queue.empty());
|
|
424
423
|
|
|
425
|
-
self.request_queue.
|
|
424
|
+
self.request_queue.push_assume_capacity(.{
|
|
426
425
|
.user_data = 0,
|
|
427
426
|
.callback = undefined,
|
|
428
427
|
.message = message.ref(),
|
|
429
|
-
})
|
|
430
|
-
error.NoSpaceLeft => unreachable, // This is the first request.
|
|
431
|
-
};
|
|
428
|
+
});
|
|
432
429
|
|
|
433
430
|
self.send_request_for_the_first_time(message);
|
|
434
431
|
}
|
|
435
432
|
|
|
436
433
|
fn send_header_to_replica(self: *Self, replica: u8, header: Header) void {
|
|
437
|
-
const message = self.create_message_from_header(header)
|
|
438
|
-
log.err("{}: no header-only message available, dropping message to replica {}", .{
|
|
439
|
-
self.id,
|
|
440
|
-
replica,
|
|
441
|
-
});
|
|
442
|
-
return;
|
|
443
|
-
};
|
|
434
|
+
const message = self.create_message_from_header(header);
|
|
444
435
|
defer self.message_bus.unref(message);
|
|
445
436
|
|
|
446
437
|
self.send_message_to_replica(replica, message);
|
|
447
438
|
}
|
|
448
439
|
|
|
449
440
|
fn send_header_to_replicas(self: *Self, header: Header) void {
|
|
450
|
-
const message = self.create_message_from_header(header)
|
|
451
|
-
log.err("{}: no header-only message available, dropping message to replicas", .{
|
|
452
|
-
self.id,
|
|
453
|
-
});
|
|
454
|
-
return;
|
|
455
|
-
};
|
|
441
|
+
const message = self.create_message_from_header(header);
|
|
456
442
|
defer self.message_bus.unref(message);
|
|
457
443
|
|
|
458
444
|
var replica: u8 = 0;
|
|
@@ -744,7 +744,7 @@ test "fuzz test" {
|
|
|
744
744
|
const allocator = &arena_allocator.allocator;
|
|
745
745
|
const ticks_max: u64 = 1_000_000;
|
|
746
746
|
const clock_count: u8 = 3;
|
|
747
|
-
const SystemTime = @import("../time.zig").Time;
|
|
747
|
+
const SystemTime = @import("../test/time.zig").Time;
|
|
748
748
|
var system_time = SystemTime{};
|
|
749
749
|
var seed = @intCast(u64, system_time.realtime());
|
|
750
750
|
var min_sync_error: u64 = 1_000_000_000;
|
|
@@ -510,11 +510,7 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
510
510
|
const physical_size = vsr.sector_ceil(exact.size);
|
|
511
511
|
assert(physical_size >= exact.size);
|
|
512
512
|
|
|
513
|
-
const message = replica.message_bus.get_message()
|
|
514
|
-
self.read_prepare_log(op, checksum, "no message available");
|
|
515
|
-
callback(replica, null, null);
|
|
516
|
-
return;
|
|
517
|
-
};
|
|
513
|
+
const message = replica.message_bus.get_message();
|
|
518
514
|
defer replica.message_bus.unref(message);
|
|
519
515
|
|
|
520
516
|
// Skip the disk read if the header is all we need:
|
|
@@ -658,7 +654,7 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
658
654
|
}
|
|
659
655
|
assert(offset < self.size_headers);
|
|
660
656
|
|
|
661
|
-
const message = replica.message_bus.get_message()
|
|
657
|
+
const message = replica.message_bus.get_message();
|
|
662
658
|
defer replica.message_bus.unref(message);
|
|
663
659
|
|
|
664
660
|
// We use the count of reads executing to know when both versions have finished reading:
|