tigerbeetle-node 0.8.0 → 0.10.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.
Files changed (99) hide show
  1. package/README.md +47 -47
  2. package/dist/benchmark.js +15 -15
  3. package/dist/benchmark.js.map +1 -1
  4. package/dist/index.d.ts +66 -61
  5. package/dist/index.js +66 -61
  6. package/dist/index.js.map +1 -1
  7. package/dist/test.js +1 -1
  8. package/dist/test.js.map +1 -1
  9. package/package.json +14 -16
  10. package/scripts/download_node_headers.sh +3 -1
  11. package/src/index.ts +5 -0
  12. package/src/node.zig +18 -19
  13. package/src/tigerbeetle/scripts/benchmark.bat +47 -46
  14. package/src/tigerbeetle/scripts/benchmark.sh +25 -10
  15. package/src/tigerbeetle/scripts/install.sh +2 -1
  16. package/src/tigerbeetle/scripts/install_zig.bat +109 -109
  17. package/src/tigerbeetle/scripts/install_zig.sh +18 -18
  18. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +12 -3
  19. package/src/tigerbeetle/scripts/vopr.bat +47 -47
  20. package/src/tigerbeetle/scripts/vopr.sh +5 -5
  21. package/src/tigerbeetle/src/benchmark.zig +17 -9
  22. package/src/tigerbeetle/src/benchmark_array_search.zig +317 -0
  23. package/src/tigerbeetle/src/benchmarks/perf.zig +299 -0
  24. package/src/tigerbeetle/src/c/tb_client/context.zig +103 -0
  25. package/src/tigerbeetle/src/c/tb_client/packet.zig +80 -0
  26. package/src/tigerbeetle/src/c/tb_client/signal.zig +288 -0
  27. package/src/tigerbeetle/src/c/tb_client/thread.zig +329 -0
  28. package/src/tigerbeetle/src/c/tb_client.h +201 -0
  29. package/src/tigerbeetle/src/c/tb_client.zig +101 -0
  30. package/src/tigerbeetle/src/c/test.zig +1 -0
  31. package/src/tigerbeetle/src/cli.zig +142 -83
  32. package/src/tigerbeetle/src/config.zig +136 -23
  33. package/src/tigerbeetle/src/demo.zig +12 -8
  34. package/src/tigerbeetle/src/demo_03_create_transfers.zig +3 -3
  35. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +10 -10
  36. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +7 -7
  37. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +3 -3
  38. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +1 -1
  39. package/src/tigerbeetle/src/ewah.zig +318 -0
  40. package/src/tigerbeetle/src/ewah_benchmark.zig +121 -0
  41. package/src/tigerbeetle/src/eytzinger_benchmark.zig +317 -0
  42. package/src/tigerbeetle/src/fifo.zig +17 -1
  43. package/src/tigerbeetle/src/io/darwin.zig +12 -10
  44. package/src/tigerbeetle/src/io/linux.zig +25 -9
  45. package/src/tigerbeetle/src/io/windows.zig +13 -9
  46. package/src/tigerbeetle/src/iops.zig +101 -0
  47. package/src/tigerbeetle/src/lsm/binary_search.zig +214 -0
  48. package/src/tigerbeetle/src/lsm/bloom_filter.zig +82 -0
  49. package/src/tigerbeetle/src/lsm/compaction.zig +603 -0
  50. package/src/tigerbeetle/src/lsm/composite_key.zig +75 -0
  51. package/src/tigerbeetle/src/lsm/direction.zig +11 -0
  52. package/src/tigerbeetle/src/lsm/eytzinger.zig +587 -0
  53. package/src/tigerbeetle/src/lsm/forest.zig +630 -0
  54. package/src/tigerbeetle/src/lsm/grid.zig +473 -0
  55. package/src/tigerbeetle/src/lsm/groove.zig +939 -0
  56. package/src/tigerbeetle/src/lsm/k_way_merge.zig +452 -0
  57. package/src/tigerbeetle/src/lsm/level_iterator.zig +296 -0
  58. package/src/tigerbeetle/src/lsm/manifest.zig +680 -0
  59. package/src/tigerbeetle/src/lsm/manifest_level.zig +1169 -0
  60. package/src/tigerbeetle/src/lsm/manifest_log.zig +904 -0
  61. package/src/tigerbeetle/src/lsm/node_pool.zig +231 -0
  62. package/src/tigerbeetle/src/lsm/posted_groove.zig +399 -0
  63. package/src/tigerbeetle/src/lsm/segmented_array.zig +998 -0
  64. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +844 -0
  65. package/src/tigerbeetle/src/lsm/table.zig +932 -0
  66. package/src/tigerbeetle/src/lsm/table_immutable.zig +196 -0
  67. package/src/tigerbeetle/src/lsm/table_iterator.zig +295 -0
  68. package/src/tigerbeetle/src/lsm/table_mutable.zig +123 -0
  69. package/src/tigerbeetle/src/lsm/test.zig +429 -0
  70. package/src/tigerbeetle/src/lsm/tree.zig +1085 -0
  71. package/src/tigerbeetle/src/main.zig +121 -95
  72. package/src/tigerbeetle/src/message_bus.zig +49 -48
  73. package/src/tigerbeetle/src/message_pool.zig +19 -3
  74. package/src/tigerbeetle/src/ring_buffer.zig +172 -31
  75. package/src/tigerbeetle/src/simulator.zig +171 -43
  76. package/src/tigerbeetle/src/state_machine.zig +1026 -599
  77. package/src/tigerbeetle/src/storage.zig +46 -16
  78. package/src/tigerbeetle/src/test/cluster.zig +257 -78
  79. package/src/tigerbeetle/src/test/message_bus.zig +15 -24
  80. package/src/tigerbeetle/src/test/network.zig +26 -17
  81. package/src/tigerbeetle/src/test/packet_simulator.zig +14 -1
  82. package/src/tigerbeetle/src/test/state_checker.zig +10 -6
  83. package/src/tigerbeetle/src/test/state_machine.zig +159 -68
  84. package/src/tigerbeetle/src/test/storage.zig +137 -49
  85. package/src/tigerbeetle/src/tigerbeetle.zig +5 -0
  86. package/src/tigerbeetle/src/unit_tests.zig +8 -0
  87. package/src/tigerbeetle/src/util.zig +51 -0
  88. package/src/tigerbeetle/src/vsr/client.zig +21 -7
  89. package/src/tigerbeetle/src/vsr/journal.zig +1429 -514
  90. package/src/tigerbeetle/src/vsr/replica.zig +1855 -550
  91. package/src/tigerbeetle/src/vsr/superblock.zig +1743 -0
  92. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +258 -0
  93. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +644 -0
  94. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +546 -0
  95. package/src/tigerbeetle/src/vsr.zig +134 -52
  96. package/.yarn/releases/yarn-berry.cjs +0 -55
  97. package/.yarnrc.yml +0 -1
  98. package/scripts/postinstall.sh +0 -6
  99. package/yarn.lock +0 -42
