tigerbeetle-node 0.11.12 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,220 +0,0 @@
1
- const std = @import("std");
2
- const mem = std.mem;
3
- const math = std.math;
4
- const assert = std.debug.assert;
5
-
6
- const constants = @import("../constants.zig");
7
- const div_ceil = @import("../stdx.zig").div_ceil;
8
- const SetAssociativeCache = @import("set_associative_cache.zig").SetAssociativeCache;
9
-
10
- /// Range queries are not supported on the TableMutable, it must first be made immutable.
11
- pub fn TableMutableType(comptime Table: type) type {
12
- const Key = Table.Key;
13
- const Value = Table.Value;
14
- const compare_keys = Table.compare_keys;
15
- const key_from_value = Table.key_from_value;
16
- const tombstone_from_key = Table.tombstone_from_key;
17
- const tombstone = Table.tombstone;
18
- const usage = Table.usage;
19
-
20
- return struct {
21
- const TableMutable = @This();
22
-
23
- const load_factor = 50;
24
- const Values = std.HashMapUnmanaged(Value, void, Table.HashMapContextValue, load_factor);
25
-
26
- pub const ValuesCache = SetAssociativeCache(
27
- Key,
28
- Value,
29
- Table.key_from_value,
30
- struct {
31
- inline fn hash(key: Key) u64 {
32
- return std.hash.Wyhash.hash(0, mem.asBytes(&key));
33
- }
34
- }.hash,
35
- struct {
36
- inline fn equal(a: Key, b: Key) bool {
37
- return compare_keys(a, b) == .eq;
38
- }
39
- }.equal,
40
- .{},
41
- );
42
-
43
- value_count_max: u32,
44
- values: Values = .{},
45
-
46
- /// Rather than using values.count(), we count how many values we could have had if every
47
- /// operation had been on a different key. This means that mistakes in calculating
48
- /// commit_entries_max are much easier to catch when fuzzing, rather than requiring very
49
- /// specific workloads.
50
- /// Invariant: value_count_worst_case <= value_count_max
51
- value_count_worst_case: u32 = 0,
52
-
53
- /// This is used to accelerate point lookups and is not used for range queries.
54
- /// Secondary index trees used only for range queries can therefore set this to null.
55
- ///
56
- /// The values cache is only used for the latest snapshot for simplicity.
57
- /// Earlier snapshots will still be able to utilize the block cache.
58
- ///
59
- /// The values cache is updated (in bulk) when the mutable table is sorted and frozen,
60
- /// rather than updating on every `put()`/`remove()`.
61
- /// This amortizes cache inserts for hot keys in the mutable table, and avoids redundantly
62
- /// storing duplicate values in both the mutable table and values cache.
63
- // TODO Share cache between trees of different grooves:
64
- // "A set associative cache of values shared by trees with the same key/value sizes.
65
- // The value type will be []u8 and this will be shared by trees with the same value size."
66
- values_cache: ?*ValuesCache,
67
-
68
- /// `commit_entries_max` is the maximum number of Values that can be inserted by a single commit.
69
- pub fn init(
70
- allocator: mem.Allocator,
71
- values_cache: ?*ValuesCache,
72
- commit_entries_max: u32,
73
- ) !TableMutable {
74
- comptime assert(constants.lsm_batch_multiple > 0);
75
- assert(commit_entries_max > 0);
76
-
77
- const value_count_max = commit_entries_max * constants.lsm_batch_multiple;
78
- const data_block_count = div_ceil(value_count_max, Table.data.value_count_max);
79
- assert(data_block_count <= Table.data_block_count_max);
80
-
81
- var values: Values = .{};
82
- try values.ensureTotalCapacity(allocator, value_count_max);
83
- errdefer values.deinit(allocator);
84
-
85
- return TableMutable{
86
- .value_count_max = value_count_max,
87
- .values = values,
88
- .values_cache = values_cache,
89
- };
90
- }
91
-
92
- pub fn deinit(table: *TableMutable, allocator: mem.Allocator) void {
93
- table.values.deinit(allocator);
94
- }
95
-
96
- pub fn get(table: *const TableMutable, key: Key) ?*const Value {
97
- if (table.values.getKeyPtr(tombstone_from_key(key))) |value| {
98
- return value;
99
- }
100
- if (table.values_cache) |cache| {
101
- // Check the cache after the mutable table (see `values_cache` for explanation).
102
- if (cache.get(key)) |value| return value;
103
- }
104
- return null;
105
- }
106
-
107
- pub fn put(table: *TableMutable, value: *const Value) void {
108
- assert(table.value_count_worst_case < table.value_count_max);
109
- table.value_count_worst_case += 1;
110
- switch (usage) {
111
- .secondary_index => {
112
- const existing = table.values.fetchRemove(value.*);
113
- if (existing) |kv| {
114
- // If there was a previous operation on this key then it must have been a remove.
115
- // The put and remove cancel out.
116
- assert(tombstone(&kv.key));
117
- } else {
118
- table.values.putAssumeCapacityNoClobber(value.*, {});
119
- }
120
- },
121
- .general => {
122
- // If the key is already present in the hash map, the old key will not be overwritten
123
- // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
124
- // level getOrPut() API and manually overwrite the old key.
125
- const upsert = table.values.getOrPutAssumeCapacity(value.*);
126
- upsert.key_ptr.* = value.*;
127
- },
128
- }
129
-
130
- // The hash map's load factor may allow for more capacity because of rounding:
131
- assert(table.values.count() <= table.value_count_max);
132
- }
133
-
134
- pub fn remove(table: *TableMutable, value: *const Value) void {
135
- assert(table.value_count_worst_case < table.value_count_max);
136
- table.value_count_worst_case += 1;
137
- switch (usage) {
138
- .secondary_index => {
139
- const existing = table.values.fetchRemove(value.*);
140
- if (existing) |kv| {
141
- // The previous operation on this key then it must have been a put.
142
- // The put and remove cancel out.
143
- assert(!tombstone(&kv.key));
144
- } else {
145
- // If the put is already on-disk, then we need to follow it with a tombstone.
146
- // The put and the tombstone may cancel each other out later during compaction.
147
- table.values.putAssumeCapacityNoClobber(tombstone_from_key(key_from_value(value)), {});
148
- }
149
- },
150
- .general => {
151
- // If the key is already present in the hash map, the old key will not be overwritten
152
- // by the new one if using e.g. putAssumeCapacity(). Instead we must use the lower
153
- // level getOrPut() API and manually overwrite the old key.
154
- const upsert = table.values.getOrPutAssumeCapacity(value.*);
155
- upsert.key_ptr.* = tombstone_from_key(key_from_value(value));
156
- },
157
- }
158
-
159
- assert(table.values.count() <= table.value_count_max);
160
- }
161
-
162
- /// This may return `false` even when committing would succeed — it pessimistically
163
- /// assumes that none of the batch's keys are already in `table.values`.
164
- pub fn can_commit_batch(table: *TableMutable, batch_count: u32) bool {
165
- assert(batch_count <= table.value_count_max);
166
- return (table.value_count_worst_case + batch_count) <= table.value_count_max;
167
- }
168
-
169
- pub fn clear(table: *TableMutable) void {
170
- assert(table.values.count() > 0);
171
- table.value_count_worst_case = 0;
172
- table.values.clearRetainingCapacity();
173
- assert(table.values.count() == 0);
174
- }
175
-
176
- pub fn count(table: *const TableMutable) u32 {
177
- const value = @intCast(u32, table.values.count());
178
- assert(value <= table.value_count_max);
179
- return value;
180
- }
181
-
182
- /// The returned slice is invalidated whenever this is called for any tree.
183
- pub fn sort_into_values_and_clear(
184
- table: *TableMutable,
185
- values_max: []Value,
186
- ) []const Value {
187
- assert(table.count() > 0);
188
- assert(table.count() <= table.value_count_max);
189
- assert(table.count() <= values_max.len);
190
- assert(values_max.len == table.value_count_max);
191
-
192
- var i: usize = 0;
193
- var it = table.values.keyIterator();
194
- while (it.next()) |value| : (i += 1) {
195
- values_max[i] = value.*;
196
-
197
- if (table.values_cache) |cache| {
198
- if (tombstone(value)) {
199
- cache.remove(key_from_value(value));
200
- } else {
201
- cache.insert(value);
202
- }
203
- }
204
- }
205
-
206
- const values = values_max[0..i];
207
- assert(values.len == table.count());
208
- std.sort.sort(Value, values, {}, sort_values_by_key_in_ascending_order);
209
-
210
- table.clear();
211
- assert(table.count() == 0);
212
-
213
- return values;
214
- }
215
-
216
- fn sort_values_by_key_in_ascending_order(_: void, a: Value, b: Value) bool {
217
- return compare_keys(key_from_value(&a), key_from_value(&b)) == .lt;
218
- }
219
- };
220
- }
@@ -1,438 +0,0 @@
1
- const std = @import("std");
2
- const testing = std.testing;
3
- const allocator = testing.allocator;
4
- const assert = std.debug.assert;
5
- const os = std.os;
6
-
7
- const constants = @import("../constants.zig");
8
- const vsr = @import("../vsr.zig");
9
- const log = std.log.scoped(.lsm_forest_test);
10
-
11
- const MessagePool = @import("../message_pool.zig").MessagePool;
12
- const Transfer = @import("../tigerbeetle.zig").Transfer;
13
- const Account = @import("../tigerbeetle.zig").Account;
14
- const Storage = @import("../storage.zig").Storage;
15
- const IO = @import("../io.zig").IO;
16
- const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
17
- .message_body_size_max = constants.message_body_size_max,
18
- });
19
-
20
- const GridType = @import("grid.zig").GridType;
21
- const GrooveType = @import("groove.zig").GrooveType;
22
- const Forest = StateMachine.Forest;
23
-
24
- const Grid = GridType(Storage);
25
- const SuperBlock = vsr.SuperBlockType(Storage);
26
-
27
- const Environment = struct {
28
- const cluster = 32;
29
- const replica = 4;
30
- const storage_size_max = vsr.Zone.superblock.size().? +
31
- vsr.Zone.wal_headers.size().? +
32
- vsr.Zone.wal_prepares.size().? +
33
- (512 + 64) * 1024 * 1024;
34
-
35
- const node_count = 1024;
36
- const cache_entries_max = 2 * 1024 * 1024;
37
- const forest_options = StateMachine.forest_options(.{
38
- .lsm_forest_node_count = node_count,
39
- .cache_entries_accounts = cache_entries_max,
40
- .cache_entries_transfers = cache_entries_max,
41
- .cache_entries_posted = cache_entries_max,
42
- });
43
-
44
- const State = enum {
45
- uninit,
46
- init,
47
- formatted,
48
- superblock_open,
49
- forest_open,
50
- forest_compacting,
51
- forest_checkpointing,
52
- superblock_checkpointing,
53
- };
54
-
55
- state: State,
56
- dir_fd: os.fd_t,
57
- fd: os.fd_t,
58
- io: IO,
59
- storage: Storage,
60
- message_pool: MessagePool,
61
- superblock: SuperBlock,
62
- superblock_context: SuperBlock.Context,
63
- grid: Grid,
64
- forest: Forest,
65
- // We need @fieldParentPtr() of forest, so we can't use an optional Forest.
66
- forest_exists: bool = false,
67
-
68
- fn init(env: *Environment, must_create: bool) !void {
69
- env.state = .uninit;
70
-
71
- const dir_path = ".";
72
- env.dir_fd = try IO.open_dir(dir_path);
73
- errdefer std.os.close(env.dir_fd);
74
-
75
- env.fd = try IO.open_file(env.dir_fd, "test_forest", storage_size_max, must_create);
76
- errdefer std.os.close(env.fd);
77
-
78
- env.io = try IO.init(128, 0);
79
- errdefer env.io.deinit();
80
-
81
- env.storage = try Storage.init(&env.io, env.fd);
82
- errdefer env.storage.deinit();
83
-
84
- env.message_pool = try MessagePool.init(allocator, .replica);
85
- errdefer env.message_pool.deinit(allocator);
86
-
87
- env.superblock = try SuperBlock.init(allocator, .{
88
- .storage = &env.storage,
89
- .storage_size_limit = constants.storage_size_max,
90
- .message_pool = &env.message_pool,
91
- });
92
- env.superblock_context = undefined;
93
- errdefer env.superblock.deinit(allocator);
94
-
95
- env.grid = try Grid.init(allocator, &env.superblock);
96
- errdefer env.grid.deinit(allocator);
97
-
98
- // Forest must be initialized with an open superblock.
99
- env.forest = undefined;
100
- env.forest_exists = false;
101
-
102
- env.state = .init;
103
- }
104
-
105
- fn deinit(env: *Environment) void {
106
- assert(env.state != .uninit);
107
- defer env.state = .uninit;
108
-
109
- if (env.forest_exists) {
110
- env.forest.deinit(allocator);
111
- env.forest_exists = false;
112
- }
113
- env.grid.deinit(allocator);
114
- env.superblock.deinit(allocator);
115
- env.message_pool.deinit(allocator);
116
- env.storage.deinit();
117
- env.io.deinit();
118
- std.os.close(env.fd);
119
- std.os.close(env.dir_fd);
120
- }
121
-
122
- fn tick(env: *Environment) !void {
123
- // env.grid.tick();
124
- try env.io.tick();
125
- }
126
-
127
- pub fn format() !void {
128
- var env: Environment = undefined;
129
-
130
- const must_create = true;
131
- try env.init(must_create);
132
- defer env.deinit();
133
-
134
- assert(env.state == .init);
135
- env.superblock.format(superblock_format_callback, &env.superblock_context, .{
136
- .cluster = cluster,
137
- .replica = replica,
138
- .storage_size_max = storage_size_max,
139
- });
140
-
141
- while (true) {
142
- switch (env.state) {
143
- .init => try env.tick(),
144
- .formatted => break,
145
- else => unreachable,
146
- }
147
- }
148
- }
149
-
150
- fn superblock_format_callback(superblock_context: *SuperBlock.Context) void {
151
- const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
152
- assert(env.state == .init);
153
- env.state = .formatted;
154
- }
155
-
156
- pub fn open(env: *Environment) !void {
157
- assert(env.state == .init);
158
- env.superblock.open(superblock_open_callback, &env.superblock_context);
159
-
160
- while (true) {
161
- switch (env.state) {
162
- .init, .superblock_open => try env.tick(),
163
- .forest_open => break,
164
- else => unreachable,
165
- }
166
- }
167
- }
168
-
169
- fn superblock_open_callback(superblock_context: *SuperBlock.Context) void {
170
- const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
171
- assert(env.state == .init);
172
- env.state = .superblock_open;
173
-
174
- env.forest = Forest.init(allocator, &env.grid, node_count, forest_options) catch unreachable;
175
- env.forest_exists = true;
176
- env.forest.open(forest_open_callback);
177
- }
178
-
179
- fn forest_open_callback(forest: *Forest) void {
180
- const env = @fieldParentPtr(@This(), "forest", forest);
181
- assert(env.state == .superblock_open);
182
- env.state = .forest_open;
183
- }
184
-
185
- pub fn checkpoint(env: *Environment) !void {
186
- assert(env.state == .forest_open);
187
- env.state = .forest_checkpointing;
188
- env.forest.checkpoint(forest_checkpoint_callback);
189
-
190
- while (true) {
191
- switch (env.state) {
192
- .forest_checkpointing, .superblock_checkpointing => try env.tick(),
193
- .forest_open => break,
194
- else => unreachable,
195
- }
196
- }
197
- }
198
-
199
- fn forest_checkpoint_callback(forest: *Forest) void {
200
- const env = @fieldParentPtr(@This(), "forest", forest);
201
- assert(env.state == .forest_checkpointing);
202
-
203
- log.debug("forest checkpointing completed!", .{});
204
-
205
- const vsr_state = &env.superblock.staging.vsr_state;
206
-
207
- env.state = .superblock_checkpointing;
208
- env.superblock.checkpoint(
209
- superblock_checkpoint_callback,
210
- &env.superblock_context,
211
- .{
212
- .commit_min_checkpoint = vsr_state.commit_min_checkpoint + 1,
213
- .commit_min = vsr_state.commit_min + 1,
214
- },
215
- );
216
- }
217
-
218
- fn superblock_checkpoint_callback(superblock_context: *SuperBlock.Context) void {
219
- const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
220
- assert(env.state == .superblock_checkpointing);
221
- env.state = .forest_open;
222
-
223
- log.debug("superblock checkpointing completed!", .{});
224
- }
225
-
226
- pub fn compact(env: *Environment, op: u64) !void {
227
- assert(env.state == .forest_open);
228
- env.state = .forest_compacting;
229
- env.forest.compact(forest_compact_callback, op);
230
-
231
- while (true) {
232
- switch (env.state) {
233
- .forest_compacting => try env.tick(),
234
- .forest_open => break,
235
- else => unreachable,
236
- }
237
- }
238
- }
239
-
240
- fn forest_compact_callback(forest: *Forest) void {
241
- const env = @fieldParentPtr(@This(), "forest", forest);
242
- assert(env.state == .forest_compacting);
243
- env.state = .forest_open;
244
- }
245
-
246
- const Visibility = enum {
247
- visible,
248
- invisible,
249
- };
250
-
251
- pub fn assert_visibility(
252
- env: *Environment,
253
- comptime visibility: Visibility,
254
- groove: anytype,
255
- objects: anytype,
256
- comptime commit_entries_max: u32,
257
- ) !void {
258
- const Groove = @TypeOf(groove.*);
259
- const Object = @TypeOf(objects[0]);
260
-
261
- const VisibilityAssertion = struct {
262
- prefetch_context: Groove.PrefetchContext = undefined,
263
- verify_count: usize = 0,
264
- objects: []const Object,
265
- groove: *Groove,
266
-
267
- fn verify(assertion: *@This()) void {
268
- assert(assertion.verify_count == 0);
269
- assertion.verify_count = std.math.min(commit_entries_max, assertion.objects.len);
270
- if (assertion.verify_count == 0) return;
271
-
272
- assertion.groove.prefetch_setup(null);
273
- for (assertion.objects[0..assertion.verify_count]) |*object| {
274
- assertion.groove.prefetch_enqueue(object.id);
275
- }
276
-
277
- assertion.groove.prefetch(prefetch_callback, &assertion.prefetch_context);
278
- }
279
-
280
- fn prefetch_callback(prefetch_context: *Groove.PrefetchContext) void {
281
- const assertion = @fieldParentPtr(@This(), "prefetch_context", prefetch_context);
282
- assert(assertion.verify_count > 0);
283
-
284
- {
285
- for (assertion.objects[0..assertion.verify_count]) |*object| {
286
- log.debug("verifying {} for id={}", .{ visibility, object.id });
287
- const result = assertion.groove.get(object.id);
288
-
289
- switch (visibility) {
290
- .invisible => assert(result == null),
291
- .visible => {
292
- assert(result != null);
293
- assert(std.mem.eql(u8, std.mem.asBytes(result.?), std.mem.asBytes(object)));
294
- },
295
- }
296
- }
297
- }
298
-
299
- assertion.objects = assertion.objects[assertion.verify_count..];
300
- assertion.verify_count = 0;
301
- assertion.verify();
302
- }
303
- };
304
-
305
- var assertion = VisibilityAssertion{ .objects = objects, .groove = groove };
306
- assertion.verify();
307
- while (assertion.verify_count > 0) try env.tick();
308
- }
309
-
310
- fn run() !void {
311
- var env: Environment = undefined;
312
-
313
- const must_create = false;
314
- try env.init(must_create);
315
-
316
- // We will be manually deinitializing during the test to simulate crashes and recovery.
317
- // If an error occurs during re-initialization, we don't want to trip this call to deinit().
318
- var crashing = false;
319
- defer if (!crashing) env.deinit();
320
-
321
- // Open the superblock then forest to start inserting accounts and transfers.
322
- try env.open();
323
-
324
- // Recording types for verification
325
- var inserted = std.ArrayList(Account).init(allocator);
326
- defer inserted.deinit();
327
-
328
- const accounts_to_insert_per_op = 1; // forest_options.accounts.commit_entries_max;
329
- const iterations = 4;
330
-
331
- var op: u64 = 1;
332
- var id: u128 = 1;
333
- var timestamp: u64 = 42;
334
- var crash_probability = std.rand.DefaultPrng.init(1337);
335
-
336
- var iter: usize = 0;
337
- while (iter < (accounts_to_insert_per_op * constants.lsm_batch_multiple * iterations)) : (iter += 1) {
338
- // Insert a bunch of accounts
339
-
340
- var i: u32 = 0;
341
- while (i < accounts_to_insert_per_op) : (i += 1) {
342
- defer id += 1;
343
- defer timestamp += 1;
344
- const account = Account{
345
- .id = id,
346
- .timestamp = timestamp,
347
- .user_data = 0,
348
- .reserved = [_]u8{0} ** 48,
349
- .ledger = 710, // Let's use the ISO-4217 Code Number for ZAR
350
- .code = 1000, // A chart of accounts code to describe this as a clearing account.
351
- .flags = .{ .debits_must_not_exceed_credits = true },
352
- .debits_pending = 0,
353
- .debits_posted = 0,
354
- .credits_pending = 0,
355
- .credits_posted = 42,
356
- };
357
-
358
- // Insert an account ...
359
- const groove = &env.forest.grooves.accounts;
360
- groove.put(&account);
361
-
362
- // ..and make sure it can be retrieved
363
- try env.assert_visibility(
364
- .visible,
365
- &env.forest.grooves.accounts,
366
- @as([]const Account, &.{account}),
367
- forest_options.accounts.tree_options_object.commit_entries_max,
368
- );
369
-
370
- // Record the successfull insertion.
371
- try inserted.append(account);
372
- }
373
-
374
- // compact the forest
375
- try env.compact(op);
376
- defer op += 1;
377
-
378
- // Checkpoint when the forest finishes compaction.
379
- // Don't repeat a checkpoint (commit_min must always advance).
380
- const checkpoint_op = op -| constants.lsm_batch_multiple;
381
- if (checkpoint_op % constants.lsm_batch_multiple == constants.lsm_batch_multiple - 1 and
382
- checkpoint_op != env.superblock.staging.vsr_state.commit_min)
383
- {
384
- // Checkpoint the forest then superblock
385
- env.superblock.staging.vsr_state.commit_min = checkpoint_op;
386
- env.superblock.staging.vsr_state.commit_max = checkpoint_op;
387
- try env.checkpoint();
388
-
389
- const checkpointed = inserted.items[0 .. checkpoint_op * accounts_to_insert_per_op];
390
- const uncommitted = inserted.items[checkpointed.len..];
391
- log.debug("checkpointed={d} uncommitted={d}", .{ checkpointed.len, uncommitted.len });
392
- assert(uncommitted.len == constants.lsm_batch_multiple * accounts_to_insert_per_op);
393
-
394
- // Randomly initiate a crash
395
- if (crash_probability.random().uintLessThanBiased(u32, 100) >= 50) {
396
- // Simulate crashing and restoring.
397
- log.debug("simulating crash", .{});
398
- crashing = true;
399
- {
400
- env.deinit();
401
- try env.init(must_create);
402
-
403
- // Re-open the superblock and forest.
404
- try env.open();
405
-
406
- // Double check the forest DOES NOT contain the un-checkpointed values (negative space)
407
- try env.assert_visibility(
408
- .invisible,
409
- &env.forest.grooves.accounts,
410
- uncommitted,
411
- forest_options.accounts.tree_options_object.commit_entries_max,
412
- );
413
-
414
- // Reset everything to after checkpoint
415
- op = checkpoint_op;
416
- inserted.items.len = checkpointed.len;
417
- id = checkpointed[checkpointed.len - 1].id + 1;
418
- timestamp = checkpointed[checkpointed.len - 1].timestamp + 1;
419
- }
420
- crashing = false;
421
- }
422
-
423
- // Double check the forest contains the checkpointed values (positive space)
424
- try env.assert_visibility(
425
- .visible,
426
- &env.forest.grooves.accounts,
427
- checkpointed,
428
- forest_options.accounts.tree_options_object.commit_entries_max,
429
- );
430
- }
431
- }
432
- }
433
- };
434
-
435
- pub fn main() !void {
436
- try Environment.format(); // NOTE: this can be commented out after first run to speed up testing.
437
- try Environment.run(); //try do_simple();
438
- }