tigerbeetle-node 0.11.8 → 0.11.9
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/dist/.client.node.sha256 +1 -1
- package/package.json +4 -3
- package/scripts/build_lib.sh +29 -0
- package/src/node.zig +1 -1
- package/src/tigerbeetle/scripts/validate_docs.sh +7 -1
- package/src/tigerbeetle/src/benchmark.zig +3 -3
- package/src/tigerbeetle/src/config.zig +29 -16
- package/src/tigerbeetle/src/constants.zig +30 -9
- package/src/tigerbeetle/src/ewah.zig +5 -5
- package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
- package/src/tigerbeetle/src/lsm/binary_search.zig +1 -1
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
- package/src/tigerbeetle/src/lsm/compaction.zig +34 -21
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +85 -103
- package/src/tigerbeetle/src/lsm/grid.zig +19 -13
- package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -10
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +12 -8
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +1 -1
- package/src/tigerbeetle/src/lsm/segmented_array.zig +17 -17
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +1 -1
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +1 -1
- package/src/tigerbeetle/src/lsm/table.zig +8 -20
- package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
- package/src/tigerbeetle/src/lsm/table_iterator.zig +3 -3
- package/src/tigerbeetle/src/lsm/table_mutable.zig +14 -2
- package/src/tigerbeetle/src/lsm/tree.zig +31 -5
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +86 -114
- package/src/tigerbeetle/src/message_bus.zig +4 -4
- package/src/tigerbeetle/src/message_pool.zig +7 -10
- package/src/tigerbeetle/src/ring_buffer.zig +22 -12
- package/src/tigerbeetle/src/simulator.zig +360 -214
- package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
- package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
- package/src/tigerbeetle/src/state_machine.zig +190 -178
- package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
- package/src/tigerbeetle/src/storage.zig +13 -6
- package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
- package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
- package/src/tigerbeetle/src/testing/cluster.zig +537 -0
- package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
- package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
- package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
- package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
- package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
- package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
- package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
- package/src/tigerbeetle/src/testing/storage.zig +754 -0
- package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
- package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
- package/src/tigerbeetle/src/tracer.zig +3 -3
- package/src/tigerbeetle/src/unit_tests.zig +4 -4
- package/src/tigerbeetle/src/vopr.zig +2 -2
- package/src/tigerbeetle/src/vsr/client.zig +5 -2
- package/src/tigerbeetle/src/vsr/clock.zig +93 -53
- package/src/tigerbeetle/src/vsr/journal.zig +29 -14
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
- package/src/tigerbeetle/src/vsr/replica.zig +1383 -774
- package/src/tigerbeetle/src/vsr/replica_format.zig +2 -2
- package/src/tigerbeetle/src/vsr/superblock.zig +59 -43
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +15 -7
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +1 -1
- package/src/tigerbeetle/src/vsr.zig +6 -4
- package/src/tigerbeetle/src/demo.zig +0 -132
- 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 -37
- 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/test/cluster.zig +0 -352
- package/src/tigerbeetle/src/test/conductor.zig +0 -366
- package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
- package/src/tigerbeetle/src/test/state_checker.zig +0 -169
- package/src/tigerbeetle/src/test/storage.zig +0 -864
- package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
|
@@ -4,7 +4,7 @@ const allocator = testing.allocator;
|
|
|
4
4
|
const assert = std.debug.assert;
|
|
5
5
|
|
|
6
6
|
const constants = @import("../constants.zig");
|
|
7
|
-
const fuzz = @import("../
|
|
7
|
+
const fuzz = @import("../testing/fuzz.zig");
|
|
8
8
|
const vsr = @import("../vsr.zig");
|
|
9
9
|
|
|
10
10
|
const log = std.log.scoped(.lsm_tree_fuzz);
|
|
@@ -13,7 +13,7 @@ const tracer = @import("../tracer.zig");
|
|
|
13
13
|
const MessagePool = @import("../message_pool.zig").MessagePool;
|
|
14
14
|
const Transfer = @import("../tigerbeetle.zig").Transfer;
|
|
15
15
|
const Account = @import("../tigerbeetle.zig").Account;
|
|
16
|
-
const Storage = @import("../
|
|
16
|
+
const Storage = @import("../testing/storage.zig").Storage;
|
|
17
17
|
const StateMachine = @import("../state_machine.zig").StateMachineType(Storage, .{
|
|
18
18
|
.message_body_size_max = constants.message_body_size_max,
|
|
19
19
|
});
|
|
@@ -62,6 +62,7 @@ const Key = packed struct {
|
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
const FuzzOpTag = std.meta.Tag(FuzzOp);
|
|
65
66
|
const FuzzOp = union(enum) {
|
|
66
67
|
// TODO Test range queries.
|
|
67
68
|
compact: struct {
|
|
@@ -72,14 +73,13 @@ const FuzzOp = union(enum) {
|
|
|
72
73
|
remove: Key.Value,
|
|
73
74
|
get: Key,
|
|
74
75
|
};
|
|
75
|
-
const FuzzOpTag = std.meta.Tag(FuzzOp);
|
|
76
76
|
|
|
77
|
-
const cluster = 32;
|
|
78
|
-
const replica = 4;
|
|
79
|
-
const node_count = 1024;
|
|
80
77
|
const batch_size_max = constants.message_size_max - @sizeOf(vsr.Header);
|
|
81
78
|
const commit_entries_max = @divFloor(batch_size_max, @sizeOf(Key.Value));
|
|
82
79
|
|
|
80
|
+
const cluster = 32;
|
|
81
|
+
const replica = 4;
|
|
82
|
+
const node_count = 1024;
|
|
83
83
|
const tree_options = .{
|
|
84
84
|
.commit_entries_max = commit_entries_max,
|
|
85
85
|
// This is the smallest size that set_associative_cache will allow us.
|
|
@@ -87,7 +87,6 @@ const tree_options = .{
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
const puts_since_compact_max = commit_entries_max;
|
|
90
|
-
|
|
91
90
|
const compacts_per_checkpoint = std.math.divCeil(
|
|
92
91
|
usize,
|
|
93
92
|
constants.journal_slot_count,
|
|
@@ -98,6 +97,7 @@ fn EnvironmentType(comptime table_usage: TableUsage) type {
|
|
|
98
97
|
return struct {
|
|
99
98
|
const Environment = @This();
|
|
100
99
|
|
|
100
|
+
const Tree = @import("tree.zig").TreeType(Table, Storage, "Key.Value");
|
|
101
101
|
const Table = TableType(
|
|
102
102
|
Key,
|
|
103
103
|
Key.Value,
|
|
@@ -108,17 +108,17 @@ fn EnvironmentType(comptime table_usage: TableUsage) type {
|
|
|
108
108
|
Key.tombstone_from_key,
|
|
109
109
|
table_usage,
|
|
110
110
|
);
|
|
111
|
-
const Tree = @import("tree.zig").TreeType(Table, Storage, "Key.Value");
|
|
112
111
|
|
|
113
112
|
const State = enum {
|
|
114
|
-
uninit,
|
|
115
113
|
init,
|
|
116
|
-
|
|
114
|
+
superblock_format,
|
|
117
115
|
superblock_open,
|
|
116
|
+
tree_init,
|
|
118
117
|
tree_open,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
fuzzing,
|
|
119
|
+
tree_compact,
|
|
120
|
+
tree_checkpoint,
|
|
121
|
+
superblock_checkpoint,
|
|
122
122
|
tree_lookup,
|
|
123
123
|
};
|
|
124
124
|
|
|
@@ -126,70 +126,40 @@ fn EnvironmentType(comptime table_usage: TableUsage) type {
|
|
|
126
126
|
storage: *Storage,
|
|
127
127
|
message_pool: MessagePool,
|
|
128
128
|
superblock: SuperBlock,
|
|
129
|
-
superblock_context: SuperBlock.Context
|
|
129
|
+
superblock_context: SuperBlock.Context,
|
|
130
130
|
grid: Grid,
|
|
131
131
|
node_pool: NodePool,
|
|
132
132
|
tree: Tree,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
lookup_value: ?*const Key.Value = null,
|
|
137
|
-
checkpoint_op: ?u64 = null,
|
|
138
|
-
|
|
139
|
-
fn init(env: *Environment, storage: *Storage) !void {
|
|
140
|
-
env.state = .uninit;
|
|
133
|
+
lookup_context: Tree.LookupContext,
|
|
134
|
+
lookup_value: ?*const Key.Value,
|
|
135
|
+
checkpoint_op: ?u64,
|
|
141
136
|
|
|
137
|
+
pub fn run(storage: *Storage, fuzz_ops: []const FuzzOp) !void {
|
|
138
|
+
var env: Environment = undefined;
|
|
139
|
+
env.state = .init;
|
|
142
140
|
env.storage = storage;
|
|
143
|
-
errdefer env.storage.deinit(allocator);
|
|
144
141
|
|
|
145
142
|
env.message_pool = try MessagePool.init(allocator, .replica);
|
|
146
|
-
|
|
143
|
+
defer env.message_pool.deinit(allocator);
|
|
147
144
|
|
|
148
145
|
env.superblock = try SuperBlock.init(allocator, .{
|
|
149
146
|
.storage = env.storage,
|
|
150
147
|
.storage_size_limit = constants.storage_size_max,
|
|
151
148
|
.message_pool = &env.message_pool,
|
|
152
149
|
});
|
|
153
|
-
|
|
150
|
+
defer env.superblock.deinit(allocator);
|
|
154
151
|
|
|
155
152
|
env.grid = try Grid.init(allocator, &env.superblock);
|
|
156
|
-
|
|
153
|
+
defer env.grid.deinit(allocator);
|
|
157
154
|
|
|
158
155
|
env.node_pool = try NodePool.init(allocator, node_count);
|
|
159
|
-
|
|
156
|
+
defer env.node_pool.deinit(allocator);
|
|
160
157
|
|
|
161
|
-
// Tree must be initialized with an open superblock.
|
|
162
158
|
env.tree = undefined;
|
|
163
|
-
env.
|
|
164
|
-
|
|
165
|
-
env.state = .init;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
fn deinit(env: *Environment) void {
|
|
169
|
-
assert(env.state != .uninit);
|
|
170
|
-
|
|
171
|
-
if (env.tree_exists) {
|
|
172
|
-
env.tree.deinit(allocator);
|
|
173
|
-
env.tree_exists = false;
|
|
174
|
-
}
|
|
175
|
-
env.node_pool.deinit(allocator);
|
|
176
|
-
env.grid.deinit(allocator);
|
|
177
|
-
env.superblock.deinit(allocator);
|
|
178
|
-
env.message_pool.deinit(allocator);
|
|
179
|
-
|
|
180
|
-
env.state = .uninit;
|
|
181
|
-
}
|
|
159
|
+
env.lookup_value = null;
|
|
160
|
+
env.checkpoint_op = null;
|
|
182
161
|
|
|
183
|
-
|
|
184
|
-
// env.grid.tick();
|
|
185
|
-
env.storage.tick();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
fn tick_until_state_change(env: *Environment, current_state: State, next_state: State) void {
|
|
189
|
-
// Sometimes IO completes synchronously (eg if cached), so we might already be in next_state before ticking.
|
|
190
|
-
assert(env.state == current_state or env.state == next_state);
|
|
191
|
-
while (env.state == current_state) env.tick();
|
|
192
|
-
assert(env.state == next_state);
|
|
162
|
+
try env.open_then_apply(fuzz_ops);
|
|
193
163
|
}
|
|
194
164
|
|
|
195
165
|
fn change_state(env: *Environment, current_state: State, next_state: State) void {
|
|
@@ -197,123 +167,129 @@ fn EnvironmentType(comptime table_usage: TableUsage) type {
|
|
|
197
167
|
env.state = next_state;
|
|
198
168
|
}
|
|
199
169
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
170
|
+
fn tick_until_state_change(env: *Environment, current_state: State, next_state: State) void {
|
|
171
|
+
// Sometimes operations complete synchronously so we might already be in next_state before ticking.
|
|
172
|
+
//assert(env.state == current_state or env.state == next_state);
|
|
173
|
+
while (env.state == current_state) env.storage.tick();
|
|
174
|
+
assert(env.state == next_state);
|
|
175
|
+
}
|
|
205
176
|
|
|
206
|
-
|
|
177
|
+
pub fn open_then_apply(env: *Environment, fuzz_ops: []const FuzzOp) !void {
|
|
178
|
+
env.change_state(.init, .superblock_format);
|
|
207
179
|
env.superblock.format(superblock_format_callback, &env.superblock_context, .{
|
|
208
180
|
.cluster = cluster,
|
|
209
181
|
.replica = replica,
|
|
210
182
|
});
|
|
211
|
-
|
|
183
|
+
|
|
184
|
+
env.tick_until_state_change(.superblock_format, .superblock_open);
|
|
185
|
+
env.superblock.open(superblock_open_callback, &env.superblock_context);
|
|
186
|
+
|
|
187
|
+
env.tick_until_state_change(.superblock_open, .tree_init);
|
|
188
|
+
env.tree = try Tree.init(allocator, &env.node_pool, &env.grid, tree_options);
|
|
189
|
+
defer env.tree.deinit(allocator);
|
|
190
|
+
|
|
191
|
+
env.change_state(.tree_init, .tree_open);
|
|
192
|
+
env.tree.open(tree_open_callback);
|
|
193
|
+
|
|
194
|
+
env.tick_until_state_change(.tree_open, .fuzzing);
|
|
195
|
+
try env.apply(fuzz_ops);
|
|
212
196
|
}
|
|
213
197
|
|
|
214
198
|
fn superblock_format_callback(superblock_context: *SuperBlock.Context) void {
|
|
215
199
|
const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
|
|
216
|
-
env.change_state(.
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
pub fn open(env: *Environment) void {
|
|
220
|
-
assert(env.state == .init);
|
|
221
|
-
env.superblock.open(superblock_open_callback, &env.superblock_context);
|
|
222
|
-
env.tick_until_state_change(.init, .tree_open);
|
|
200
|
+
env.change_state(.superblock_format, .superblock_open);
|
|
223
201
|
}
|
|
224
202
|
|
|
225
203
|
fn superblock_open_callback(superblock_context: *SuperBlock.Context) void {
|
|
226
204
|
const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
|
|
227
|
-
env.change_state(.
|
|
228
|
-
env.tree = Tree.init(allocator, &env.node_pool, &env.grid, tree_options) catch unreachable;
|
|
229
|
-
env.tree_exists = true;
|
|
230
|
-
env.tree.open(tree_open_callback);
|
|
205
|
+
env.change_state(.superblock_open, .tree_init);
|
|
231
206
|
}
|
|
232
207
|
|
|
233
208
|
fn tree_open_callback(tree: *Tree) void {
|
|
234
209
|
const env = @fieldParentPtr(@This(), "tree", tree);
|
|
235
|
-
env.change_state(.
|
|
210
|
+
env.change_state(.tree_open, .fuzzing);
|
|
236
211
|
}
|
|
237
212
|
|
|
238
213
|
pub fn compact(env: *Environment, op: u64) void {
|
|
239
|
-
env.change_state(.
|
|
214
|
+
env.change_state(.fuzzing, .tree_compact);
|
|
240
215
|
env.tree.compact(tree_compact_callback, op);
|
|
241
|
-
env.tick_until_state_change(.
|
|
216
|
+
env.tick_until_state_change(.tree_compact, .fuzzing);
|
|
242
217
|
}
|
|
243
218
|
|
|
244
219
|
fn tree_compact_callback(tree: *Tree) void {
|
|
245
220
|
const env = @fieldParentPtr(@This(), "tree", tree);
|
|
246
|
-
env.change_state(.
|
|
221
|
+
env.change_state(.tree_compact, .fuzzing);
|
|
247
222
|
}
|
|
248
223
|
|
|
249
224
|
pub fn checkpoint(env: *Environment, op: u64) void {
|
|
225
|
+
assert(env.checkpoint_op == null);
|
|
250
226
|
env.checkpoint_op = op - constants.lsm_batch_multiple;
|
|
251
|
-
|
|
227
|
+
|
|
228
|
+
env.change_state(.fuzzing, .tree_checkpoint);
|
|
252
229
|
env.tree.checkpoint(tree_checkpoint_callback);
|
|
253
|
-
env.tick_until_state_change(.
|
|
254
|
-
env.tick_until_state_change(.
|
|
230
|
+
env.tick_until_state_change(.tree_checkpoint, .superblock_checkpoint);
|
|
231
|
+
env.tick_until_state_change(.superblock_checkpoint, .fuzzing);
|
|
255
232
|
}
|
|
256
233
|
|
|
257
234
|
fn tree_checkpoint_callback(tree: *Tree) void {
|
|
258
235
|
const env = @fieldParentPtr(@This(), "tree", tree);
|
|
259
|
-
env.
|
|
236
|
+
const op = env.checkpoint_op.?;
|
|
237
|
+
env.checkpoint_op = null;
|
|
238
|
+
|
|
239
|
+
env.change_state(.tree_checkpoint, .superblock_checkpoint);
|
|
260
240
|
env.superblock.checkpoint(superblock_checkpoint_callback, &env.superblock_context, .{
|
|
261
241
|
.commit_min_checksum = env.superblock.working.vsr_state.commit_min_checksum + 1,
|
|
262
|
-
.commit_min =
|
|
263
|
-
.commit_max =
|
|
264
|
-
.
|
|
242
|
+
.commit_min = op,
|
|
243
|
+
.commit_max = op + 1,
|
|
244
|
+
.log_view = 0,
|
|
265
245
|
.view = 0,
|
|
266
246
|
});
|
|
267
|
-
env.checkpoint_op = null;
|
|
268
247
|
}
|
|
269
248
|
|
|
270
249
|
fn superblock_checkpoint_callback(superblock_context: *SuperBlock.Context) void {
|
|
271
250
|
const env = @fieldParentPtr(@This(), "superblock_context", superblock_context);
|
|
272
|
-
env.change_state(.
|
|
251
|
+
env.change_state(.superblock_checkpoint, .fuzzing);
|
|
273
252
|
}
|
|
274
253
|
|
|
275
|
-
fn get(env: *Environment, key: Key) ?*const Key.Value {
|
|
254
|
+
pub fn get(env: *Environment, key: Key) ?*const Key.Value {
|
|
255
|
+
env.change_state(.fuzzing, .tree_lookup);
|
|
256
|
+
|
|
276
257
|
if (env.tree.lookup_from_memory(env.tree.lookup_snapshot_max, key)) |value| {
|
|
258
|
+
env.change_state(.tree_lookup, .fuzzing);
|
|
277
259
|
return Tree.unwrap_tombstone(value);
|
|
278
|
-
} else {
|
|
279
|
-
env.change_state(.tree_open, .tree_lookup);
|
|
280
|
-
env.lookup_context = undefined;
|
|
281
|
-
env.lookup_value = null;
|
|
282
|
-
env.tree.lookup_from_levels(get_callback, &env.lookup_context, env.tree.lookup_snapshot_max, key);
|
|
283
|
-
env.tick_until_state_change(.tree_lookup, .tree_open);
|
|
284
|
-
return env.lookup_value;
|
|
285
260
|
}
|
|
261
|
+
|
|
262
|
+
env.lookup_value = null;
|
|
263
|
+
env.tree.lookup_from_levels(get_callback, &env.lookup_context, env.tree.lookup_snapshot_max, key);
|
|
264
|
+
env.tick_until_state_change(.tree_lookup, .fuzzing);
|
|
265
|
+
return env.lookup_value;
|
|
286
266
|
}
|
|
287
267
|
|
|
288
268
|
fn get_callback(lookup_context: *Tree.LookupContext, value: ?*const Key.Value) void {
|
|
289
269
|
const env = @fieldParentPtr(Environment, "lookup_context", lookup_context);
|
|
290
270
|
assert(env.lookup_value == null);
|
|
291
271
|
env.lookup_value = value;
|
|
292
|
-
env.change_state(.tree_lookup, .
|
|
272
|
+
env.change_state(.tree_lookup, .fuzzing);
|
|
293
273
|
}
|
|
294
274
|
|
|
295
|
-
fn
|
|
296
|
-
var env: Environment = undefined;
|
|
297
|
-
|
|
298
|
-
try env.init(storage);
|
|
299
|
-
defer env.deinit();
|
|
300
|
-
|
|
301
|
-
// Open the superblock then tree.
|
|
302
|
-
env.open();
|
|
303
|
-
|
|
275
|
+
pub fn apply(env: *Environment, fuzz_ops: []const FuzzOp) !void {
|
|
304
276
|
// The tree should behave like a simple key-value data-structure.
|
|
305
277
|
// We'll compare it to a hash map.
|
|
306
278
|
var model = std.hash_map.AutoHashMap(Key, Key.Value).init(allocator);
|
|
307
279
|
defer model.deinit();
|
|
308
280
|
|
|
309
281
|
for (fuzz_ops) |fuzz_op, fuzz_op_index| {
|
|
282
|
+
assert(env.state == .fuzzing);
|
|
310
283
|
log.debug("Running fuzz_ops[{}/{}] == {}", .{ fuzz_op_index, fuzz_ops.len, fuzz_op });
|
|
311
|
-
|
|
312
|
-
|
|
284
|
+
|
|
285
|
+
const storage_size_used = env.storage.size_used();
|
|
286
|
+
log.debug("storage.size_used = {}/{}", .{ storage_size_used, env.storage.size });
|
|
287
|
+
|
|
313
288
|
const model_size = model.count() * @sizeOf(Key.Value);
|
|
314
289
|
log.debug("space_amplification = {d:.2}", .{
|
|
315
290
|
@intToFloat(f64, storage_size_used) / @intToFloat(f64, model_size),
|
|
316
291
|
});
|
|
292
|
+
|
|
317
293
|
// Apply fuzz_op to the tree and the model.
|
|
318
294
|
switch (fuzz_op) {
|
|
319
295
|
.compact => |compact| {
|
|
@@ -489,14 +465,10 @@ pub fn main() !void {
|
|
|
489
465
|
// TODO Use inline switch after upgrading to zig 0.10
|
|
490
466
|
switch (table_usage) {
|
|
491
467
|
.general => {
|
|
492
|
-
|
|
493
|
-
try Environment.format(&storage);
|
|
494
|
-
try Environment.run(&storage, fuzz_ops);
|
|
468
|
+
try EnvironmentType(.general).run(&storage, fuzz_ops);
|
|
495
469
|
},
|
|
496
470
|
.secondary_index => {
|
|
497
|
-
|
|
498
|
-
try Environment.format(&storage);
|
|
499
|
-
try Environment.run(&storage, fuzz_ops);
|
|
471
|
+
try EnvironmentType(.secondary_index).run(&storage, fuzz_ops);
|
|
500
472
|
},
|
|
501
473
|
}
|
|
502
474
|
|
|
@@ -12,7 +12,7 @@ const log = std.log.scoped(.message_bus);
|
|
|
12
12
|
const vsr = @import("vsr.zig");
|
|
13
13
|
const Header = vsr.Header;
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const stdx = @import("stdx.zig");
|
|
16
16
|
const RingBuffer = @import("ring_buffer.zig").RingBuffer;
|
|
17
17
|
const IO = @import("io.zig").IO;
|
|
18
18
|
const MessagePool = @import("message_pool.zig").MessagePool;
|
|
@@ -750,7 +750,7 @@ fn MessageBusType(comptime process_type: vsr.ProcessType) type {
|
|
|
750
750
|
if (connection.recv_progress == header.size) return connection.recv_message.?.ref();
|
|
751
751
|
|
|
752
752
|
const message = bus.get_message();
|
|
753
|
-
|
|
753
|
+
stdx.copy_disjoint(.inexact, u8, message.buffer, data[0..header.size]);
|
|
754
754
|
return message;
|
|
755
755
|
}
|
|
756
756
|
|
|
@@ -771,7 +771,7 @@ fn MessageBusType(comptime process_type: vsr.ProcessType) type {
|
|
|
771
771
|
const sector_ceil = vsr.sector_ceil(message.header.size);
|
|
772
772
|
if (message.header.size != sector_ceil) {
|
|
773
773
|
assert(message.header.size < sector_ceil);
|
|
774
|
-
assert(message.buffer.len == constants.message_size_max
|
|
774
|
+
assert(message.buffer.len == constants.message_size_max);
|
|
775
775
|
mem.set(u8, message.buffer[message.header.size..sector_ceil], 0);
|
|
776
776
|
}
|
|
777
777
|
}
|
|
@@ -842,7 +842,7 @@ fn MessageBusType(comptime process_type: vsr.ProcessType) type {
|
|
|
842
842
|
assert(connection.recv_progress > 0);
|
|
843
843
|
assert(connection.recv_parsed > 0);
|
|
844
844
|
const data = recv_message.buffer[connection.recv_parsed..connection.recv_progress];
|
|
845
|
-
|
|
845
|
+
stdx.copy_disjoint(.inexact, u8, new_message.buffer, data);
|
|
846
846
|
connection.recv_progress = data.len;
|
|
847
847
|
connection.recv_parsed = 0;
|
|
848
848
|
} else {
|
|
@@ -13,10 +13,6 @@ comptime {
|
|
|
13
13
|
assert(constants.message_size_max % constants.sector_size == 0);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/// Add an extra sector_size bytes to allow a partially received subsequent
|
|
17
|
-
/// message to be shifted to make space for 0 padding to vsr.sector_ceil.
|
|
18
|
-
const message_size_max_padded = constants.message_size_max + constants.sector_size;
|
|
19
|
-
|
|
20
16
|
/// The number of full-sized messages allocated at initialization by the replica message pool.
|
|
21
17
|
/// There must be enough messages to ensure that the replica can always progress, to avoid deadlock.
|
|
22
18
|
pub const messages_max_replica = messages_max: {
|
|
@@ -26,7 +22,8 @@ pub const messages_max_replica = messages_max: {
|
|
|
26
22
|
sum += constants.journal_iops_write_max; // Journal writes
|
|
27
23
|
sum += constants.clients_max; // SuperBlock.client_table
|
|
28
24
|
sum += 1; // Replica.loopback_queue
|
|
29
|
-
sum += constants.
|
|
25
|
+
sum += constants.pipeline_prepare_queue_max; // Replica.Pipeline{Queue|Cache}
|
|
26
|
+
sum += constants.pipeline_request_queue_max; // Replica.Pipeline{Queue|Cache}
|
|
30
27
|
sum += 1; // Replica.commit_prepare
|
|
31
28
|
// Replica.do_view_change_from_all_replicas quorum:
|
|
32
29
|
// Replica.recovery_response_quorum is only used for recovery and does not increase the limit.
|
|
@@ -67,7 +64,7 @@ pub const MessagePool = struct {
|
|
|
67
64
|
pub const Message = struct {
|
|
68
65
|
// TODO: replace this with a header() function to save memory
|
|
69
66
|
header: *Header,
|
|
70
|
-
buffer:
|
|
67
|
+
buffer: *align(constants.sector_size) [constants.message_size_max]u8,
|
|
71
68
|
references: u32 = 0,
|
|
72
69
|
next: ?*Message,
|
|
73
70
|
|
|
@@ -105,13 +102,13 @@ pub const MessagePool = struct {
|
|
|
105
102
|
const buffer = try allocator.allocAdvanced(
|
|
106
103
|
u8,
|
|
107
104
|
constants.sector_size,
|
|
108
|
-
|
|
105
|
+
constants.message_size_max,
|
|
109
106
|
.exact,
|
|
110
107
|
);
|
|
111
108
|
const message = try allocator.create(Message);
|
|
112
109
|
message.* = .{
|
|
113
110
|
.header = mem.bytesAsValue(Header, buffer[0..@sizeOf(Header)]),
|
|
114
|
-
.buffer = buffer,
|
|
111
|
+
.buffer = buffer[0..constants.message_size_max],
|
|
115
112
|
.next = pool.free_list,
|
|
116
113
|
};
|
|
117
114
|
pool.free_list = message;
|
|
@@ -126,7 +123,7 @@ pub const MessagePool = struct {
|
|
|
126
123
|
var free_count: usize = 0;
|
|
127
124
|
while (pool.free_list) |message| {
|
|
128
125
|
pool.free_list = message.next;
|
|
129
|
-
allocator.free(message.buffer);
|
|
126
|
+
allocator.free(@as([]const u8, message.buffer));
|
|
130
127
|
allocator.destroy(message);
|
|
131
128
|
free_count += 1;
|
|
132
129
|
}
|
|
@@ -151,7 +148,7 @@ pub const MessagePool = struct {
|
|
|
151
148
|
pub fn unref(pool: *MessagePool, message: *Message) void {
|
|
152
149
|
message.references -= 1;
|
|
153
150
|
if (message.references == 0) {
|
|
154
|
-
|
|
151
|
+
mem.set(u8, message.buffer, 0);
|
|
155
152
|
message.next = pool.free_list;
|
|
156
153
|
pool.free_list = message;
|
|
157
154
|
}
|
|
@@ -3,7 +3,7 @@ const assert = std.debug.assert;
|
|
|
3
3
|
const math = std.math;
|
|
4
4
|
const mem = std.mem;
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const stdx = @import("stdx.zig");
|
|
7
7
|
|
|
8
8
|
/// A First In, First Out ring buffer holding at most `count_max` elements.
|
|
9
9
|
pub fn RingBuffer(
|
|
@@ -47,36 +47,38 @@ pub fn RingBuffer(
|
|
|
47
47
|
|
|
48
48
|
// TODO Add doc comments to these functions:
|
|
49
49
|
pub inline fn head(self: Self) ?T {
|
|
50
|
-
if (self.empty()) return null;
|
|
50
|
+
if (count_max == 0 or self.empty()) return null;
|
|
51
51
|
return self.buffer[self.index];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
pub inline fn head_ptr(self: *Self) ?*T {
|
|
55
|
-
if (self.empty()) return null;
|
|
55
|
+
if (count_max == 0 or self.empty()) return null;
|
|
56
56
|
return &self.buffer[self.index];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
pub inline fn head_ptr_const(self: *const Self) ?*const T {
|
|
60
|
-
if (self.empty()) return null;
|
|
60
|
+
if (count_max == 0 or self.empty()) return null;
|
|
61
61
|
return &self.buffer[self.index];
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
pub inline fn tail(self: Self) ?T {
|
|
65
|
-
if (self.empty()) return null;
|
|
65
|
+
if (count_max == 0 or self.empty()) return null;
|
|
66
66
|
return self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
pub inline fn tail_ptr(self: *Self) ?*T {
|
|
70
|
-
if (self.empty()) return null;
|
|
70
|
+
if (count_max == 0 or self.empty()) return null;
|
|
71
71
|
return &self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
pub inline fn tail_ptr_const(self: *const Self) ?*const T {
|
|
75
|
-
if (self.empty()) return null;
|
|
75
|
+
if (count_max == 0 or self.empty()) return null;
|
|
76
76
|
return &self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
pub inline fn get_ptr(self: *Self, index: usize) ?*T {
|
|
80
|
+
if (count_max == 0) unreachable;
|
|
81
|
+
|
|
80
82
|
if (index < self.count) {
|
|
81
83
|
return &self.buffer[(self.index + index) % self.buffer.len];
|
|
82
84
|
} else {
|
|
@@ -86,17 +88,17 @@ pub fn RingBuffer(
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
pub inline fn next_tail(self: Self) ?T {
|
|
89
|
-
if (self.full()) return null;
|
|
91
|
+
if (count_max == 0 or self.full()) return null;
|
|
90
92
|
return self.buffer[(self.index + self.count) % self.buffer.len];
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
pub inline fn next_tail_ptr(self: *Self) ?*T {
|
|
94
|
-
if (self.full()) return null;
|
|
96
|
+
if (count_max == 0 or self.full()) return null;
|
|
95
97
|
return &self.buffer[(self.index + self.count) % self.buffer.len];
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
pub inline fn next_tail_ptr_const(self: *const Self) ?*const T {
|
|
99
|
-
if (self.full()) return null;
|
|
101
|
+
if (count_max == 0 or self.full()) return null;
|
|
100
102
|
return &self.buffer[(self.index + self.count) % self.buffer.len];
|
|
101
103
|
}
|
|
102
104
|
|
|
@@ -143,14 +145,15 @@ pub fn RingBuffer(
|
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
pub fn push_slice(self: *Self, items: []const T) error{NoSpaceLeft}!void {
|
|
148
|
+
if (count_max == 0) return error.NoSpaceLeft;
|
|
146
149
|
if (self.count + items.len > self.buffer.len) return error.NoSpaceLeft;
|
|
147
150
|
|
|
148
151
|
const pre_wrap_start = (self.index + self.count) % self.buffer.len;
|
|
149
152
|
const pre_wrap_count = math.min(items.len, self.buffer.len - pre_wrap_start);
|
|
150
153
|
const post_wrap_count = items.len - pre_wrap_count;
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
stdx.copy_disjoint(.inexact, T, self.buffer[pre_wrap_start..], items[0..pre_wrap_count]);
|
|
156
|
+
stdx.copy_disjoint(.exact, T, self.buffer[0..post_wrap_count], items[pre_wrap_count..]);
|
|
154
157
|
|
|
155
158
|
self.count += items.len;
|
|
156
159
|
}
|
|
@@ -174,6 +177,7 @@ pub fn RingBuffer(
|
|
|
174
177
|
count: usize = 0,
|
|
175
178
|
|
|
176
179
|
pub fn next(it: *Iterator) ?T {
|
|
180
|
+
if (count_max == 0) return null;
|
|
177
181
|
// TODO Use next_ptr() internally to avoid duplicating this code.
|
|
178
182
|
assert(it.count <= it.ring.count);
|
|
179
183
|
if (it.count == it.ring.count) return null;
|
|
@@ -183,6 +187,7 @@ pub fn RingBuffer(
|
|
|
183
187
|
|
|
184
188
|
pub fn next_ptr(it: *Iterator) ?*const T {
|
|
185
189
|
assert(it.count <= it.ring.count);
|
|
190
|
+
if (count_max == 0) return null;
|
|
186
191
|
if (it.count == it.ring.count) return null;
|
|
187
192
|
defer it.count += 1;
|
|
188
193
|
return &it.ring.buffer[(it.ring.index + it.count) % it.ring.buffer.len];
|
|
@@ -201,6 +206,7 @@ pub fn RingBuffer(
|
|
|
201
206
|
|
|
202
207
|
pub fn next_ptr(it: *IteratorMutable) ?*T {
|
|
203
208
|
assert(it.count <= it.ring.count);
|
|
209
|
+
if (count_max == 0) return null;
|
|
204
210
|
if (it.count == it.ring.count) return null;
|
|
205
211
|
defer it.count += 1;
|
|
206
212
|
return &it.ring.buffer[(it.ring.index + it.count) % it.ring.buffer.len];
|
|
@@ -387,3 +393,7 @@ test "RingBuffer: pop_tail" {
|
|
|
387
393
|
try testing.expectEqual(@as(?u32, null), lifo.pop_tail());
|
|
388
394
|
try testing.expect(lifo.empty());
|
|
389
395
|
}
|
|
396
|
+
|
|
397
|
+
test "RingBuffer: count_max=0" {
|
|
398
|
+
std.testing.refAllDecls(RingBuffer(u32, 0, .array));
|
|
399
|
+
}
|