@@ -0,0 +1,101 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ /// Take a u6 to limit to 64 items max (2^6 = 64)
5
+ pub fn IOPS(comptime T: type, comptime size: u6) type {
6
+ const Map = std.StaticBitSet(size);
7
+ return struct {
8
+ const Self = @This();
9
+
10
+ items: [size]T = undefined,
11
+ /// 1 bits are free items.
12
+ free: Map = Map.initFull(),
13
+
14
+ pub fn acquire(self: *Self) ?*T {
15
+ const i = self.free.findFirstSet() orelse return null;
16
+ self.free.unset(i);
17
+ return &self.items[i];
18
+ }
19
+
20
+ pub fn release(self: *Self, item: *T) void {
21
+ item.* = undefined;
22
+ const i = (@ptrToInt(item) - @ptrToInt(&self.items)) / @sizeOf(T);
23
+ assert(!self.free.isSet(i));
24
+ self.free.set(i);
25
+ }
26
+
27
+ /// Returns the count of IOPs available.
28
+ pub fn available(self: *const Self) usize {
29
+ return self.free.count();
30
+ }
31
+
32
+ /// Returns the count of IOPs in use.
33
+ pub fn executing(self: *const Self) usize {
34
+ return size - self.available();
35
+ }
36
+
37
+ pub const Iterator = struct {
38
+ iops: *Self,
39
+ bitset_iterator: Map.Iterator(.{ .kind = .unset }),
40
+
41
+ pub fn next(iterator: *@This()) ?*T {
42
+ const i = iterator.bitset_iterator.next() orelse return null;
43
+ return &iterator.iops.items[i];
44
+ }
45
+ };
46
+
47
+ pub fn iterate(self: *Self) Iterator {
48
+ return .{
49
+ .iops = self,
50
+ .bitset_iterator = self.free.iterator(.{ .kind = .unset }),
51
+ };
52
+ }
53
+ };
54
+ }
55
+
56
+ test "IOPS" {
57
+ const testing = std.testing;
58
+ var iops = IOPS(u32, 4){};
59
+
60
+ try testing.expectEqual(@as(usize, 4), iops.available());
61
+ try testing.expectEqual(@as(usize, 0), iops.executing());
62
+
63
+ var one = iops.acquire().?;
64
+
65
+ try testing.expectEqual(@as(usize, 3), iops.available());
66
+ try testing.expectEqual(@as(usize, 1), iops.executing());
67
+
68
+ var two = iops.acquire().?;
69
+ var three = iops.acquire().?;
70
+
71
+ try testing.expectEqual(@as(usize, 1), iops.available());
72
+ try testing.expectEqual(@as(usize, 3), iops.executing());
73
+
74
+ var four = iops.acquire().?;
75
+ try testing.expectEqual(@as(?*u32, null), iops.acquire());
76
+
77
+ try testing.expectEqual(@as(usize, 0), iops.available());
78
+ try testing.expectEqual(@as(usize, 4), iops.executing());
79
+
80
+ iops.release(two);
81
+
82
+ try testing.expectEqual(@as(usize, 1), iops.available());
83
+ try testing.expectEqual(@as(usize, 3), iops.executing());
84
+
85
+ // there is only one slot free, so we will get the same pointer back.
86
+ try testing.expectEqual(@as(?*u32, two), iops.acquire());
87
+
88
+ iops.release(four);
89
+ iops.release(two);
90
+ iops.release(one);
91
+ iops.release(three);
92
+
93
+ try testing.expectEqual(@as(usize, 4), iops.available());
94
+ try testing.expectEqual(@as(usize, 0), iops.executing());
95
+
96
+ one = iops.acquire().?;
97
+ two = iops.acquire().?;
98
+ three = iops.acquire().?;
99
+ four = iops.acquire().?;
100
+ try testing.expectEqual(@as(?*u32, null), iops.acquire());
101
+ }
@@ -0,0 +1,214 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+ const math = std.math;
4
+
5
+ // TODO Add prefeching when @prefetch is available: https://github.com/ziglang/zig/issues/3600.
6
+ //
7
+ // TODO The Zig self hosted compiler will implement inlining itself before passing the IR to llvm,
8
+ // which should eliminate the current poor codegen of key_from_value/compare_keys.
9
+
10
+ /// Returns the index of the key either exactly equal to the target key or, if there is no exact
11
+ /// match, the next greatest key.
12
+ /// Doesn't preform the extra key comparison to determine if the match is exact
13
+ pub fn binary_search_values_raw(
14
+ comptime Key: type,
15
+ comptime Value: type,
16
+ comptime key_from_value: fn (*const Value) callconv(.Inline) Key,
17
+ comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
18
+ values: []const Value,
19
+ key: Key,
20
+ ) u32 {
21
+ assert(values.len > 0);
22
+
23
+ var offset: usize = 0;
24
+ var length: usize = values.len;
25
+ while (length > 1) {
26
+ const half = length / 2;
27
+ const mid = offset + half;
28
+
29
+ // This trick seems to be what's needed to get llvm to emit branchless code for this,
30
+ // a ternay-style if expression was generated as a jump here for whatever reason.
31
+ const next_offsets = [_]usize{ offset, mid };
32
+ offset = next_offsets[@boolToInt(compare_keys(key_from_value(&values[mid]), key) == .lt)];
33
+
34
+ length -= half;
35
+ }
36
+
37
+ return @intCast(u32, offset + @boolToInt(compare_keys(key_from_value(&values[offset]), key) == .lt));
38
+ }
39
+
40
+ pub inline fn binary_search_keys_raw(
41
+ comptime Key: type,
42
+ comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
43
+ keys: []const Key,
44
+ key: Key,
45
+ ) u32 {
46
+ return binary_search_values_raw(
47
+ Key,
48
+ Key,
49
+ struct {
50
+ inline fn key_from_key(k: *const Key) Key {
51
+ return k.*;
52
+ }
53
+ }.key_from_key,
54
+ compare_keys,
55
+ keys,
56
+ key,
57
+ );
58
+ }
59
+
60
+ const BinarySearchResult = struct {
61
+ index: u32,
62
+ exact: bool,
63
+ };
64
+
65
+ pub inline fn binary_search_values(
66
+ comptime Key: type,
67
+ comptime Value: type,
68
+ comptime key_from_value: fn (*const Value) callconv(.Inline) Key,
69
+ comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
70
+ values: []const Value,
71
+ key: Key,
72
+ ) BinarySearchResult {
73
+ const index = binary_search_values_raw(Key, Value, key_from_value, compare_keys, values, key);
74
+ return .{
75
+ .index = index,
76
+ .exact = index < values.len and compare_keys(key_from_value(&values[index]), key) == .eq,
77
+ };
78
+ }
79
+
80
+ pub inline fn binary_search_keys(
81
+ comptime Key: type,
82
+ comptime compare_keys: fn (Key, Key) callconv(.Inline) math.Order,
83
+ keys: []const Key,
84
+ key: Key,
85
+ ) BinarySearchResult {
86
+ const index = binary_search_keys_raw(Key, compare_keys, keys, key);
87
+ return .{
88
+ .index = index,
89
+ .exact = index < keys.len and compare_keys(keys[index], key) == .eq,
90
+ };
91
+ }
92
+
93
+ const test_binary_search = struct {
94
+ const log = false;
95
+
96
+ const gpa = std.testing.allocator;
97
+
98
+ inline fn compare_keys(a: u32, b: u32) math.Order {
99
+ return math.order(a, b);
100
+ }
101
+
102
+ fn exhaustive_search(keys_count: u32) !void {
103
+ const keys = try gpa.alloc(u32, keys_count);
104
+ defer gpa.free(keys);
105
+
106
+ for (keys) |*key, i| key.* = @intCast(u32, 7 * i + 3);
107
+
108
+ var target_key: u32 = 0;
109
+ while (target_key < keys_count + 13) : (target_key += 1) {
110
+ var expect: BinarySearchResult = .{ .index = 0, .exact = false };
111
+ for (keys) |key, i| {
112
+ switch (compare_keys(key, target_key)) {
113
+ .lt => expect.index = @intCast(u32, i) + 1,
114
+ .eq => {
115
+ expect.exact = true;
116
+ break;
117
+ },
118
+ .gt => break,
119
+ }
120
+ }
121
+
122
+ if (log) {
123
+ std.debug.print("keys:", .{});
124
+ for (keys) |k| std.debug.print("{},", .{k});
125
+ std.debug.print("\n", .{});
126
+ std.debug.print("target key: {}\n", .{target_key});
127
+ }
128
+
129
+ const actual = binary_search_keys(
130
+ u32,
131
+ compare_keys,
132
+ keys,
133
+ target_key,
134
+ );
135
+
136
+ if (log) std.debug.print("expected: {}, actual: {}\n", .{ expect, actual });
137
+ try std.testing.expectEqual(expect.index, actual.index);
138
+ try std.testing.expectEqual(expect.exact, actual.exact);
139
+ }
140
+ }
141
+
142
+ fn explicit_search(
143
+ keys: []const u32,
144
+ target_keys: []const u32,
145
+ expected_results: []const BinarySearchResult,
146
+ ) !void {
147
+ assert(target_keys.len == expected_results.len);
148
+
149
+ for (target_keys) |target_key, i| {
150
+ if (log) {
151
+ std.debug.print("keys:", .{});
152
+ for (keys) |k| std.debug.print("{},", .{k});
153
+ std.debug.print("\n", .{});
154
+ std.debug.print("target key: {}\n", .{target_key});
155
+ }
156
+ const expect = expected_results[i];
157
+ const actual = binary_search_keys(
158
+ u32,
159
+ compare_keys,
160
+ keys,
161
+ target_key,
162
+ );
163
+ try std.testing.expectEqual(expect.index, actual.index);
164
+ try std.testing.expectEqual(expect.exact, actual.exact);
165
+ }
166
+ }
167
+ };
168
+
169
+ // TODO test search on empty slice
170
+ test "binary search: exhaustive" {
171
+ if (test_binary_search.log) std.debug.print("\n", .{});
172
+ var i: u32 = 1;
173
+ while (i < 300) : (i += 1) {
174
+ try test_binary_search.exhaustive_search(i);
175
+ }
176
+ }
177
+
178
+ test "binary search: explicit" {
179
+ if (test_binary_search.log) std.debug.print("\n", .{});
180
+ try test_binary_search.explicit_search(
181
+ &[_]u32{ 0, 3, 5, 8, 9, 11 },
182
+ &[_]u32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
183
+ &[_]BinarySearchResult{
184
+ .{ .index = 0, .exact = true },
185
+ .{ .index = 1, .exact = false },
186
+ .{ .index = 1, .exact = false },
187
+ .{ .index = 1, .exact = true },
188
+ .{ .index = 2, .exact = false },
189
+ .{ .index = 2, .exact = true },
190
+ .{ .index = 3, .exact = false },
191
+ .{ .index = 3, .exact = false },
192
+ .{ .index = 3, .exact = true },
193
+ .{ .index = 4, .exact = true },
194
+ .{ .index = 5, .exact = false },
195
+ .{ .index = 5, .exact = true },
196
+ .{ .index = 6, .exact = false },
197
+ .{ .index = 6, .exact = false },
198
+ },
199
+ );
200
+ }
201
+
202
+ test "binary search: duplicates" {
203
+ if (test_binary_search.log) std.debug.print("\n", .{});
204
+ try test_binary_search.explicit_search(
205
+ &[_]u32{ 0, 0, 3, 3, 3, 5, 5, 5, 5 },
206
+ &[_]u32{ 1, 2, 4, 6 },
207
+ &[_]BinarySearchResult{
208
+ .{ .index = 2, .exact = false },
209
+ .{ .index = 2, .exact = false },
210
+ .{ .index = 5, .exact = false },
211
+ .{ .index = 9, .exact = false },
212
+ },
213
+ );
214
+ }
@@ -0,0 +1,82 @@
1
+ //! Implementation of Split block Bloom filters: https://arxiv.org/pdf/2101.01719v4.pdf
2
+
3
+ const std = @import("std");
4
+ const assert = std.debug.assert;
5
+ const mem = std.mem;
6
+ const meta = std.meta;
7
+
8
+ pub const Fingerprint = struct {
9
+ /// Hash value used to map key to block.
10
+ hash: u32,
11
+ /// Mask of bits set in the block for the key.
12
+ mask: meta.Vector(8, u32),
13
+
14
+ pub fn create(key: []const u8) Fingerprint {
15
+ const hash = std.hash.Wyhash.hash(0, key);
16
+ const hash_lower = @truncate(u32, hash);
17
+ const hash_upper = @intCast(u32, hash >> 32);
18
+
19
+ // TODO These constants are from the paper and we understand them to be arbitrary odd
20
+ // integers. Experimentally compare the performance of these with other randomly chosen
21
+ // odd integers to verify/improve our understanding.
22
+ const odd_integers: meta.Vector(8, u32) = [8]u32{
23
+ 0x47b6137b,
24
+ 0x44974d91,
25
+ 0x8824ad5b,
26
+ 0xa2b7289d,
27
+ 0x705495c7,
28
+ 0x2df1424b,
29
+ 0x9efc4947,
30
+ 0x5c6bfb31,
31
+ };
32
+
33
+ // Multiply-shift hashing. This produces 8 values in the range 0 to 31 (2^5 - 1).
34
+ const bit_indexes = (odd_integers *% @splat(8, hash_lower)) >> @splat(8, @as(u5, 32 - 5));
35
+
36
+ return .{
37
+ .hash = hash_upper,
38
+ .mask = @splat(8, @as(u32, 1)) << @intCast(meta.Vector(8, u5), bit_indexes),
39
+ };
40
+ }
41
+ };
42
+
43
+ /// Add the key with the given fingerprint to the filter.
44
+ /// filter.len must be a multiple of 32.
45
+ pub fn add(fingerprint: Fingerprint, filter: []u8) void {
46
+ comptime assert(@sizeOf(meta.Vector(8, u32)) == 32);
47
+
48
+ assert(filter.len > 0);
49
+ assert(filter.len % @sizeOf(meta.Vector(8, u32)) == 0);
50
+
51
+ const blocks = mem.bytesAsSlice([8]u32, filter);
52
+ const index = block_index(fingerprint.hash, filter.len);
53
+
54
+ const current: meta.Vector(8, u32) = blocks[index];
55
+ blocks[index] = current | fingerprint.mask;
56
+ }
57
+
58
+ /// Check if the key with the given fingerprint may have been added to the filter.
59
+ /// filter.len must be a multiple of 32.
60
+ pub fn may_contain(fingerprint: Fingerprint, filter: []const u8) bool {
61
+ comptime assert(@sizeOf(meta.Vector(8, u32)) == 32);
62
+
63
+ assert(filter.len > 0);
64
+ assert(filter.len % @sizeOf(meta.Vector(8, u32)) == 0);
65
+
66
+ const blocks = mem.bytesAsSlice([8]u32, filter);
67
+ const index = block_index(fingerprint.hash, filter.len);
68
+
69
+ const current: meta.Vector(8, u32) = blocks[index];
70
+ return @reduce(.Or, ~current & fingerprint.mask) == 0;
71
+ }
72
+
73
+ inline fn block_index(hash: u32, size: usize) u32 {
74
+ assert(size > 0);
75
+
76
+ const block_count = @divExact(size, @sizeOf(meta.Vector(8, u32)));
77
+ return @intCast(u32, (@as(u64, hash) * block_count) >> 32);
78
+ }
79
+
80
+ test {
81
+ _ = std.testing.refAllDecls(@This());
82
+ }