tigerbeetle-node 0.11.0 → 0.11.1
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/package.json +4 -3
- package/src/tigerbeetle/scripts/fuzz_loop.sh +1 -1
- package/src/tigerbeetle/scripts/pre-commit.sh +2 -2
- package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
- package/src/tigerbeetle/src/benchmark.zig +25 -11
- package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
- package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
- package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
- package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
- package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -256
- package/src/tigerbeetle/src/c/tb_client.h +18 -4
- package/src/tigerbeetle/src/c/tb_client.zig +88 -26
- package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
- package/src/tigerbeetle/src/c/test.zig +371 -1
- package/src/tigerbeetle/src/cli.zig +36 -6
- package/src/tigerbeetle/src/config.zig +10 -1
- package/src/tigerbeetle/src/demo.zig +2 -1
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
- package/src/tigerbeetle/src/ewah.zig +11 -33
- package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
- package/src/tigerbeetle/src/lsm/README.md +97 -3
- package/src/tigerbeetle/src/lsm/compaction.zig +32 -7
- package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +34 -32
- package/src/tigerbeetle/src/lsm/grid.zig +39 -21
- package/src/tigerbeetle/src/lsm/groove.zig +1 -0
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +3 -3
- package/src/tigerbeetle/src/lsm/level_iterator.zig +1 -1
- package/src/tigerbeetle/src/lsm/manifest.zig +13 -0
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -49
- package/src/tigerbeetle/src/lsm/manifest_log.zig +173 -335
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
- package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +1 -0
- package/src/tigerbeetle/src/lsm/segmented_array.zig +24 -15
- package/src/tigerbeetle/src/lsm/table.zig +32 -20
- package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
- package/src/tigerbeetle/src/lsm/table_iterator.zig +4 -5
- package/src/tigerbeetle/src/lsm/test.zig +13 -2
- package/src/tigerbeetle/src/lsm/tree.zig +45 -7
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +36 -32
- package/src/tigerbeetle/src/main.zig +55 -2
- package/src/tigerbeetle/src/message_bus.zig +18 -7
- package/src/tigerbeetle/src/message_pool.zig +8 -2
- package/src/tigerbeetle/src/ring_buffer.zig +7 -3
- package/src/tigerbeetle/src/simulator.zig +38 -11
- package/src/tigerbeetle/src/state_machine.zig +47 -22
- package/src/tigerbeetle/src/test/accounting/workload.zig +9 -5
- package/src/tigerbeetle/src/test/cluster.zig +15 -33
- package/src/tigerbeetle/src/test/conductor.zig +2 -1
- package/src/tigerbeetle/src/test/network.zig +45 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
- package/src/tigerbeetle/src/test/state_checker.zig +5 -7
- package/src/tigerbeetle/src/test/storage.zig +453 -110
- package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +1 -0
- package/src/tigerbeetle/src/unit_tests.zig +6 -1
- package/src/tigerbeetle/src/util.zig +97 -11
- package/src/tigerbeetle/src/vopr.zig +2 -1
- package/src/tigerbeetle/src/vsr/client.zig +8 -3
- package/src/tigerbeetle/src/vsr/journal.zig +280 -202
- package/src/tigerbeetle/src/vsr/replica.zig +169 -31
- package/src/tigerbeetle/src/vsr/superblock.zig +356 -629
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -6
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +414 -151
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +44 -9
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
- package/src/tigerbeetle/src/vsr.zig +19 -5
- package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
- package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
- package/src/tigerbeetle/src/vopr_hub/README.md +0 -58
- package/src/tigerbeetle/src/vopr_hub/SETUP.md +0 -199
- package/src/tigerbeetle/src/vopr_hub/go.mod +0 -3
- package/src/tigerbeetle/src/vopr_hub/main.go +0 -1022
- package/src/tigerbeetle/src/vopr_hub/scheduler/go.mod +0 -3
- package/src/tigerbeetle/src/vopr_hub/scheduler/main.go +0 -403
|
@@ -1,181 +1,44 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
-
const os = std.os;
|
|
3
2
|
const assert = std.debug.assert;
|
|
4
3
|
|
|
5
4
|
const config = @import("../../config.zig");
|
|
6
|
-
const log = std.log.scoped(.
|
|
7
|
-
|
|
8
|
-
const vsr = @import("../../vsr.zig");
|
|
9
|
-
const Header = vsr.Header;
|
|
10
|
-
|
|
11
|
-
const IO = @import("../../io.zig").IO;
|
|
12
|
-
const message_pool = @import("../../message_pool.zig");
|
|
13
|
-
|
|
14
|
-
const MessagePool = message_pool.MessagePool;
|
|
15
|
-
const Message = MessagePool.Message;
|
|
5
|
+
const log = std.log.scoped(.tb_client_thread);
|
|
16
6
|
|
|
17
7
|
const Packet = @import("packet.zig").Packet;
|
|
18
8
|
const Signal = @import("signal.zig").Signal;
|
|
19
9
|
|
|
20
10
|
pub fn ThreadType(
|
|
21
|
-
comptime
|
|
22
|
-
comptime MessageBus: type,
|
|
11
|
+
comptime Context: type,
|
|
23
12
|
) type {
|
|
24
13
|
return struct {
|
|
25
|
-
pub const Client = vsr.Client(StateMachine, MessageBus);
|
|
26
|
-
pub const Operation = StateMachine.Operation;
|
|
27
|
-
|
|
28
|
-
fn operation_size_of(op: u8) ?usize {
|
|
29
|
-
const allowed_operations = [_]Operation{
|
|
30
|
-
.create_accounts,
|
|
31
|
-
.create_transfers,
|
|
32
|
-
.lookup_accounts,
|
|
33
|
-
.lookup_transfers,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
inline for (allowed_operations) |operation| {
|
|
37
|
-
if (op == @enumToInt(operation)) {
|
|
38
|
-
return @sizeOf(StateMachine.Event(operation));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/////////////////////////////////////////////////////////////////////////
|
|
46
|
-
|
|
47
14
|
const Self = @This();
|
|
48
15
|
|
|
49
|
-
|
|
50
|
-
client_id: u128,
|
|
51
|
-
packets: []Packet,
|
|
52
|
-
|
|
53
|
-
addresses: []std.net.Address,
|
|
54
|
-
io: IO,
|
|
55
|
-
message_pool: MessagePool,
|
|
56
|
-
message_bus: MessageBus,
|
|
57
|
-
client: Client,
|
|
16
|
+
context: *Context,
|
|
58
17
|
|
|
59
18
|
retry: Packet.List,
|
|
60
19
|
submitted: Packet.Stack,
|
|
61
|
-
available_messages: usize,
|
|
62
|
-
on_completion_fn: CompletionFn,
|
|
63
20
|
|
|
64
21
|
signal: Signal,
|
|
65
22
|
thread: std.Thread,
|
|
66
23
|
|
|
67
|
-
pub const CompletionFn = fn (
|
|
68
|
-
client_thread: *Self,
|
|
69
|
-
packet: *Packet,
|
|
70
|
-
result: ?[]const u8,
|
|
71
|
-
) void;
|
|
72
|
-
|
|
73
|
-
pub const Error = error{
|
|
74
|
-
Unexpected,
|
|
75
|
-
OutOfMemory,
|
|
76
|
-
InvalidAddress,
|
|
77
|
-
SystemResources,
|
|
78
|
-
NetworkSubsystemFailed,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
24
|
pub fn init(
|
|
82
25
|
self: *Self,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
num_packets: u32,
|
|
87
|
-
on_completion_fn: CompletionFn,
|
|
88
|
-
) Error!void {
|
|
89
|
-
self.allocator = allocator;
|
|
90
|
-
self.client_id = std.crypto.random.int(u128);
|
|
91
|
-
log.debug("init: initializing client_id={}.", .{self.client_id});
|
|
92
|
-
|
|
93
|
-
log.debug("init: allocating tb_packets.", .{});
|
|
94
|
-
self.packets = self.allocator.alloc(Packet, num_packets) catch |err| {
|
|
95
|
-
log.err("failed to allocate tb_packets: {}", .{err});
|
|
96
|
-
return Error.OutOfMemory;
|
|
97
|
-
};
|
|
98
|
-
errdefer self.allocator.free(self.packets);
|
|
99
|
-
|
|
100
|
-
log.debug("init: parsing vsr addresses.", .{});
|
|
101
|
-
const address_limit = std.mem.count(u8, addresses, ",") + 1;
|
|
102
|
-
self.addresses = vsr.parse_addresses(self.allocator, addresses, address_limit) catch |err| {
|
|
103
|
-
log.err("failed to parse addresses: {}.", .{err});
|
|
104
|
-
return Error.InvalidAddress;
|
|
105
|
-
};
|
|
106
|
-
errdefer self.allocator.free(self.addresses);
|
|
107
|
-
|
|
108
|
-
log.debug("init: initializing IO.", .{});
|
|
109
|
-
self.io = IO.init(32, 0) catch |err| {
|
|
110
|
-
log.err("failed to initialize IO: {}.", .{err});
|
|
111
|
-
return switch (err) {
|
|
112
|
-
error.ProcessFdQuotaExceeded => error.SystemResources,
|
|
113
|
-
error.Unexpected => error.Unexpected,
|
|
114
|
-
else => unreachable,
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
errdefer self.io.deinit();
|
|
118
|
-
|
|
119
|
-
log.debug("init: initializing MessagePool", .{});
|
|
120
|
-
self.message_pool = MessagePool.init(allocator, .client) catch |err| {
|
|
121
|
-
log.err("failed to initialize MessagePool: {}", .{err});
|
|
122
|
-
return err;
|
|
123
|
-
};
|
|
124
|
-
errdefer self.message_pool.deinit(self.allocator);
|
|
125
|
-
|
|
126
|
-
log.debug("init: initializing MessageBus.", .{});
|
|
127
|
-
self.message_bus = MessageBus.init(
|
|
128
|
-
self.allocator,
|
|
129
|
-
cluster_id,
|
|
130
|
-
.{ .client = self.client_id },
|
|
131
|
-
&self.message_pool,
|
|
132
|
-
Client.on_message,
|
|
133
|
-
.{
|
|
134
|
-
.configuration = self.addresses,
|
|
135
|
-
.io = &self.io,
|
|
136
|
-
},
|
|
137
|
-
) catch |err| {
|
|
138
|
-
log.err("failed to initialize message bus: {}.", .{err});
|
|
139
|
-
return err;
|
|
140
|
-
};
|
|
141
|
-
errdefer self.message_bus.deinit(self.allocator);
|
|
142
|
-
|
|
143
|
-
log.debug("init: Initializing client(cluster_id={d}, client_id={d}, addresses={o})", .{
|
|
144
|
-
cluster_id,
|
|
145
|
-
self.client_id,
|
|
146
|
-
self.addresses,
|
|
147
|
-
});
|
|
148
|
-
self.client = Client.init(
|
|
149
|
-
allocator,
|
|
150
|
-
self.client_id,
|
|
151
|
-
cluster_id,
|
|
152
|
-
@intCast(u8, self.addresses.len),
|
|
153
|
-
&self.message_pool,
|
|
154
|
-
.{
|
|
155
|
-
.configuration = self.addresses,
|
|
156
|
-
.io = &self.io,
|
|
157
|
-
},
|
|
158
|
-
) catch |err| {
|
|
159
|
-
log.err("failed to initalize client: {}", .{err});
|
|
160
|
-
return err;
|
|
161
|
-
};
|
|
162
|
-
errdefer self.client.deinit(self.allocator);
|
|
163
|
-
|
|
26
|
+
context: *Context,
|
|
27
|
+
) !void {
|
|
28
|
+
self.context = context;
|
|
164
29
|
self.retry = .{};
|
|
165
30
|
self.submitted = .{};
|
|
166
|
-
self.available_messages = message_pool.messages_max_client;
|
|
167
|
-
self.on_completion_fn = on_completion_fn;
|
|
168
31
|
|
|
169
|
-
log.debug("init: initializing
|
|
170
|
-
self.signal.init(&
|
|
171
|
-
log.err("failed to initialize Signal: {}.", .{err});
|
|
172
|
-
return err;
|
|
173
|
-
};
|
|
32
|
+
log.debug("{}: init: initializing signal", .{context.client_id});
|
|
33
|
+
try self.signal.init(&context.io, Self.on_signal);
|
|
174
34
|
errdefer self.signal.deinit();
|
|
175
35
|
|
|
176
|
-
log.debug("init: spawning
|
|
177
|
-
self.thread = std.Thread.spawn(.{},
|
|
178
|
-
log.err("failed to spawn
|
|
36
|
+
log.debug("{}: init: spawning thread", .{context.client_id});
|
|
37
|
+
self.thread = std.Thread.spawn(.{}, Context.run, .{context}) catch |err| {
|
|
38
|
+
log.err("{}: failed to spawn thread: {s}", .{
|
|
39
|
+
context.client_id,
|
|
40
|
+
@errorName(err),
|
|
41
|
+
});
|
|
179
42
|
return switch (err) {
|
|
180
43
|
error.Unexpected => error.Unexpected,
|
|
181
44
|
error.OutOfMemory => error.OutOfMemory,
|
|
@@ -189,13 +52,6 @@ pub fn ThreadType(
|
|
|
189
52
|
self.thread.join();
|
|
190
53
|
self.signal.deinit();
|
|
191
54
|
|
|
192
|
-
self.client.deinit(self.allocator);
|
|
193
|
-
self.message_bus.deinit(self.allocator);
|
|
194
|
-
self.message_pool.deinit(self.allocator);
|
|
195
|
-
self.io.deinit();
|
|
196
|
-
|
|
197
|
-
self.allocator.free(self.addresses);
|
|
198
|
-
self.allocator.free(self.packets);
|
|
199
55
|
self.* = undefined;
|
|
200
56
|
}
|
|
201
57
|
|
|
@@ -205,19 +61,9 @@ pub fn ThreadType(
|
|
|
205
61
|
self.signal.notify();
|
|
206
62
|
}
|
|
207
63
|
|
|
208
|
-
fn run(self: *Self) void {
|
|
209
|
-
while (!self.signal.is_shutdown()) {
|
|
210
|
-
self.client.tick();
|
|
211
|
-
self.io.run_for_ns(config.tick_ms * std.time.ns_per_ms) catch |err| {
|
|
212
|
-
log.err("IO.run() failed with {}", .{err});
|
|
213
|
-
return;
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
64
|
fn on_signal(signal: *Signal) void {
|
|
219
65
|
const self = @fieldParentPtr(Self, "signal", signal);
|
|
220
|
-
self.
|
|
66
|
+
self.context.tick();
|
|
221
67
|
|
|
222
68
|
// Consume all of retry here to avoid infinite loop
|
|
223
69
|
// if the code below pushes to self.retry while we're dequeueing.
|
|
@@ -234,95 +80,10 @@ pub fn ThreadType(
|
|
|
234
80
|
}
|
|
235
81
|
|
|
236
82
|
// Process packets from either pending or submitted as long as we have messages.
|
|
237
|
-
while (self.
|
|
83
|
+
while (self.context.messages_available > 0) {
|
|
238
84
|
const packet = pending.pop() orelse self.submitted.pop() orelse break;
|
|
239
|
-
|
|
240
|
-
defer self.client.unref(message);
|
|
241
|
-
|
|
242
|
-
self.available_messages -= 1;
|
|
243
|
-
self.request(packet, message);
|
|
85
|
+
self.context.request(packet);
|
|
244
86
|
}
|
|
245
87
|
}
|
|
246
|
-
|
|
247
|
-
fn request(self: *Self, packet: *Packet, message: *Message) void {
|
|
248
|
-
// Get the size of each request structure in the packet.data
|
|
249
|
-
const request_size: usize = operation_size_of(packet.operation) orelse {
|
|
250
|
-
return self.on_complete(packet, error.InvalidOperation);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
// Make sure the packet.data size is correct.
|
|
254
|
-
const readable = packet.data[0..packet.data_size];
|
|
255
|
-
if (readable.len == 0 or readable.len % request_size != 0) {
|
|
256
|
-
return self.on_complete(packet, error.InvalidDataSize);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Make sure the packet.data wouldn't overflow a message.
|
|
260
|
-
const writable = message.buffer[@sizeOf(Header)..];
|
|
261
|
-
if (readable.len > writable.len) {
|
|
262
|
-
return self.on_complete(packet, error.TooMuchData);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Write the packet data to the message
|
|
266
|
-
std.mem.copy(u8, writable, readable);
|
|
267
|
-
const wrote = readable.len;
|
|
268
|
-
|
|
269
|
-
// .. and submit the message for processing
|
|
270
|
-
self.client.request(
|
|
271
|
-
@bitCast(u128, UserData{
|
|
272
|
-
.self = self,
|
|
273
|
-
.packet = packet,
|
|
274
|
-
}),
|
|
275
|
-
Self.on_result,
|
|
276
|
-
@intToEnum(Operation, packet.operation),
|
|
277
|
-
message,
|
|
278
|
-
wrote,
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const UserData = packed struct {
|
|
283
|
-
self: *Self,
|
|
284
|
-
packet: *Packet,
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
fn on_result(raw_user_data: u128, op: Operation, results: Client.Error![]const u8) void {
|
|
288
|
-
const user_data = @bitCast(UserData, raw_user_data);
|
|
289
|
-
const self = user_data.self;
|
|
290
|
-
const packet = user_data.packet;
|
|
291
|
-
|
|
292
|
-
assert(packet.operation == @enumToInt(op));
|
|
293
|
-
self.on_complete(packet, results);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const PacketError = Client.Error || error{
|
|
297
|
-
TooMuchData,
|
|
298
|
-
InvalidOperation,
|
|
299
|
-
InvalidDataSize,
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
fn on_complete(
|
|
303
|
-
self: *Self,
|
|
304
|
-
packet: *Packet,
|
|
305
|
-
result: PacketError![]const u8,
|
|
306
|
-
) void {
|
|
307
|
-
assert(self.available_messages < message_pool.messages_max_client);
|
|
308
|
-
self.available_messages += 1;
|
|
309
|
-
|
|
310
|
-
const bytes = result catch |err| {
|
|
311
|
-
packet.status = switch (err) {
|
|
312
|
-
// If there's too many requests, (re)try submitting the packet later
|
|
313
|
-
error.TooManyOutstandingRequests => {
|
|
314
|
-
return self.retry.push(Packet.List.from(packet));
|
|
315
|
-
},
|
|
316
|
-
error.TooMuchData => .too_much_data,
|
|
317
|
-
error.InvalidOperation => .invalid_operation,
|
|
318
|
-
error.InvalidDataSize => .invalid_data_size,
|
|
319
|
-
};
|
|
320
|
-
return self.on_completion_fn(self, packet, null);
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
// The packet completed normally
|
|
324
|
-
packet.status = .ok;
|
|
325
|
-
self.on_completion_fn(self, packet, bytes);
|
|
326
|
-
}
|
|
327
88
|
};
|
|
328
89
|
}
|
|
@@ -120,6 +120,7 @@ typedef enum TB_CREATE_TRANSFER_RESULT {
|
|
|
120
120
|
TB_CREATE_TRANSFER_OVERFLOWS_CREDITS_POSTED,
|
|
121
121
|
TB_CREATE_TRANSFER_OVERFLOWS_DEBITS,
|
|
122
122
|
TB_CREATE_TRANSFER_OVERFLOWS_CREDITS,
|
|
123
|
+
TB_CREATE_TRANSFER_OVERFLOWS_TIMEOUT,
|
|
123
124
|
|
|
124
125
|
TB_CREATE_TRANSFER_EXCEEDS_CREDITS,
|
|
125
126
|
TB_CREATE_TRANSFER_EXCEEDS_DEBITS,
|
|
@@ -193,9 +194,11 @@ typedef enum TB_STATUS {
|
|
|
193
194
|
TB_STATUS_SUCCESS = 0,
|
|
194
195
|
TB_STATUS_UNEXPECTED = 1,
|
|
195
196
|
TB_STATUS_OUT_OF_MEMORY = 2,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
TB_STATUS_ADDRESS_INVALID = 3,
|
|
198
|
+
TB_STATUS_ADDRESS_LIMIT_EXCEEDED = 4,
|
|
199
|
+
TB_STATUS_PACKETS_COUNT_INVALID = 5,
|
|
200
|
+
TB_STATUS_SYSTEM_RESOURCES = 6,
|
|
201
|
+
TB_STATUS_NETWORK_SUBSYSTEM = 7,
|
|
199
202
|
} TB_STATUS;
|
|
200
203
|
|
|
201
204
|
TB_STATUS tb_client_init(
|
|
@@ -204,7 +207,18 @@ TB_STATUS tb_client_init(
|
|
|
204
207
|
uint32_t cluster_id,
|
|
205
208
|
const char* address_ptr,
|
|
206
209
|
uint32_t address_len,
|
|
207
|
-
uint32_t
|
|
210
|
+
uint32_t packets_count,
|
|
211
|
+
uintptr_t on_completion_ctx,
|
|
212
|
+
void (*on_completion_fn)(uintptr_t, tb_client_t, tb_packet_t*, const uint8_t*, uint32_t)
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
TB_STATUS tb_client_init_echo(
|
|
216
|
+
tb_client_t* out_client,
|
|
217
|
+
tb_packet_list_t* out_packets,
|
|
218
|
+
uint32_t cluster_id,
|
|
219
|
+
const char* address_ptr,
|
|
220
|
+
uint32_t address_len,
|
|
221
|
+
uint32_t packets_count,
|
|
208
222
|
uintptr_t on_completion_ctx,
|
|
209
223
|
void (*on_completion_fn)(uintptr_t, tb_client_t, tb_packet_t*, const uint8_t*, uint32_t)
|
|
210
224
|
);
|
|
@@ -10,7 +10,9 @@ pub const tb_status_t = enum(c_int) {
|
|
|
10
10
|
success = 0,
|
|
11
11
|
unexpected,
|
|
12
12
|
out_of_memory,
|
|
13
|
-
|
|
13
|
+
address_invalid,
|
|
14
|
+
address_limit_exceeded,
|
|
15
|
+
packets_count_invalid,
|
|
14
16
|
system_resources,
|
|
15
17
|
network_subsystem,
|
|
16
18
|
};
|
|
@@ -23,9 +25,26 @@ pub const tb_completion_t = fn (
|
|
|
23
25
|
result_len: u32,
|
|
24
26
|
) callconv(.C) void;
|
|
25
27
|
|
|
28
|
+
const config = @import("../config.zig");
|
|
29
|
+
const Storage = @import("../storage.zig").Storage;
|
|
30
|
+
const MessageBus = @import("../message_bus.zig").MessageBusClient;
|
|
31
|
+
const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
|
|
32
|
+
.message_body_size_max = config.message_body_size_max,
|
|
33
|
+
});
|
|
34
|
+
|
|
26
35
|
const ContextType = @import("tb_client/context.zig").ContextType;
|
|
27
36
|
const ContextImplementation = @import("tb_client/context.zig").ContextImplementation;
|
|
28
37
|
|
|
38
|
+
const DefaultContext = blk: {
|
|
39
|
+
const Client = @import("../vsr/client.zig").Client(StateMachine, MessageBus);
|
|
40
|
+
break :blk ContextType(Client);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const TestingContext = blk: {
|
|
44
|
+
const EchoClient = @import("tb_client/echo_client.zig").EchoClient(StateMachine, MessageBus);
|
|
45
|
+
break :blk ContextType(EchoClient);
|
|
46
|
+
};
|
|
47
|
+
|
|
29
48
|
pub fn context_to_client(implementation: *ContextImplementation) tb_client_t {
|
|
30
49
|
return @ptrCast(tb_client_t, implementation);
|
|
31
50
|
}
|
|
@@ -34,24 +53,10 @@ fn client_to_context(tb_client: tb_client_t) *ContextImplementation {
|
|
|
34
53
|
return @ptrCast(*ContextImplementation, @alignCast(@alignOf(ContextImplementation), tb_client));
|
|
35
54
|
}
|
|
36
55
|
|
|
37
|
-
const DefaultContext = blk: {
|
|
38
|
-
const config = @import("../config.zig");
|
|
39
|
-
const Storage = @import("../storage.zig").Storage;
|
|
40
|
-
const MessageBus = @import("../message_bus.zig").MessageBusClient;
|
|
41
|
-
const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
|
|
42
|
-
.message_body_size_max = config.message_body_size_max,
|
|
43
|
-
});
|
|
44
|
-
break :blk ContextType(StateMachine, MessageBus);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// const TestingContext = blk: {
|
|
48
|
-
// const MessageBus = @import("test_message_bus.zig").MessageBusClient;
|
|
49
|
-
// const StateMachine = @import("../state_machine.zig").StateMachine;
|
|
50
|
-
// break :blk ContextType(StateMachine, MessageBus);
|
|
51
|
-
// };
|
|
52
|
-
|
|
53
56
|
// Pick the most suitable allocator
|
|
54
|
-
const global_allocator = if (builtin.
|
|
57
|
+
const global_allocator = if (builtin.is_test)
|
|
58
|
+
std.testing.allocator
|
|
59
|
+
else if (builtin.link_libc)
|
|
55
60
|
std.heap.c_allocator
|
|
56
61
|
else if (builtin.target.os.tag == .windows)
|
|
57
62
|
(struct {
|
|
@@ -66,28 +71,85 @@ pub export fn tb_client_init(
|
|
|
66
71
|
cluster_id: u32,
|
|
67
72
|
addresses_ptr: [*:0]const u8,
|
|
68
73
|
addresses_len: u32,
|
|
69
|
-
|
|
74
|
+
packets_count: u32,
|
|
70
75
|
on_completion_ctx: usize,
|
|
71
76
|
on_completion_fn: tb_completion_t,
|
|
72
77
|
) tb_status_t {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
return init(
|
|
79
|
+
DefaultContext,
|
|
80
|
+
out_client,
|
|
81
|
+
out_packets,
|
|
82
|
+
cluster_id,
|
|
83
|
+
addresses_ptr,
|
|
84
|
+
addresses_len,
|
|
85
|
+
packets_count,
|
|
86
|
+
on_completion_ctx,
|
|
87
|
+
on_completion_fn,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
pub export fn tb_client_init_echo(
|
|
92
|
+
out_client: *tb_client_t,
|
|
93
|
+
out_packets: *tb_packet_list_t,
|
|
94
|
+
cluster_id: u32,
|
|
95
|
+
addresses_ptr: [*:0]const u8,
|
|
96
|
+
addresses_len: u32,
|
|
97
|
+
packets_count: u32,
|
|
98
|
+
on_completion_ctx: usize,
|
|
99
|
+
on_completion_fn: tb_completion_t,
|
|
100
|
+
) tb_status_t {
|
|
101
|
+
return init(
|
|
102
|
+
TestingContext,
|
|
80
103
|
out_client,
|
|
81
104
|
out_packets,
|
|
82
105
|
cluster_id,
|
|
83
106
|
addresses_ptr,
|
|
84
107
|
addresses_len,
|
|
85
|
-
|
|
108
|
+
packets_count,
|
|
86
109
|
on_completion_ctx,
|
|
87
110
|
on_completion_fn,
|
|
88
111
|
);
|
|
89
112
|
}
|
|
90
113
|
|
|
114
|
+
fn init(
|
|
115
|
+
comptime Context: type,
|
|
116
|
+
out_client: *tb_client_t,
|
|
117
|
+
out_packets: *tb_packet_list_t,
|
|
118
|
+
cluster_id: u32,
|
|
119
|
+
addresses_ptr: [*:0]const u8,
|
|
120
|
+
addresses_len: u32,
|
|
121
|
+
packets_count: u32,
|
|
122
|
+
on_completion_ctx: usize,
|
|
123
|
+
on_completion_fn: tb_completion_t,
|
|
124
|
+
) tb_status_t {
|
|
125
|
+
const addresses = @ptrCast([*]const u8, addresses_ptr)[0..addresses_len];
|
|
126
|
+
const context = Context.init(
|
|
127
|
+
global_allocator,
|
|
128
|
+
cluster_id,
|
|
129
|
+
addresses,
|
|
130
|
+
packets_count,
|
|
131
|
+
on_completion_ctx,
|
|
132
|
+
on_completion_fn,
|
|
133
|
+
) catch |err| switch (err) {
|
|
134
|
+
error.Unexpected => return .unexpected,
|
|
135
|
+
error.OutOfMemory => return .out_of_memory,
|
|
136
|
+
error.AddressInvalid => return .address_invalid,
|
|
137
|
+
error.AddressLimitExceeded => return .address_limit_exceeded,
|
|
138
|
+
error.PacketsCountInvalid => return .packets_count_invalid,
|
|
139
|
+
error.SystemResources => return .system_resources,
|
|
140
|
+
error.NetworkSubsystemFailed => return .network_subsystem,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
out_client.* = context_to_client(&context.implementation);
|
|
144
|
+
var list = tb_packet_list_t{};
|
|
145
|
+
for (context.packets) |*packet| {
|
|
146
|
+
list.push(tb_packet_list_t.from(packet));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
out_packets.* = list;
|
|
150
|
+
return .success;
|
|
151
|
+
}
|
|
152
|
+
|
|
91
153
|
pub export fn tb_client_submit(
|
|
92
154
|
client: tb_client_t,
|
|
93
155
|
packets: *tb_packet_list_t,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
|
|
4
|
+
const tb = @import("../tigerbeetle.zig");
|
|
5
|
+
const tb_client = @import("./tb_client.zig");
|
|
6
|
+
const c = @cImport(@cInclude("tb_client.h"));
|
|
7
|
+
|
|
8
|
+
fn to_lowercase(comptime input: []const u8) []const u8 {
|
|
9
|
+
comptime var lowercase: [input.len]u8 = undefined;
|
|
10
|
+
inline for (input) |char, i| {
|
|
11
|
+
const is_uppercase = (char >= 'A') and (char <= 'Z');
|
|
12
|
+
lowercase[i] = char + (@as(u8, @boolToInt(is_uppercase)) * 32);
|
|
13
|
+
}
|
|
14
|
+
return &lowercase;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn to_uppercase(comptime input: []const u8) []const u8 {
|
|
18
|
+
comptime var uppercase: [input.len]u8 = undefined;
|
|
19
|
+
inline for (input) |char, i| {
|
|
20
|
+
const is_lowercase = (char >= 'a') and (char <= 'z');
|
|
21
|
+
uppercase[i] = char - (@as(u8, @boolToInt(is_lowercase)) * 32);
|
|
22
|
+
}
|
|
23
|
+
return &uppercase;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fn to_snakecase(comptime input: []const u8) []const u8 {
|
|
27
|
+
comptime var output: []const u8 = &.{};
|
|
28
|
+
inline for (input) |char, i| {
|
|
29
|
+
const is_uppercase = (char >= 'A') and (char <= 'Z');
|
|
30
|
+
if (is_uppercase and i > 0) output = "_" ++ output;
|
|
31
|
+
output = output ++ &[_]u8{char};
|
|
32
|
+
}
|
|
33
|
+
return output;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test "valid tb_client.h" {
|
|
37
|
+
@setEvalBranchQuota(10_000);
|
|
38
|
+
|
|
39
|
+
inline for (.{
|
|
40
|
+
.{ tb.Account, "tb_account_t" },
|
|
41
|
+
.{ tb.Transfer, "tb_transfer_t" },
|
|
42
|
+
.{ tb.AccountFlags, "TB_ACCOUNT_FLAGS" },
|
|
43
|
+
.{ tb.TransferFlags, "TB_TRANSFER_FLAGS" },
|
|
44
|
+
.{ tb.CreateAccountResult, "TB_CREATE_ACCOUNT_RESULT" },
|
|
45
|
+
.{ tb.CreateTransferResult, "TB_CREATE_TRANSFER_RESULT" },
|
|
46
|
+
.{ tb.CreateAccountsResult, "tb_create_accounts_result_t" },
|
|
47
|
+
.{ tb.CreateTransfersResult, "tb_create_transfers_result_t" },
|
|
48
|
+
|
|
49
|
+
.{ u128, "tb_uint128_t" },
|
|
50
|
+
.{ tb_client.tb_status_t, "TB_STATUS" },
|
|
51
|
+
.{ tb_client.tb_client_t, "tb_client_t" },
|
|
52
|
+
.{ tb_client.tb_packet_t, "tb_packet_t" },
|
|
53
|
+
.{ tb_client.tb_packet_list_t, "tb_packet_list_t" },
|
|
54
|
+
.{ tb_client.tb_packet_status_t, "TB_PACKET_STATUS" },
|
|
55
|
+
}) |c_export| {
|
|
56
|
+
const ty: type = c_export[0];
|
|
57
|
+
const c_type_name = @as([]const u8, c_export[1]);
|
|
58
|
+
const c_type: type = @field(c, c_type_name);
|
|
59
|
+
|
|
60
|
+
switch (@typeInfo(ty)) {
|
|
61
|
+
.Int => comptime assert(ty == c_type),
|
|
62
|
+
.Pointer => comptime assert(@sizeOf(ty) == @sizeOf(c_type)),
|
|
63
|
+
.Enum => {
|
|
64
|
+
const prefix_offset = comptime std.mem.lastIndexOf(u8, c_type_name, "_").?;
|
|
65
|
+
comptime var c_enum_prefix: []const u8 = c_type_name[0 .. prefix_offset + 1];
|
|
66
|
+
comptime assert(c_type == c_uint);
|
|
67
|
+
|
|
68
|
+
// TB_STATUS is a special case in naming
|
|
69
|
+
if (comptime std.mem.eql(u8, c_type_name, "TB_STATUS")) {
|
|
70
|
+
c_enum_prefix = c_type_name ++ "_";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Compare the enum int values in C to the enum int values in Zig.
|
|
74
|
+
inline for (std.meta.fields(ty)) |field| {
|
|
75
|
+
const c_enum_field = comptime to_uppercase(to_snakecase(field.name));
|
|
76
|
+
const c_value = @field(c, c_enum_prefix ++ c_enum_field);
|
|
77
|
+
|
|
78
|
+
const zig_value = @enumToInt(@field(ty, field.name));
|
|
79
|
+
comptime assert(zig_value == c_value);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
.Struct => |type_info| switch (type_info.layout) {
|
|
83
|
+
.Auto => @compileError("struct must be extern or packed to be used in C"),
|
|
84
|
+
.Packed => {
|
|
85
|
+
const prefix_offset = comptime std.mem.lastIndexOf(u8, c_type_name, "_").?;
|
|
86
|
+
const c_enum_prefix = c_type_name[0 .. prefix_offset + 1];
|
|
87
|
+
comptime assert(c_type == c_uint);
|
|
88
|
+
|
|
89
|
+
inline for (std.meta.fields(ty)) |field| {
|
|
90
|
+
if (comptime !std.mem.eql(u8, field.name, "padding")) {
|
|
91
|
+
// Get the bit value in the C enum.
|
|
92
|
+
const c_enum_field = comptime to_uppercase(to_snakecase(field.name));
|
|
93
|
+
const c_value = @field(c, c_enum_prefix ++ c_enum_field);
|
|
94
|
+
|
|
95
|
+
// Compare the bit value to the packed struct's field.
|
|
96
|
+
comptime var instance = std.mem.zeroes(ty);
|
|
97
|
+
@field(instance, field.name) = true;
|
|
98
|
+
comptime assert(@bitCast(u16, instance) == c_value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
.Extern => {
|
|
103
|
+
// Ensure structs are effectively the same.
|
|
104
|
+
comptime assert(@sizeOf(ty) == @sizeOf(c_type));
|
|
105
|
+
comptime assert(@alignOf(ty) == @alignOf(c_type));
|
|
106
|
+
|
|
107
|
+
inline for (std.meta.fields(ty)) |field| {
|
|
108
|
+
// In C, packed structs and enums are replaced with integers.
|
|
109
|
+
comptime var field_type = field.field_type;
|
|
110
|
+
switch (@typeInfo(field_type)) {
|
|
111
|
+
.Struct => |info| {
|
|
112
|
+
comptime assert(info.layout == .Packed);
|
|
113
|
+
comptime assert(@sizeOf(field_type) <= @sizeOf(u128));
|
|
114
|
+
field_type = std.meta.Int(.unsigned, @bitSizeOf(field_type));
|
|
115
|
+
},
|
|
116
|
+
.Enum => |info| field_type = info.tag_type,
|
|
117
|
+
else => {},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// In C, pointers are opaque so we compare only the field sizes,
|
|
121
|
+
comptime var c_field_type = @TypeOf(@field(@as(c_type, undefined), field.name));
|
|
122
|
+
switch (@typeInfo(c_field_type)) {
|
|
123
|
+
.Pointer => |info| {
|
|
124
|
+
comptime assert(info.size == .C);
|
|
125
|
+
comptime assert(@sizeOf(c_field_type) == @sizeOf(field_type));
|
|
126
|
+
},
|
|
127
|
+
else => comptime assert(c_field_type == field_type),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
else => |i| @compileLog("TODO", i),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|