tigerbeetle 0.0.34 → 0.0.37
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/ext/tb_client/extconf.rb +13 -13
- data/ext/tb_client/tigerbeetle/LICENSE +177 -0
- data/ext/tb_client/tigerbeetle/build.zig +2327 -0
- data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
- data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
- data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
- data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
- data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
- data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
- data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
- data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
- data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
- data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
- data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
- data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
- data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
- data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
- data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
- data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
- data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
- data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
- data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
- data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
- data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
- data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
- data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
- data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
- data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
- data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
- data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
- data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
- data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
- data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
- data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
- data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
- data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
- data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
- data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
- data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
- data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
- data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
- data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
- data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
- data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
- data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
- data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
- data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
- data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
- data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
- data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
- data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
- data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
- data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
- data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
- data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
- data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
- data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
- data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
- data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
- data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
- data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
- data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
- data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
- data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
- data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
- data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
- data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
- data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
- data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
- data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
- data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
- data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
- data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
- data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
- data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
- data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
- data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
- data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
- data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
- data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
- data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
- data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
- data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
- data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
- data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
- data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
- data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
- data/lib/tb_client/shared_lib.rb +12 -5
- data/lib/tigerbeetle/client.rb +1 -1
- data/lib/tigerbeetle/platforms.rb +9 -0
- data/lib/tigerbeetle/version.rb +2 -2
- data/tigerbeetle.gemspec +22 -5
- metadata +242 -3
- data/ext/tb_client/pkg.tar.gz +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
const maybe = stdx.maybe;
|
|
4
|
+
const log = std.log.scoped(.storage);
|
|
5
|
+
|
|
6
|
+
const vsr = @import("vsr.zig");
|
|
7
|
+
const stdx = vsr.stdx;
|
|
8
|
+
const QueueType = vsr.queue.QueueType;
|
|
9
|
+
const constants = vsr.constants;
|
|
10
|
+
const Tracer = vsr.trace.Tracer;
|
|
11
|
+
|
|
12
|
+
pub fn StorageType(comptime IO: type) type {
|
|
13
|
+
return struct {
|
|
14
|
+
const Storage = @This();
|
|
15
|
+
|
|
16
|
+
/// See usage in Journal.write_sectors() for details.
|
|
17
|
+
pub const synchronicity: enum {
|
|
18
|
+
always_synchronous,
|
|
19
|
+
always_asynchronous,
|
|
20
|
+
} = .always_asynchronous;
|
|
21
|
+
|
|
22
|
+
pub const Read = struct {
|
|
23
|
+
completion: IO.Completion,
|
|
24
|
+
callback: *const fn (read: *Storage.Read) void,
|
|
25
|
+
|
|
26
|
+
/// The buffer to read into, re-sliced and re-assigned
|
|
27
|
+
/// as we go, e.g. after partial reads.
|
|
28
|
+
buffer: []u8,
|
|
29
|
+
|
|
30
|
+
/// The position into the file descriptor from where
|
|
31
|
+
/// we should read, also adjusted as we go.
|
|
32
|
+
offset: u64,
|
|
33
|
+
|
|
34
|
+
/// The maximum amount of bytes to read per syscall. We use this to subdivide
|
|
35
|
+
/// troublesome reads into smaller reads to work around latent sector errors (LSEs).
|
|
36
|
+
target_max: u64,
|
|
37
|
+
|
|
38
|
+
zone: vsr.Zone,
|
|
39
|
+
start: ?stdx.Instant,
|
|
40
|
+
|
|
41
|
+
/// Returns a target slice into `buffer` to read into, capped by `target_max`.
|
|
42
|
+
/// If the previous read was a partial read of physical sectors (e.g. 512 bytes) less
|
|
43
|
+
/// than our logical sector size (e.g. 4 KiB), so that the remainder of the buffer is
|
|
44
|
+
/// no longer aligned to a logical sector, then we further cap the slice to get back
|
|
45
|
+
/// onto a logical sector boundary.
|
|
46
|
+
fn target(read: *Read) []u8 {
|
|
47
|
+
// A worked example of a partial read that leaves the rest of the buffer unaligned:
|
|
48
|
+
// This could happen for non-Advanced Format disks with a physical
|
|
49
|
+
// sector of 512 bytes.
|
|
50
|
+
//
|
|
51
|
+
// We want to read 8 KiB:
|
|
52
|
+
// buffer.ptr = 0
|
|
53
|
+
// buffer.len = 8192
|
|
54
|
+
// ... and then experience a partial read of only 512 bytes:
|
|
55
|
+
// buffer.ptr = 512
|
|
56
|
+
// buffer.len = 7680
|
|
57
|
+
//
|
|
58
|
+
// We can now see that `buffer.len` is no longer a sector multiple of 4 KiB and
|
|
59
|
+
// further that we have 3584 bytes left of the partial sector read.
|
|
60
|
+
// If we subtract this amount from our logical sector size of 4 KiB we get
|
|
61
|
+
// 512 bytes, which is the alignment error that we need to subtract from
|
|
62
|
+
// `target_max` to get back onto the boundary.
|
|
63
|
+
var max = read.target_max;
|
|
64
|
+
|
|
65
|
+
const partial_sector_read_remainder = read.buffer.len % constants.sector_size;
|
|
66
|
+
if (partial_sector_read_remainder != 0) {
|
|
67
|
+
// TODO log.debug() because this is interesting,
|
|
68
|
+
// and to ensure fuzz test coverage.
|
|
69
|
+
const partial_sector_read =
|
|
70
|
+
constants.sector_size -
|
|
71
|
+
partial_sector_read_remainder;
|
|
72
|
+
max -= partial_sector_read;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return read.buffer[0..@min(read.buffer.len, max)];
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
pub const Write = struct {
|
|
80
|
+
completion: IO.Completion,
|
|
81
|
+
callback: *const fn (write: *Storage.Write) void,
|
|
82
|
+
buffer: []const u8,
|
|
83
|
+
offset: u64,
|
|
84
|
+
|
|
85
|
+
zone: vsr.Zone,
|
|
86
|
+
start: ?stdx.Instant,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
pub const NextTick = struct {
|
|
90
|
+
link: QueueType(NextTick).Link = .{},
|
|
91
|
+
source: NextTickSource,
|
|
92
|
+
callback: *const fn (next_tick: *NextTick) void,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
pub const NextTickSource = enum { lsm, vsr };
|
|
96
|
+
|
|
97
|
+
io: *IO,
|
|
98
|
+
tracer: *Tracer,
|
|
99
|
+
dir_fd: IO.fd_t,
|
|
100
|
+
fd: IO.fd_t,
|
|
101
|
+
|
|
102
|
+
next_tick_queue: QueueType(NextTick) = QueueType(NextTick).init(.{
|
|
103
|
+
.name = "storage_next_tick",
|
|
104
|
+
}),
|
|
105
|
+
next_tick_completion_scheduled: bool = false,
|
|
106
|
+
next_tick_completion: IO.Completion = undefined,
|
|
107
|
+
|
|
108
|
+
pub fn init(io: *IO, tracer: *Tracer, options: struct {
|
|
109
|
+
path: []const u8,
|
|
110
|
+
size_min: u64,
|
|
111
|
+
purpose: IO.OpenDataFilePurpose,
|
|
112
|
+
direct_io: vsr.io.DirectIO,
|
|
113
|
+
}) !Storage {
|
|
114
|
+
// TODO Resolve the parent directory properly in the presence of .. and symlinks.
|
|
115
|
+
// TODO Handle physical volumes where there is no directory to fsync.
|
|
116
|
+
const dirname = std.fs.path.dirname(options.path) orelse ".";
|
|
117
|
+
const basename = std.fs.path.basename(options.path);
|
|
118
|
+
|
|
119
|
+
const dir_fd = try IO.open_dir(dirname);
|
|
120
|
+
errdefer std.posix.close(dir_fd);
|
|
121
|
+
|
|
122
|
+
const fd = try io.open_data_file(
|
|
123
|
+
dir_fd,
|
|
124
|
+
basename,
|
|
125
|
+
options.size_min,
|
|
126
|
+
options.purpose,
|
|
127
|
+
options.direct_io,
|
|
128
|
+
);
|
|
129
|
+
errdefer std.posix.close(fd);
|
|
130
|
+
|
|
131
|
+
return .{
|
|
132
|
+
.io = io,
|
|
133
|
+
.tracer = tracer,
|
|
134
|
+
.dir_fd = dir_fd,
|
|
135
|
+
.fd = fd,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub fn deinit(storage: *Storage) void {
|
|
140
|
+
assert(storage.next_tick_queue.empty());
|
|
141
|
+
assert(storage.fd != IO.INVALID_FILE);
|
|
142
|
+
assert(storage.dir_fd != IO.INVALID_FILE);
|
|
143
|
+
|
|
144
|
+
std.posix.close(storage.fd);
|
|
145
|
+
storage.fd = IO.INVALID_FILE;
|
|
146
|
+
|
|
147
|
+
std.posix.close(storage.dir_fd);
|
|
148
|
+
storage.dir_fd = IO.INVALID_FILE;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
pub fn run(storage: *Storage) void {
|
|
152
|
+
storage.io.run() catch |err| {
|
|
153
|
+
log.warn("tick: {}", .{err});
|
|
154
|
+
std.debug.panic("io.tick(): {}", .{err});
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
pub fn on_next_tick(
|
|
159
|
+
storage: *Storage,
|
|
160
|
+
source: NextTickSource,
|
|
161
|
+
callback: *const fn (next_tick: *Storage.NextTick) void,
|
|
162
|
+
next_tick: *Storage.NextTick,
|
|
163
|
+
) void {
|
|
164
|
+
next_tick.* = .{
|
|
165
|
+
.source = source,
|
|
166
|
+
.callback = callback,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
storage.next_tick_queue.push(next_tick);
|
|
170
|
+
|
|
171
|
+
if (!storage.next_tick_completion_scheduled) {
|
|
172
|
+
storage.next_tick_completion_scheduled = true;
|
|
173
|
+
storage.io.timeout(
|
|
174
|
+
*Storage,
|
|
175
|
+
storage,
|
|
176
|
+
timeout_callback,
|
|
177
|
+
&storage.next_tick_completion,
|
|
178
|
+
0, // 0ns timeout means to resolve as soon as possible - like a yield
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pub fn reset_next_tick_lsm(storage: *Storage) void {
|
|
184
|
+
var next_tick_iterator = storage.next_tick_queue;
|
|
185
|
+
storage.next_tick_queue.reset();
|
|
186
|
+
|
|
187
|
+
while (next_tick_iterator.pop()) |next_tick| {
|
|
188
|
+
if (next_tick.source != .lsm) storage.next_tick_queue.push(next_tick);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fn timeout_callback(
|
|
193
|
+
storage: *Storage,
|
|
194
|
+
completion: *IO.Completion,
|
|
195
|
+
result: IO.TimeoutError!void,
|
|
196
|
+
) void {
|
|
197
|
+
assert(completion == &storage.next_tick_completion);
|
|
198
|
+
_ = result catch |e| switch (e) {
|
|
199
|
+
error.Canceled => unreachable,
|
|
200
|
+
error.Unexpected => unreachable,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Reset the scheduled flag after processing all tick entries
|
|
204
|
+
assert(storage.next_tick_completion_scheduled);
|
|
205
|
+
defer {
|
|
206
|
+
assert(storage.next_tick_completion_scheduled);
|
|
207
|
+
storage.next_tick_completion_scheduled = false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
while (storage.next_tick_queue.pop()) |next_tick| {
|
|
211
|
+
next_tick.callback(next_tick);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
pub fn read_sectors(
|
|
216
|
+
self: *Storage,
|
|
217
|
+
callback: *const fn (read: *Storage.Read) void,
|
|
218
|
+
read: *Storage.Read,
|
|
219
|
+
buffer: []u8,
|
|
220
|
+
zone: vsr.Zone,
|
|
221
|
+
offset_in_zone: u64,
|
|
222
|
+
) void {
|
|
223
|
+
zone.verify_iop(buffer, offset_in_zone);
|
|
224
|
+
assert(zone != .grid_padding);
|
|
225
|
+
|
|
226
|
+
const offset_in_storage = zone.offset(offset_in_zone);
|
|
227
|
+
read.* = .{
|
|
228
|
+
.completion = undefined,
|
|
229
|
+
.callback = callback,
|
|
230
|
+
.buffer = buffer,
|
|
231
|
+
.offset = offset_in_storage,
|
|
232
|
+
.target_max = buffer.len,
|
|
233
|
+
.zone = zone,
|
|
234
|
+
.start = self.tracer.time.monotonic(),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
self.start_read(read, null);
|
|
238
|
+
assert(read.target().len > 0);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fn start_read(self: *Storage, read: *Storage.Read, bytes_read: ?usize) void {
|
|
242
|
+
assert(read.offset % constants.sector_size == 0);
|
|
243
|
+
maybe(bytes_read == 0); // Retrying erroneous read; same offset with smaller window.
|
|
244
|
+
|
|
245
|
+
const bytes = bytes_read orelse 0;
|
|
246
|
+
assert(bytes <= read.target().len);
|
|
247
|
+
|
|
248
|
+
read.offset += bytes;
|
|
249
|
+
read.buffer = read.buffer[bytes..];
|
|
250
|
+
|
|
251
|
+
const target = read.target();
|
|
252
|
+
if (target.len == 0) {
|
|
253
|
+
// Resolving the read inline means start_read() must not have been called from
|
|
254
|
+
// read_sectors(). If it was, this is a synchronous callback resolution and should
|
|
255
|
+
// be reported.
|
|
256
|
+
assert(bytes_read != null);
|
|
257
|
+
|
|
258
|
+
self.tracer.timing(
|
|
259
|
+
.{ .storage_read = .{ .zone = read.zone } },
|
|
260
|
+
self.tracer.time.monotonic().duration_since(read.start.?),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
read.callback(read);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
self.assert_bounds(target, read.offset);
|
|
268
|
+
self.io.read(
|
|
269
|
+
*Storage,
|
|
270
|
+
self,
|
|
271
|
+
on_read,
|
|
272
|
+
&read.completion,
|
|
273
|
+
self.fd,
|
|
274
|
+
target,
|
|
275
|
+
read.offset,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
fn on_read(self: *Storage, completion: *IO.Completion, result: IO.ReadError!usize) void {
|
|
280
|
+
const read: *Storage.Read = @fieldParentPtr("completion", completion);
|
|
281
|
+
|
|
282
|
+
const bytes_read = result catch |err| switch (err) {
|
|
283
|
+
error.InputOutput => {
|
|
284
|
+
// The disk was unable to read some sectors (an internal CRC or
|
|
285
|
+
// hardware failure): We may also have already experienced a partial
|
|
286
|
+
// unaligned read, reading less physical sectors than the logical sector size,
|
|
287
|
+
// so we cannot expect `target.len` to be an exact logical sector multiple.
|
|
288
|
+
const target = read.target();
|
|
289
|
+
if (target.len > constants.sector_size) {
|
|
290
|
+
// We tried to read more than a logical sector and failed.
|
|
291
|
+
log.warn("latent sector error: offset={}, subdividing read...", .{
|
|
292
|
+
read.offset,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Divide the buffer in half and try to read each half separately:
|
|
296
|
+
// This creates a recursive binary search for the sector(s)
|
|
297
|
+
// causing the error. This is considerably slower than doing a single
|
|
298
|
+
// bulk read and by now we might also have experienced the disk's
|
|
299
|
+
// read retry timeout (in seconds). TODO Our docs must instruct on why
|
|
300
|
+
// and how to reduce disk firmware timeouts.
|
|
301
|
+
|
|
302
|
+
// These lines both implement ceiling division e.g.
|
|
303
|
+
// `((3 - 1) / 2) + 1 == 2` and require that the numerator
|
|
304
|
+
// is always greater than zero:
|
|
305
|
+
assert(target.len > 0);
|
|
306
|
+
const target_sectors = @divFloor(target.len - 1, constants.sector_size) + 1;
|
|
307
|
+
assert(target_sectors > 0);
|
|
308
|
+
read.target_max =
|
|
309
|
+
(@divFloor(target_sectors - 1, 2) + 1) * constants.sector_size;
|
|
310
|
+
assert(read.target_max >= constants.sector_size);
|
|
311
|
+
|
|
312
|
+
// Pass 0 for `bytes_read` to retry the read with smaller `target_max`:
|
|
313
|
+
self.start_read(read, 0);
|
|
314
|
+
return;
|
|
315
|
+
} else {
|
|
316
|
+
// We tried to read at (or less than) logical sector granularity and failed.
|
|
317
|
+
log.warn("latent sector error: offset={}, zeroing sector...", .{
|
|
318
|
+
read.offset,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Zero this logical sector which can't be read:
|
|
322
|
+
// We will treat these EIO errors the same as a checksum failure.
|
|
323
|
+
// TODO This could be an interesting avenue to explore further, whether
|
|
324
|
+
// temporary or permanent EIO errors should be conflated
|
|
325
|
+
// with checksum failures.
|
|
326
|
+
assert(target.len > 0);
|
|
327
|
+
@memset(target, 0);
|
|
328
|
+
|
|
329
|
+
// We could set `read.target_max` to `vsr.sector_ceil(read.buffer.len)` here
|
|
330
|
+
// in order to restart our pseudo-binary search on the rest of the sectors
|
|
331
|
+
// to be read, optimistically assuming that this is the last failing sector.
|
|
332
|
+
// However, data corruption that causes EIO errors often has spatial
|
|
333
|
+
// locality. Therefore, restarting our pseudo-binary search here might give
|
|
334
|
+
// us abysmal performance in the (not uncommon) case of many successive
|
|
335
|
+
// failing sectors.
|
|
336
|
+
self.start_read(read, target.len);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
error.WouldBlock,
|
|
342
|
+
error.NotOpenForReading,
|
|
343
|
+
error.ConnectionResetByPeer,
|
|
344
|
+
error.Alignment,
|
|
345
|
+
error.IsDir,
|
|
346
|
+
error.SystemResources,
|
|
347
|
+
error.Unseekable,
|
|
348
|
+
error.ConnectionTimedOut,
|
|
349
|
+
error.Unexpected,
|
|
350
|
+
=> {
|
|
351
|
+
log.err(
|
|
352
|
+
"impossible read: offset={} buffer.len={} error={s}",
|
|
353
|
+
.{ read.offset, read.buffer.len, @errorName(err) },
|
|
354
|
+
);
|
|
355
|
+
@panic("impossible read");
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// We tried to read more than there really is available to read.
|
|
360
|
+
// In other words, we thought we could read beyond the end of the file descriptor.
|
|
361
|
+
//
|
|
362
|
+
// Some possible causes:
|
|
363
|
+
// - The data file inode `size` was truncated or corrupted.
|
|
364
|
+
// - We are reading the last grid block in the data file, (block_size bytes), but the
|
|
365
|
+
// block in question is smaller (e.g. only 1 sector).
|
|
366
|
+
// - Another replica requested a block, but we are lagging far behind, and the block
|
|
367
|
+
// address requested is beyond the end of our data file.
|
|
368
|
+
if (bytes_read == 0) {
|
|
369
|
+
@memset(read.buffer, 0);
|
|
370
|
+
self.start_read(read, read.buffer.len);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// If our target was limited to a single sector, perhaps because of a latent sector
|
|
375
|
+
// error, then increase `target_max` according to AIMD now that we have read
|
|
376
|
+
// successfully and hopefully cleared the faulty zone.
|
|
377
|
+
// We assume that `target_max` may exceed `read.buffer.len` at any time.
|
|
378
|
+
if (read.target_max == constants.sector_size) {
|
|
379
|
+
// TODO Add log.debug because this is interesting.
|
|
380
|
+
read.target_max += constants.sector_size;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
self.start_read(read, bytes_read);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
pub fn write_sectors(
|
|
387
|
+
self: *Storage,
|
|
388
|
+
callback: *const fn (write: *Storage.Write) void,
|
|
389
|
+
write: *Storage.Write,
|
|
390
|
+
buffer: []const u8,
|
|
391
|
+
zone: vsr.Zone,
|
|
392
|
+
offset_in_zone: u64,
|
|
393
|
+
) void {
|
|
394
|
+
zone.verify_iop(buffer, offset_in_zone);
|
|
395
|
+
assert(zone != .grid_padding); // Padding is never touched.
|
|
396
|
+
|
|
397
|
+
const offset_in_storage = zone.offset(offset_in_zone);
|
|
398
|
+
write.* = .{
|
|
399
|
+
.completion = undefined,
|
|
400
|
+
.callback = callback,
|
|
401
|
+
.buffer = buffer,
|
|
402
|
+
.offset = offset_in_storage,
|
|
403
|
+
.zone = zone,
|
|
404
|
+
.start = self.tracer.time.monotonic(),
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
self.start_write(write);
|
|
408
|
+
// Assert that the callback is called asynchronously.
|
|
409
|
+
assert(write.buffer.len > 0);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
fn start_write(self: *Storage, write: *Storage.Write) void {
|
|
413
|
+
assert(write.offset % constants.sector_size == 0);
|
|
414
|
+
self.assert_bounds(write.buffer, write.offset);
|
|
415
|
+
|
|
416
|
+
self.io.write(
|
|
417
|
+
*Storage,
|
|
418
|
+
self,
|
|
419
|
+
on_write,
|
|
420
|
+
&write.completion,
|
|
421
|
+
self.fd,
|
|
422
|
+
write.buffer,
|
|
423
|
+
write.offset,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
fn on_write(self: *Storage, completion: *IO.Completion, result: IO.WriteError!usize) void {
|
|
428
|
+
const write: *Storage.Write = @fieldParentPtr("completion", completion);
|
|
429
|
+
|
|
430
|
+
const bytes_written = result catch |err| switch (err) {
|
|
431
|
+
// We assume that the disk will attempt to reallocate a spare sector for any LSE.
|
|
432
|
+
// TODO What if we receive a temporary EIO error because of a faulty cable?
|
|
433
|
+
error.InputOutput => @panic("latent sector error: no spare sectors to reallocate"),
|
|
434
|
+
// TODO: It seems like it might be possible for some filesystems to return ETIMEDOUT
|
|
435
|
+
// here. Consider handling this without panicking.
|
|
436
|
+
error.NoSpaceLeft => {
|
|
437
|
+
// NB: Intentionally crash on physical space exhaustion.
|
|
438
|
+
// Low space condition is handled logically, via `--limit-storage` argument.
|
|
439
|
+
vsr.fatal(
|
|
440
|
+
.no_space_left,
|
|
441
|
+
"write failed: no space left on device (offset={} size={})",
|
|
442
|
+
.{ write.offset, write.buffer.len },
|
|
443
|
+
);
|
|
444
|
+
},
|
|
445
|
+
else => {
|
|
446
|
+
log.err(
|
|
447
|
+
"impossible write: offset={} buffer.len={} error={s}",
|
|
448
|
+
.{ write.offset, write.buffer.len, @errorName(err) },
|
|
449
|
+
);
|
|
450
|
+
@panic("impossible write");
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
if (bytes_written == 0) {
|
|
455
|
+
// This should never happen if the kernel and filesystem are well behaved.
|
|
456
|
+
// However, block devices are known to exhibit this behavior in the wild.
|
|
457
|
+
// TODO: Consider retrying with a timeout if this panic proves problematic, and be
|
|
458
|
+
// careful to avoid logging in a busy loop. Perhaps a better approach might be to
|
|
459
|
+
// return wrote = null here and let the protocol retry at a higher layer where
|
|
460
|
+
// there is more context available to decide on how important this is or whether
|
|
461
|
+
// to cancel.
|
|
462
|
+
@panic("write operation returned 0 bytes written");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
write.offset += bytes_written;
|
|
466
|
+
write.buffer = write.buffer[bytes_written..];
|
|
467
|
+
|
|
468
|
+
if (write.buffer.len == 0) {
|
|
469
|
+
self.tracer.timing(
|
|
470
|
+
.{ .storage_write = .{ .zone = write.zone } },
|
|
471
|
+
self.tracer.time.monotonic().duration_since(write.start.?),
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
write.callback(write);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
self.start_write(write);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/// Ensures that the read or write is within bounds and intends to read or write some bytes.
|
|
482
|
+
fn assert_bounds(self: *Storage, buffer: []const u8, offset: u64) void {
|
|
483
|
+
_ = self;
|
|
484
|
+
_ = offset;
|
|
485
|
+
|
|
486
|
+
assert(buffer.len > 0);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
|
|
4
|
+
const vsr = @import("vsr.zig");
|
|
5
|
+
const stdx = vsr.stdx;
|
|
6
|
+
const constants = @import("constants.zig");
|
|
7
|
+
const IO = @import("testing/io.zig").IO;
|
|
8
|
+
const Storage = @import("storage.zig").StorageType(IO);
|
|
9
|
+
const fixtures = @import("testing/fixtures.zig");
|
|
10
|
+
const fuzz = @import("testing/fuzz.zig");
|
|
11
|
+
const ratio = stdx.PRNG.ratio;
|
|
12
|
+
|
|
13
|
+
pub fn main(gpa: std.mem.Allocator, args: fuzz.FuzzArgs) !void {
|
|
14
|
+
const zones: []const vsr.Zone = &.{
|
|
15
|
+
.superblock,
|
|
16
|
+
.wal_headers,
|
|
17
|
+
.wal_prepares,
|
|
18
|
+
.client_replies,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const sector_size = constants.sector_size;
|
|
22
|
+
const sector_count = 64;
|
|
23
|
+
const storage_size = sector_count * sector_size;
|
|
24
|
+
const iterations = args.events_max orelse 10_000;
|
|
25
|
+
|
|
26
|
+
var time_os: vsr.time.TimeOS = .{};
|
|
27
|
+
const time = time_os.time();
|
|
28
|
+
|
|
29
|
+
var prng = stdx.PRNG.from_seed(args.seed);
|
|
30
|
+
for (0..iterations) |_| {
|
|
31
|
+
var fault_map = std.bit_set.ArrayBitSet(u8, sector_count).initEmpty();
|
|
32
|
+
|
|
33
|
+
const failed_sector_cluster_count = prng.range_inclusive(usize, 1, 10);
|
|
34
|
+
const failed_sector_cluster_minimum_length = prng.range_inclusive(usize, 1, 3);
|
|
35
|
+
const failed_sector_cluster_maximum_length =
|
|
36
|
+
failed_sector_cluster_minimum_length + prng.range_inclusive(usize, 1, 3);
|
|
37
|
+
|
|
38
|
+
for (0..failed_sector_cluster_count) |_| {
|
|
39
|
+
const start = prng.range_inclusive(
|
|
40
|
+
usize,
|
|
41
|
+
0,
|
|
42
|
+
sector_count - failed_sector_cluster_maximum_length,
|
|
43
|
+
);
|
|
44
|
+
const end = start + prng.range_inclusive(
|
|
45
|
+
usize,
|
|
46
|
+
failed_sector_cluster_minimum_length,
|
|
47
|
+
failed_sector_cluster_maximum_length,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
fault_map.setRangeValue(.{ .start = start, .end = @min(end, sector_count) }, true);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var storage_data_written: [storage_size]u8 align(sector_size) = undefined;
|
|
54
|
+
@memset(&storage_data_written, 0);
|
|
55
|
+
|
|
56
|
+
for (0..sector_count) |sector| {
|
|
57
|
+
if (!fault_map.isSet(sector)) {
|
|
58
|
+
prng.fill(
|
|
59
|
+
storage_data_written[sector * sector_size ..][0..sector_size],
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var storage_data_stored: [storage_size]u8 align(sector_size) = undefined;
|
|
65
|
+
@memset(&storage_data_stored, 0);
|
|
66
|
+
var storage_data_read: [storage_size]u8 align(sector_size) = undefined;
|
|
67
|
+
@memset(&storage_data_read, 0);
|
|
68
|
+
|
|
69
|
+
var files: [1]IO.File = .{
|
|
70
|
+
.{
|
|
71
|
+
.buffer = &storage_data_stored,
|
|
72
|
+
.fault_map = &fault_map.masks,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
var io = try IO.init(&files, .{
|
|
77
|
+
.seed = args.seed,
|
|
78
|
+
.larger_than_logical_sector_read_fault_probability = ratio(10, 100),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
var tracer = try fixtures.init_tracer(gpa, time, .{});
|
|
82
|
+
defer tracer.deinit(gpa);
|
|
83
|
+
|
|
84
|
+
var storage: Storage = .{
|
|
85
|
+
.io = &io,
|
|
86
|
+
.tracer = &tracer,
|
|
87
|
+
.dir_fd = 0,
|
|
88
|
+
.fd = 0,
|
|
89
|
+
};
|
|
90
|
+
// NB: Intentionally skipping deinit to avoid closing stdin.
|
|
91
|
+
|
|
92
|
+
var write_completion: Storage.Write = undefined;
|
|
93
|
+
|
|
94
|
+
for (zones) |zone| {
|
|
95
|
+
storage.write_sectors(
|
|
96
|
+
struct {
|
|
97
|
+
fn callback(completion: *Storage.Write) void {
|
|
98
|
+
_ = completion;
|
|
99
|
+
}
|
|
100
|
+
}.callback,
|
|
101
|
+
&write_completion,
|
|
102
|
+
storage_data_written[zone.start()..][0..zone.size().?],
|
|
103
|
+
zone,
|
|
104
|
+
0,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
storage.run();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (zones) |zone| {
|
|
111
|
+
const ReadDetail = struct {
|
|
112
|
+
offset_in_zone: u64,
|
|
113
|
+
read_length: u64,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
var read_details: [32]ReadDetail = undefined;
|
|
117
|
+
|
|
118
|
+
const zone_sector_count: u64 = @divExact(zone.size().?, sector_size);
|
|
119
|
+
assert(zone_sector_count <= read_details.len);
|
|
120
|
+
|
|
121
|
+
var index: u64 = 0;
|
|
122
|
+
var read_detail_length: usize = 0;
|
|
123
|
+
|
|
124
|
+
while (index < zone_sector_count) : (read_detail_length += 1) {
|
|
125
|
+
const n_sectors = prng.range_inclusive(
|
|
126
|
+
u64,
|
|
127
|
+
1,
|
|
128
|
+
@min(4, zone_sector_count - index),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
read_details[read_detail_length] = .{
|
|
132
|
+
.offset_in_zone = index * sector_size,
|
|
133
|
+
.read_length = n_sectors * sector_size,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
index += n_sectors;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
prng.shuffle(ReadDetail, read_details[0..read_detail_length]);
|
|
140
|
+
|
|
141
|
+
for (read_details[0..read_detail_length]) |read_detail| {
|
|
142
|
+
const sector_offset = read_detail.offset_in_zone;
|
|
143
|
+
const read_length = read_detail.read_length;
|
|
144
|
+
const read_buffer =
|
|
145
|
+
storage_data_read[zone.start() + sector_offset ..][0..read_length];
|
|
146
|
+
|
|
147
|
+
var read_completion: Storage.Read = undefined;
|
|
148
|
+
storage.read_sectors(
|
|
149
|
+
struct {
|
|
150
|
+
fn callback(completion: *Storage.Read) void {
|
|
151
|
+
_ = completion;
|
|
152
|
+
}
|
|
153
|
+
}.callback,
|
|
154
|
+
&read_completion,
|
|
155
|
+
read_buffer,
|
|
156
|
+
zone,
|
|
157
|
+
sector_offset,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
storage.run();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (zones) |zone| {
|
|
165
|
+
const start = zone.start();
|
|
166
|
+
const end = start + zone.size().?;
|
|
167
|
+
|
|
168
|
+
try std.testing.expectEqualSlices(
|
|
169
|
+
u8,
|
|
170
|
+
storage_data_written[start..end],
|
|
171
|
+
storage_data_stored[start..end],
|
|
172
|
+
);
|
|
173
|
+
try std.testing.expectEqualSlices(
|
|
174
|
+
u8,
|
|
175
|
+
storage_data_stored[start..end],
|
|
176
|
+
storage_data_read[start..end],
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|