tigerbeetle-node 0.8.1 → 0.9.143
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 +584 -184
- package/dist/benchmark.js +59 -51
- package/dist/benchmark.js.map +1 -1
- 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/bin/x86_64-windows/client.node +0 -0
- package/dist/bindings.d.ts +141 -0
- package/dist/bindings.js +112 -0
- package/dist/bindings.js.map +1 -0
- package/dist/index.d.ts +2 -125
- package/dist/index.js +51 -101
- package/dist/index.js.map +1 -1
- package/dist/test.js +69 -55
- package/dist/test.js.map +1 -1
- package/package-lock.json +26 -0
- package/package.json +17 -28
- package/src/benchmark.ts +58 -49
- package/src/bindings.ts +631 -0
- package/src/index.ts +71 -163
- package/src/node.zig +169 -148
- package/src/test.ts +71 -57
- package/src/translate.zig +19 -36
- package/.yarn/releases/yarn-berry.cjs +0 -55
- package/.yarnrc.yml +0 -1
- package/scripts/download_node_headers.sh +0 -25
- package/scripts/postinstall.sh +0 -6
- package/src/tigerbeetle/scripts/benchmark.bat +0 -46
- package/src/tigerbeetle/scripts/benchmark.sh +0 -55
- package/src/tigerbeetle/scripts/install.sh +0 -6
- package/src/tigerbeetle/scripts/install_zig.bat +0 -109
- package/src/tigerbeetle/scripts/install_zig.sh +0 -84
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -39
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -290
- package/src/tigerbeetle/src/cli.zig +0 -244
- package/src/tigerbeetle/src/config.zig +0 -239
- package/src/tigerbeetle/src/demo.zig +0 -125
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
- package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -24
- package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
- package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
- package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
- package/src/tigerbeetle/src/fifo.zig +0 -104
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -793
- package/src/tigerbeetle/src/io/linux.zig +0 -1038
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1161
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/main.zig +0 -144
- package/src/tigerbeetle/src/message_bus.zig +0 -1000
- package/src/tigerbeetle/src/message_pool.zig +0 -142
- package/src/tigerbeetle/src/ring_buffer.zig +0 -289
- package/src/tigerbeetle/src/simulator.zig +0 -417
- package/src/tigerbeetle/src/state_machine.zig +0 -2470
- package/src/tigerbeetle/src/storage.zig +0 -308
- package/src/tigerbeetle/src/test/cluster.zig +0 -351
- package/src/tigerbeetle/src/test/message_bus.zig +0 -93
- package/src/tigerbeetle/src/test/network.zig +0 -179
- package/src/tigerbeetle/src/test/packet_simulator.zig +0 -387
- package/src/tigerbeetle/src/test/state_checker.zig +0 -145
- package/src/tigerbeetle/src/test/state_machine.zig +0 -76
- package/src/tigerbeetle/src/test/storage.zig +0 -438
- package/src/tigerbeetle/src/test/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -222
- package/src/tigerbeetle/src/time.zig +0 -113
- package/src/tigerbeetle/src/unit_tests.zig +0 -14
- package/src/tigerbeetle/src/vsr/client.zig +0 -505
- package/src/tigerbeetle/src/vsr/clock.zig +0 -812
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2293
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -5015
- package/src/tigerbeetle/src/vsr.zig +0 -1017
- package/yarn.lock +0 -42
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const messages = [
|
|
4
|
-
'prepare',
|
|
5
|
-
'prepare_ok',
|
|
6
|
-
'commit',
|
|
7
|
-
'request_state_transfer',
|
|
8
|
-
'state_transfer',
|
|
9
|
-
'start_view_change',
|
|
10
|
-
'do_view_change',
|
|
11
|
-
'start_view'
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
const views = [
|
|
15
|
-
'old_view',
|
|
16
|
-
'same_view',
|
|
17
|
-
'new_view'
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const statuses = [
|
|
21
|
-
'normal',
|
|
22
|
-
'view_change',
|
|
23
|
-
'recovering'
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const tuples = {};
|
|
27
|
-
|
|
28
|
-
statuses.forEach(
|
|
29
|
-
function(status) {
|
|
30
|
-
views.forEach(
|
|
31
|
-
function(view) {
|
|
32
|
-
messages.forEach(
|
|
33
|
-
function(message) {
|
|
34
|
-
tuples[`${message}, ${view}, ${status}`] = true;
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
Object.keys(tuples).sort().forEach(
|
|
43
|
-
function(key) {
|
|
44
|
-
console.log(key);
|
|
45
|
-
}
|
|
46
|
-
);
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const config = @import("config.zig");
|
|
5
|
-
|
|
6
|
-
const log = std.log;
|
|
7
|
-
pub const log_level: std.log.Level = .err;
|
|
8
|
-
|
|
9
|
-
const cli = @import("cli.zig");
|
|
10
|
-
const IO = @import("io.zig").IO;
|
|
11
|
-
|
|
12
|
-
const MessageBus = @import("message_bus.zig").MessageBusClient;
|
|
13
|
-
const StateMachine = @import("state_machine.zig").StateMachine;
|
|
14
|
-
const RingBuffer = @import("ring_buffer.zig").RingBuffer;
|
|
15
|
-
|
|
16
|
-
const vsr = @import("vsr.zig");
|
|
17
|
-
const Client = vsr.Client(StateMachine, MessageBus);
|
|
18
|
-
|
|
19
|
-
const tb = @import("tigerbeetle.zig");
|
|
20
|
-
|
|
21
|
-
const batches_count = 100;
|
|
22
|
-
|
|
23
|
-
const transfers_per_batch: u32 = @divExact(
|
|
24
|
-
config.message_size_max - @sizeOf(vsr.Header),
|
|
25
|
-
@sizeOf(tb.Transfer),
|
|
26
|
-
);
|
|
27
|
-
comptime {
|
|
28
|
-
assert(transfers_per_batch >= 2041);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const transfers_max: u32 = batches_count * transfers_per_batch;
|
|
32
|
-
|
|
33
|
-
var accounts = [_]tb.Account{
|
|
34
|
-
.{
|
|
35
|
-
.id = 1,
|
|
36
|
-
.user_data = 0,
|
|
37
|
-
.reserved = [_]u8{0} ** 48,
|
|
38
|
-
.ledger = 2,
|
|
39
|
-
.code = 0,
|
|
40
|
-
.flags = .{},
|
|
41
|
-
.debits_pending = 0,
|
|
42
|
-
.debits_posted = 0,
|
|
43
|
-
.credits_pending = 0,
|
|
44
|
-
.credits_posted = 0,
|
|
45
|
-
},
|
|
46
|
-
.{
|
|
47
|
-
.id = 2,
|
|
48
|
-
.user_data = 0,
|
|
49
|
-
.reserved = [_]u8{0} ** 48,
|
|
50
|
-
.ledger = 2,
|
|
51
|
-
.code = 0,
|
|
52
|
-
.flags = .{},
|
|
53
|
-
.debits_pending = 0,
|
|
54
|
-
.debits_posted = 0,
|
|
55
|
-
.credits_pending = 0,
|
|
56
|
-
.credits_posted = 0,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
var create_transfers_latency_max: i64 = 0;
|
|
60
|
-
|
|
61
|
-
pub fn main() !void {
|
|
62
|
-
const stdout = std.io.getStdOut().writer();
|
|
63
|
-
const stderr = std.io.getStdErr().writer();
|
|
64
|
-
|
|
65
|
-
if (builtin.mode != .ReleaseSafe and builtin.mode != .ReleaseFast) {
|
|
66
|
-
try stderr.print("Benchmark must be built as ReleaseSafe for reasonable results.\n", .{});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
70
|
-
defer arena.deinit();
|
|
71
|
-
|
|
72
|
-
const allocator = arena.allocator();
|
|
73
|
-
|
|
74
|
-
const client_id = std.crypto.random.int(u128);
|
|
75
|
-
const cluster_id: u32 = 0;
|
|
76
|
-
var address = [_]std.net.Address{try std.net.Address.parseIp4("127.0.0.1", config.port)};
|
|
77
|
-
|
|
78
|
-
var io = try IO.init(32, 0);
|
|
79
|
-
var message_bus = try MessageBus.init(allocator, cluster_id, address[0..], client_id, &io);
|
|
80
|
-
defer message_bus.deinit();
|
|
81
|
-
|
|
82
|
-
var client = try Client.init(
|
|
83
|
-
allocator,
|
|
84
|
-
client_id,
|
|
85
|
-
cluster_id,
|
|
86
|
-
@intCast(u8, address.len),
|
|
87
|
-
&message_bus,
|
|
88
|
-
);
|
|
89
|
-
defer client.deinit();
|
|
90
|
-
|
|
91
|
-
message_bus.set_on_message(*Client, &client, Client.on_message);
|
|
92
|
-
|
|
93
|
-
// Pre-allocate a million transfers:
|
|
94
|
-
const transfers = try arena.allocator().alloc(tb.Transfer, transfers_max);
|
|
95
|
-
for (transfers) |*transfer, index| {
|
|
96
|
-
transfer.* = .{
|
|
97
|
-
.id = index,
|
|
98
|
-
.debit_account_id = accounts[0].id,
|
|
99
|
-
.credit_account_id = accounts[1].id,
|
|
100
|
-
.pending_id = 0,
|
|
101
|
-
.user_data = 0,
|
|
102
|
-
.reserved = 0,
|
|
103
|
-
.code = 0,
|
|
104
|
-
.ledger = 2,
|
|
105
|
-
.flags = .{},
|
|
106
|
-
.amount = 1,
|
|
107
|
-
.timeout = 0,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
try wait_for_connect(&client, &io);
|
|
112
|
-
|
|
113
|
-
var queue = TimedQueue.init(&client, &io);
|
|
114
|
-
try queue.push(.{
|
|
115
|
-
.operation = StateMachine.Operation.create_accounts,
|
|
116
|
-
.data = std.mem.sliceAsBytes(accounts[0..]),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
try queue.execute();
|
|
120
|
-
assert(queue.end != null);
|
|
121
|
-
assert(queue.batches.empty());
|
|
122
|
-
|
|
123
|
-
var count: u64 = 0;
|
|
124
|
-
queue.reset();
|
|
125
|
-
while (count < transfers.len) {
|
|
126
|
-
try queue.push(.{
|
|
127
|
-
.operation = .create_transfers,
|
|
128
|
-
.data = std.mem.sliceAsBytes(transfers[count..][0..transfers_per_batch]),
|
|
129
|
-
});
|
|
130
|
-
count += transfers_per_batch;
|
|
131
|
-
}
|
|
132
|
-
assert(count == transfers_max);
|
|
133
|
-
|
|
134
|
-
try queue.execute();
|
|
135
|
-
assert(queue.end != null);
|
|
136
|
-
assert(queue.batches.empty());
|
|
137
|
-
|
|
138
|
-
var ms = queue.end.? - queue.start.?;
|
|
139
|
-
|
|
140
|
-
const result: i64 = @divFloor(@intCast(i64, transfers.len * 1000), ms);
|
|
141
|
-
try stdout.print("============================================\n", .{});
|
|
142
|
-
try stdout.print("{} transfers per second\n\n", .{result});
|
|
143
|
-
try stdout.print("max p100 latency per {} transfers = {}ms\n", .{
|
|
144
|
-
transfers_per_batch,
|
|
145
|
-
queue.transfers_latency_max,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const Batch = struct {
|
|
150
|
-
operation: StateMachine.Operation,
|
|
151
|
-
data: []u8,
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const TimedQueue = struct {
|
|
155
|
-
batch_start: ?i64,
|
|
156
|
-
start: ?i64,
|
|
157
|
-
end: ?i64,
|
|
158
|
-
transfers_latency_max: i64,
|
|
159
|
-
client: *Client,
|
|
160
|
-
io: *IO,
|
|
161
|
-
batches: RingBuffer(Batch, batches_count),
|
|
162
|
-
|
|
163
|
-
pub fn init(client: *Client, io: *IO) TimedQueue {
|
|
164
|
-
var self = TimedQueue{
|
|
165
|
-
.batch_start = null,
|
|
166
|
-
.start = null,
|
|
167
|
-
.end = null,
|
|
168
|
-
.transfers_latency_max = 0,
|
|
169
|
-
.client = client,
|
|
170
|
-
.io = io,
|
|
171
|
-
.batches = .{},
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
return self;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
pub fn reset(self: *TimedQueue) void {
|
|
178
|
-
self.batch_start = null;
|
|
179
|
-
self.start = null;
|
|
180
|
-
self.end = null;
|
|
181
|
-
self.transfers_latency_max = 0;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
pub fn push(self: *TimedQueue, batch: Batch) !void {
|
|
185
|
-
try self.batches.push(batch);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
pub fn execute(self: *TimedQueue) !void {
|
|
189
|
-
assert(self.start == null);
|
|
190
|
-
assert(!self.batches.empty());
|
|
191
|
-
self.reset();
|
|
192
|
-
log.debug("executing batches...", .{});
|
|
193
|
-
|
|
194
|
-
const now = std.time.milliTimestamp();
|
|
195
|
-
self.start = now;
|
|
196
|
-
if (self.batches.head_ptr()) |starting_batch| {
|
|
197
|
-
log.debug("sending first batch...", .{});
|
|
198
|
-
self.batch_start = now;
|
|
199
|
-
const message = self.client.get_message();
|
|
200
|
-
defer self.client.unref(message);
|
|
201
|
-
|
|
202
|
-
std.mem.copy(
|
|
203
|
-
u8,
|
|
204
|
-
message.buffer[@sizeOf(vsr.Header)..],
|
|
205
|
-
std.mem.sliceAsBytes(starting_batch.data),
|
|
206
|
-
);
|
|
207
|
-
self.client.request(
|
|
208
|
-
@intCast(u128, @ptrToInt(self)),
|
|
209
|
-
TimedQueue.lap,
|
|
210
|
-
starting_batch.operation,
|
|
211
|
-
message,
|
|
212
|
-
starting_batch.data.len,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
while (!self.batches.empty()) {
|
|
217
|
-
self.client.tick();
|
|
218
|
-
try self.io.run_for_ns(5 * std.time.ns_per_ms);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
pub fn lap(
|
|
223
|
-
user_data: u128,
|
|
224
|
-
operation: StateMachine.Operation,
|
|
225
|
-
results: Client.Error![]const u8,
|
|
226
|
-
) void {
|
|
227
|
-
const now = std.time.milliTimestamp();
|
|
228
|
-
const value = results catch |err| {
|
|
229
|
-
log.err("Client returned error={o}", .{@errorName(err)});
|
|
230
|
-
@panic("Client returned error during benchmarking.");
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
log.debug("response={s}", .{std.mem.bytesAsSlice(tb.CreateAccountsResult, value)});
|
|
234
|
-
|
|
235
|
-
const self: *TimedQueue = @intToPtr(*TimedQueue, @intCast(usize, user_data));
|
|
236
|
-
const completed_batch: ?Batch = self.batches.pop();
|
|
237
|
-
assert(completed_batch != null);
|
|
238
|
-
assert(completed_batch.?.operation == operation);
|
|
239
|
-
|
|
240
|
-
log.debug("completed batch operation={} start={}", .{
|
|
241
|
-
completed_batch.?.operation,
|
|
242
|
-
self.batch_start,
|
|
243
|
-
});
|
|
244
|
-
const latency = now - self.batch_start.?;
|
|
245
|
-
switch (operation) {
|
|
246
|
-
.create_accounts => {},
|
|
247
|
-
.create_transfers => {
|
|
248
|
-
if (latency > self.transfers_latency_max) {
|
|
249
|
-
self.transfers_latency_max = latency;
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
else => unreachable,
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (self.batches.head_ptr()) |next_batch| {
|
|
256
|
-
const message = self.client.get_message();
|
|
257
|
-
defer self.client.unref(message);
|
|
258
|
-
|
|
259
|
-
std.mem.copy(
|
|
260
|
-
u8,
|
|
261
|
-
message.buffer[@sizeOf(vsr.Header)..],
|
|
262
|
-
std.mem.sliceAsBytes(next_batch.data),
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
self.batch_start = std.time.milliTimestamp();
|
|
266
|
-
self.client.request(
|
|
267
|
-
@intCast(u128, @ptrToInt(self)),
|
|
268
|
-
TimedQueue.lap,
|
|
269
|
-
next_batch.operation,
|
|
270
|
-
message,
|
|
271
|
-
next_batch.data.len,
|
|
272
|
-
);
|
|
273
|
-
} else {
|
|
274
|
-
log.debug("stopping timer...", .{});
|
|
275
|
-
self.end = now;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
fn wait_for_connect(client: *Client, io: *IO) !void {
|
|
281
|
-
var ticks: u32 = 0;
|
|
282
|
-
while (ticks < 20) : (ticks += 1) {
|
|
283
|
-
client.tick();
|
|
284
|
-
// We tick IO outside of client so that an IO instance can be shared by multiple clients:
|
|
285
|
-
// Otherwise we will hit io_uring memory restrictions too quickly.
|
|
286
|
-
try io.tick();
|
|
287
|
-
|
|
288
|
-
std.time.sleep(10 * std.time.ns_per_ms);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const assert = std.debug.assert;
|
|
3
|
-
const fmt = std.fmt;
|
|
4
|
-
const mem = std.mem;
|
|
5
|
-
const meta = std.meta;
|
|
6
|
-
const net = std.net;
|
|
7
|
-
const os = std.os;
|
|
8
|
-
|
|
9
|
-
const config = @import("config.zig");
|
|
10
|
-
const vsr = @import("vsr.zig");
|
|
11
|
-
const IO = @import("io.zig").IO;
|
|
12
|
-
|
|
13
|
-
const usage = fmt.comptimePrint(
|
|
14
|
-
\\Usage:
|
|
15
|
-
\\
|
|
16
|
-
\\ tigerbeetle [-h | --help]
|
|
17
|
-
\\
|
|
18
|
-
\\ tigerbeetle init [--directory=<path>] --cluster=<integer> --replica=<index>
|
|
19
|
-
\\
|
|
20
|
-
\\ tigerbeetle start [--directory=<path>] --cluster=<integer> --replica=<index> --addresses=<addresses>
|
|
21
|
-
\\
|
|
22
|
-
\\Commands:
|
|
23
|
-
\\
|
|
24
|
-
\\ init Create a new .tigerbeetle data file. Requires the --cluster and
|
|
25
|
-
\\ --replica options. The file will be created in the path set by
|
|
26
|
-
\\ the --directory option if provided. Otherwise, it will be created in
|
|
27
|
-
\\ the default {[default_directory]s}.
|
|
28
|
-
\\
|
|
29
|
-
\\ start Run a TigerBeetle replica as part of the cluster specified by the
|
|
30
|
-
\\ --cluster, --replica, and --addresses options. This requires an
|
|
31
|
-
\\ existing .tigerbeetle data file, either in the default
|
|
32
|
-
\\ {[default_directory]s} or the path set with --directory.
|
|
33
|
-
\\
|
|
34
|
-
\\Options:
|
|
35
|
-
\\
|
|
36
|
-
\\ -h, --help
|
|
37
|
-
\\ Print this help message and exit.
|
|
38
|
-
\\
|
|
39
|
-
\\ --directory=<path>
|
|
40
|
-
\\ Set the directory used to store .tigerbeetle data files. If this option is
|
|
41
|
-
\\ omitted, the default {[default_directory]s} will be used.
|
|
42
|
-
\\
|
|
43
|
-
\\ --cluster=<integer>
|
|
44
|
-
\\ Set the cluster ID to the provided 32-bit unsigned integer.
|
|
45
|
-
\\
|
|
46
|
-
\\ --replica=<index>
|
|
47
|
-
\\ Set the zero-based index that will be used for this replica process.
|
|
48
|
-
\\ The value of this option will be interpreted as an index into the --addresses array.
|
|
49
|
-
\\
|
|
50
|
-
\\ --addresses=<addresses>
|
|
51
|
-
\\ Set the addresses of all replicas in the cluster. Accepts a
|
|
52
|
-
\\ comma-separated list of IPv4 addresses with port numbers.
|
|
53
|
-
\\ Either the IPv4 address or port number, but not both, may be
|
|
54
|
-
\\ ommited in which case a default of {[default_address]s} or {[default_port]d}
|
|
55
|
-
\\ will be used.
|
|
56
|
-
\\
|
|
57
|
-
\\Examples:
|
|
58
|
-
\\
|
|
59
|
-
\\ tigerbeetle init --cluster=0 --replica=0 --directory=/var/lib/tigerbeetle
|
|
60
|
-
\\ tigerbeetle init --cluster=0 --replica=1 --directory=/var/lib/tigerbeetle
|
|
61
|
-
\\ tigerbeetle init --cluster=0 --replica=2 --directory=/var/lib/tigerbeetle
|
|
62
|
-
\\
|
|
63
|
-
\\ tigerbeetle start --cluster=0 --replica=0 --addresses=127.0.0.1:3003,127.0.0.1:3001,127.0.0.1:3002
|
|
64
|
-
\\ tigerbeetle start --cluster=0 --replica=1 --addresses=3003,3001,3002
|
|
65
|
-
\\ tigerbeetle start --cluster=0 --replica=2 --addresses=3003,3001,3002
|
|
66
|
-
\\
|
|
67
|
-
\\ tigerbeetle start --cluster=1 --replica=0 --addresses=192.168.0.1,192.168.0.2,192.168.0.3
|
|
68
|
-
\\
|
|
69
|
-
, .{
|
|
70
|
-
.default_directory = config.directory,
|
|
71
|
-
.default_address = config.address,
|
|
72
|
-
.default_port = config.port,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
pub const Command = union(enum) {
|
|
76
|
-
init: struct {
|
|
77
|
-
cluster: u32,
|
|
78
|
-
replica: u8,
|
|
79
|
-
dir_fd: os.fd_t,
|
|
80
|
-
},
|
|
81
|
-
start: struct {
|
|
82
|
-
cluster: u32,
|
|
83
|
-
replica: u8,
|
|
84
|
-
addresses: []net.Address,
|
|
85
|
-
dir_fd: os.fd_t,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/// Parse the command line arguments passed to the tigerbeetle binary.
|
|
90
|
-
/// Exits the program with a non-zero exit code if an error is found.
|
|
91
|
-
pub fn parse_args(allocator: std.mem.Allocator) !Command {
|
|
92
|
-
var maybe_cluster: ?[]const u8 = null;
|
|
93
|
-
var maybe_replica: ?[]const u8 = null;
|
|
94
|
-
var maybe_addresses: ?[]const u8 = null;
|
|
95
|
-
var maybe_directory: ?[:0]const u8 = null;
|
|
96
|
-
|
|
97
|
-
var args = try std.process.argsWithAllocator(allocator);
|
|
98
|
-
defer args.deinit();
|
|
99
|
-
|
|
100
|
-
// Keep track of the args from the ArgIterator above that were allocated
|
|
101
|
-
// then free them all at the end of the scope.
|
|
102
|
-
var args_allocated = std.ArrayList([:0]const u8).init(allocator);
|
|
103
|
-
defer {
|
|
104
|
-
for (args_allocated.items) |arg| allocator.free(arg);
|
|
105
|
-
args_allocated.deinit();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Skip argv[0] which is the name of this executable
|
|
109
|
-
const did_skip = args.skip();
|
|
110
|
-
assert(did_skip);
|
|
111
|
-
|
|
112
|
-
const raw_command = try (args.next(allocator) orelse
|
|
113
|
-
fatal("no command provided, expected 'start' or 'init'", .{}));
|
|
114
|
-
defer allocator.free(raw_command);
|
|
115
|
-
|
|
116
|
-
if (mem.eql(u8, raw_command, "-h") or mem.eql(u8, raw_command, "--help")) {
|
|
117
|
-
std.io.getStdOut().writeAll(usage) catch os.exit(1);
|
|
118
|
-
os.exit(0);
|
|
119
|
-
}
|
|
120
|
-
const command = meta.stringToEnum(meta.Tag(Command), raw_command) orelse
|
|
121
|
-
fatal("unknown command '{s}', expected 'start' or 'init'", .{raw_command});
|
|
122
|
-
|
|
123
|
-
while (args.next(allocator)) |parsed_arg| {
|
|
124
|
-
const arg = try parsed_arg;
|
|
125
|
-
try args_allocated.append(arg);
|
|
126
|
-
|
|
127
|
-
if (mem.startsWith(u8, arg, "--cluster")) {
|
|
128
|
-
maybe_cluster = parse_flag("--cluster", arg);
|
|
129
|
-
} else if (mem.startsWith(u8, arg, "--replica")) {
|
|
130
|
-
maybe_replica = parse_flag("--replica", arg);
|
|
131
|
-
} else if (mem.startsWith(u8, arg, "--addresses")) {
|
|
132
|
-
maybe_addresses = parse_flag("--addresses", arg);
|
|
133
|
-
} else if (mem.startsWith(u8, arg, "--directory")) {
|
|
134
|
-
maybe_directory = parse_flag("--directory", arg);
|
|
135
|
-
} else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
|
|
136
|
-
std.io.getStdOut().writeAll(usage) catch os.exit(1);
|
|
137
|
-
os.exit(0);
|
|
138
|
-
} else if (mem.startsWith(u8, arg, "--")) {
|
|
139
|
-
fatal("unexpected argument: '{s}'", .{arg});
|
|
140
|
-
} else {
|
|
141
|
-
fatal("unexpected argument: '{s}' (must start with '--')", .{arg});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const raw_cluster = maybe_cluster orelse fatal("required argument: --cluster", .{});
|
|
146
|
-
const raw_replica = maybe_replica orelse fatal("required argument: --replica", .{});
|
|
147
|
-
|
|
148
|
-
const cluster = parse_cluster(raw_cluster);
|
|
149
|
-
const replica = parse_replica(raw_replica);
|
|
150
|
-
|
|
151
|
-
const dir_path = maybe_directory orelse config.directory;
|
|
152
|
-
const dir_fd = IO.open_dir(dir_path) catch |err|
|
|
153
|
-
fatal("failed to open directory '{s}': {}", .{ dir_path, err });
|
|
154
|
-
|
|
155
|
-
switch (command) {
|
|
156
|
-
.init => {
|
|
157
|
-
if (maybe_addresses != null) {
|
|
158
|
-
fatal("--addresses: supported only by 'start' command", .{});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return Command{
|
|
162
|
-
.init = .{
|
|
163
|
-
.cluster = cluster,
|
|
164
|
-
.replica = replica,
|
|
165
|
-
.dir_fd = dir_fd,
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
},
|
|
169
|
-
.start => {
|
|
170
|
-
const raw_addresses = maybe_addresses orelse
|
|
171
|
-
fatal("required argument: --addresses", .{});
|
|
172
|
-
const addresses = parse_addresses(allocator, raw_addresses);
|
|
173
|
-
|
|
174
|
-
if (replica >= addresses.len) {
|
|
175
|
-
fatal("--replica: value greater than length of --addresses array", .{});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return Command{
|
|
179
|
-
.start = .{
|
|
180
|
-
.cluster = cluster,
|
|
181
|
-
.replica = replica,
|
|
182
|
-
.addresses = addresses,
|
|
183
|
-
.dir_fd = dir_fd,
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/// Format and print an error message followed by the usage string to stderr,
|
|
191
|
-
/// then exit with an exit code of 1.
|
|
192
|
-
fn fatal(comptime fmt_string: []const u8, args: anytype) noreturn {
|
|
193
|
-
const stderr = std.io.getStdErr().writer();
|
|
194
|
-
stderr.print("error: " ++ fmt_string ++ "\n", args) catch {};
|
|
195
|
-
os.exit(1);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/// Parse e.g. `--cluster=1a2b3c` into `1a2b3c` with error handling.
|
|
199
|
-
fn parse_flag(comptime flag: []const u8, arg: [:0]const u8) [:0]const u8 {
|
|
200
|
-
const value = arg[flag.len..];
|
|
201
|
-
if (value.len < 2) {
|
|
202
|
-
fatal("{s} argument requires a value", .{flag});
|
|
203
|
-
}
|
|
204
|
-
if (value[0] != '=') {
|
|
205
|
-
fatal("expected '=' after {s} but found '{c}'", .{ flag, value[0] });
|
|
206
|
-
}
|
|
207
|
-
return value[1..];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
fn parse_cluster(raw_cluster: []const u8) u32 {
|
|
211
|
-
const cluster = fmt.parseUnsigned(u32, raw_cluster, 10) catch |err| switch (err) {
|
|
212
|
-
error.Overflow => fatal("--cluster: value exceeds a 32-bit unsigned integer", .{}),
|
|
213
|
-
error.InvalidCharacter => fatal("--cluster: value contains an invalid character", .{}),
|
|
214
|
-
};
|
|
215
|
-
return cluster;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/// Parse and allocate the addresses returning a slice into that array.
|
|
219
|
-
fn parse_addresses(allocator: std.mem.Allocator, raw_addresses: []const u8) []net.Address {
|
|
220
|
-
return vsr.parse_addresses(allocator, raw_addresses) catch |err| switch (err) {
|
|
221
|
-
error.AddressHasTrailingComma => fatal("--addresses: invalid trailing comma", .{}),
|
|
222
|
-
error.AddressLimitExceeded => {
|
|
223
|
-
fatal("--addresses: too many addresses, at most {d} are allowed", .{
|
|
224
|
-
config.replicas_max,
|
|
225
|
-
});
|
|
226
|
-
},
|
|
227
|
-
error.AddressHasMoreThanOneColon => {
|
|
228
|
-
fatal("--addresses: invalid address with more than one colon", .{});
|
|
229
|
-
},
|
|
230
|
-
error.PortOverflow => fatal("--addresses: port exceeds 65535", .{}),
|
|
231
|
-
error.PortInvalid => fatal("--addresses: invalid port", .{}),
|
|
232
|
-
error.AddressInvalid => fatal("--addresses: invalid IPv4 address", .{}),
|
|
233
|
-
error.OutOfMemory => fatal("--addresses: out of memory", .{}),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
fn parse_replica(raw_replica: []const u8) u8 {
|
|
238
|
-
comptime assert(config.replicas_max <= std.math.maxInt(u8));
|
|
239
|
-
const replica = fmt.parseUnsigned(u8, raw_replica, 10) catch |err| switch (err) {
|
|
240
|
-
error.Overflow => fatal("--replica: value exceeds an 8-bit unsigned integer", .{}),
|
|
241
|
-
error.InvalidCharacter => fatal("--replica: value contains an invalid character", .{}),
|
|
242
|
-
};
|
|
243
|
-
return replica;
|
|
244
|
-
}
|