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,1294 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
const math = std.math;
|
|
4
|
+
const mem = std.mem;
|
|
5
|
+
const meta = std.meta;
|
|
6
|
+
const maybe = stdx.maybe;
|
|
7
|
+
|
|
8
|
+
const stdx = @import("stdx");
|
|
9
|
+
const constants = @import("../constants.zig");
|
|
10
|
+
const lsm = @import("tree.zig");
|
|
11
|
+
const binary_search = @import("binary_search.zig");
|
|
12
|
+
|
|
13
|
+
const Direction = @import("../direction.zig").Direction;
|
|
14
|
+
const SortedSegmentedArrayType = @import("segmented_array.zig").SortedSegmentedArrayType;
|
|
15
|
+
|
|
16
|
+
pub fn ManifestLevelType(
|
|
17
|
+
comptime NodePool: type,
|
|
18
|
+
comptime Key: type,
|
|
19
|
+
comptime TableInfo: type,
|
|
20
|
+
comptime table_count_max_tree: u32,
|
|
21
|
+
) type {
|
|
22
|
+
comptime assert(@typeInfo(Key) == .int or @typeInfo(Key) == .comptime_int);
|
|
23
|
+
|
|
24
|
+
return struct {
|
|
25
|
+
const ManifestLevel = @This();
|
|
26
|
+
|
|
27
|
+
pub const Keys = SortedSegmentedArrayType(
|
|
28
|
+
Key,
|
|
29
|
+
NodePool,
|
|
30
|
+
table_count_max_tree,
|
|
31
|
+
Key,
|
|
32
|
+
struct {
|
|
33
|
+
inline fn key_from_value(value: *const Key) Key {
|
|
34
|
+
return value.*;
|
|
35
|
+
}
|
|
36
|
+
}.key_from_value,
|
|
37
|
+
.{},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
pub const Tables = SortedSegmentedArrayType(
|
|
41
|
+
TableInfo,
|
|
42
|
+
NodePool,
|
|
43
|
+
table_count_max_tree,
|
|
44
|
+
KeyMaxSnapshotMin.Int,
|
|
45
|
+
struct {
|
|
46
|
+
inline fn key_from_value(table_info: *const TableInfo) KeyMaxSnapshotMin.Int {
|
|
47
|
+
return KeyMaxSnapshotMin.key_from_value(.{
|
|
48
|
+
.key_max = table_info.key_max,
|
|
49
|
+
.snapshot_min = table_info.snapshot_min,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}.key_from_value,
|
|
53
|
+
.{},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
pub const KeyMaxSnapshotMin = packed struct(KeyMaxSnapshotMin.Int) {
|
|
57
|
+
pub const Int = std.meta.Int(
|
|
58
|
+
.unsigned,
|
|
59
|
+
@bitSizeOf(u64) + @bitSizeOf(Key),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// The tables are ordered by (key_max,snapshot_min),
|
|
63
|
+
// fields are declared from the least significant to the most significant:
|
|
64
|
+
|
|
65
|
+
snapshot_min: u64,
|
|
66
|
+
key_max: Key,
|
|
67
|
+
|
|
68
|
+
pub inline fn key_from_value(value: KeyMaxSnapshotMin) Int {
|
|
69
|
+
return @bitCast(value);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// A direct reference to a TableInfo within the Tables array.
|
|
74
|
+
pub const TableInfoReference = struct { table_info: *TableInfo, generation: u32 };
|
|
75
|
+
|
|
76
|
+
pub const LeastOverlapTable = struct {
|
|
77
|
+
table: TableInfoReference,
|
|
78
|
+
range: OverlapRange,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
pub const OverlapRange = struct {
|
|
82
|
+
/// The minimum key across both levels.
|
|
83
|
+
key_min: Key,
|
|
84
|
+
/// The maximum key across both levels.
|
|
85
|
+
key_max: Key,
|
|
86
|
+
// References to tables in level B that intersect with the chosen table in level A.
|
|
87
|
+
tables: stdx.BoundedArrayType(TableInfoReference, constants.lsm_growth_factor),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
pub const LevelKeyRange = struct {
|
|
91
|
+
key_range: ?KeyRange,
|
|
92
|
+
|
|
93
|
+
/// Excludes the specified range from the level's key range, i.e. if the specified range
|
|
94
|
+
/// contributes to the level's key_min/key_max, find a new key_min/key_max.
|
|
95
|
+
///
|
|
96
|
+
/// This is achieved by querying the tables visible to snapshot_latest and updating
|
|
97
|
+
/// level key_min/key_max to the key_min/key_max of the first table returned by the
|
|
98
|
+
/// iterator. The query is guaranteed to only fetch non-snapshotted tables, since
|
|
99
|
+
/// tables visible to old snapshots that users have retained would have
|
|
100
|
+
/// snapshot_max set to a non math.maxInt(u64) value. Therefore, they wouldn't
|
|
101
|
+
/// be visible to queries with snapshot_latest (math.maxInt(u64 - 1)).
|
|
102
|
+
fn exclude(self: *LevelKeyRange, exclude_range: KeyRange) void {
|
|
103
|
+
assert(self.key_range != null);
|
|
104
|
+
|
|
105
|
+
var level: *ManifestLevel = @fieldParentPtr("key_range_latest", self);
|
|
106
|
+
if (level.table_count_visible == 0) {
|
|
107
|
+
self.key_range = null;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const snapshots = &[1]u64{lsm.snapshot_latest};
|
|
112
|
+
if (exclude_range.key_max == self.key_range.?.key_max) {
|
|
113
|
+
var itr = level.iterator(.visible, snapshots, .descending, null);
|
|
114
|
+
const table: ?*const TableInfo = itr.next();
|
|
115
|
+
assert(table != null);
|
|
116
|
+
self.key_range.?.key_max = table.?.key_max;
|
|
117
|
+
}
|
|
118
|
+
if (exclude_range.key_min == self.key_range.?.key_min) {
|
|
119
|
+
var itr = level.iterator(.visible, snapshots, .ascending, null);
|
|
120
|
+
const table: ?*const TableInfo = itr.next();
|
|
121
|
+
assert(table != null);
|
|
122
|
+
self.key_range.?.key_min = table.?.key_min;
|
|
123
|
+
}
|
|
124
|
+
assert(self.key_range != null);
|
|
125
|
+
assert(self.key_range.?.key_min <= self.key_range.?.key_max);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn include(self: *LevelKeyRange, include_range: KeyRange) void {
|
|
129
|
+
if (self.key_range) |*level_range| {
|
|
130
|
+
if (include_range.key_min < level_range.key_min) {
|
|
131
|
+
level_range.key_min = include_range.key_min;
|
|
132
|
+
}
|
|
133
|
+
if (include_range.key_max > level_range.key_max) {
|
|
134
|
+
level_range.key_max = include_range.key_max;
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
self.key_range = include_range;
|
|
138
|
+
}
|
|
139
|
+
assert(self.key_range != null);
|
|
140
|
+
assert(self.key_range.?.key_min <= self.key_range.?.key_max);
|
|
141
|
+
assert(self.key_range.?.key_min <= include_range.key_min and
|
|
142
|
+
include_range.key_max <= self.key_range.?.key_max);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
inline fn contains(self: *const LevelKeyRange, key: Key) bool {
|
|
146
|
+
return (self.key_range != null) and
|
|
147
|
+
self.key_range.?.key_min <= key and
|
|
148
|
+
key <= self.key_range.?.key_max;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// These two segmented arrays are parallel. That is, the absolute indexes of maximum key
|
|
153
|
+
// and corresponding TableInfo are the same. However, the number of nodes, node index, and
|
|
154
|
+
// relative index into the node differ as the elements per node are different.
|
|
155
|
+
//
|
|
156
|
+
// Ordered by ascending (maximum) key. Keys may repeat due to snapshots.
|
|
157
|
+
keys: Keys,
|
|
158
|
+
tables: Tables,
|
|
159
|
+
|
|
160
|
+
/// The range of keys in this level covered by tables visible to snapshot_latest.
|
|
161
|
+
key_range_latest: LevelKeyRange = .{ .key_range = null },
|
|
162
|
+
|
|
163
|
+
/// The number of tables visible to snapshot_latest.
|
|
164
|
+
/// Used to enforce table_count_max_tree_for_level().
|
|
165
|
+
// TODO Track this in Manifest instead, since it knows both when tables are
|
|
166
|
+
// added/updated/removed, and also knows the superblock's persisted snapshots.
|
|
167
|
+
table_count_visible: u32 = 0,
|
|
168
|
+
|
|
169
|
+
/// A monotonically increasing generation number that is used detect invalid internal
|
|
170
|
+
/// TableInfo references.
|
|
171
|
+
generation: u32 = 0,
|
|
172
|
+
|
|
173
|
+
pub fn init(level: *ManifestLevel, allocator: mem.Allocator, node_pool: *NodePool) !void {
|
|
174
|
+
level.* = .{
|
|
175
|
+
.keys = undefined,
|
|
176
|
+
.tables = undefined,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
level.keys = try Keys.init(allocator);
|
|
180
|
+
errdefer level.keys.deinit(allocator, node_pool);
|
|
181
|
+
|
|
182
|
+
level.tables = try Tables.init(allocator);
|
|
183
|
+
errdefer level.tables.deinit(allocator, node_pool);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
pub fn deinit(level: *ManifestLevel, allocator: mem.Allocator, node_pool: *NodePool) void {
|
|
187
|
+
level.keys.deinit(allocator, node_pool);
|
|
188
|
+
level.tables.deinit(allocator, node_pool);
|
|
189
|
+
|
|
190
|
+
level.* = undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
pub fn reset(level: *ManifestLevel, node_pool: *NodePool) void {
|
|
194
|
+
level.keys.reset(node_pool);
|
|
195
|
+
level.tables.reset(node_pool);
|
|
196
|
+
|
|
197
|
+
level.* = .{
|
|
198
|
+
.keys = level.keys,
|
|
199
|
+
.tables = level.tables,
|
|
200
|
+
.generation = level.generation + 1,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// Inserts the given table into the ManifestLevel.
|
|
205
|
+
pub fn insert_table(
|
|
206
|
+
level: *ManifestLevel,
|
|
207
|
+
node_pool: *NodePool,
|
|
208
|
+
table: *const TableInfo,
|
|
209
|
+
) void {
|
|
210
|
+
if (constants.verify) {
|
|
211
|
+
assert(!level.contains(table));
|
|
212
|
+
}
|
|
213
|
+
assert(level.keys.len() == level.tables.len());
|
|
214
|
+
|
|
215
|
+
const absolute_index_keys = level.keys.insert_element(node_pool, table.key_max);
|
|
216
|
+
assert(absolute_index_keys < level.keys.len());
|
|
217
|
+
|
|
218
|
+
const absolute_index_tables = level.tables.insert_element(node_pool, table.*);
|
|
219
|
+
assert(absolute_index_tables < level.tables.len());
|
|
220
|
+
|
|
221
|
+
if (table.visible(lsm.snapshot_latest)) level.table_count_visible += 1;
|
|
222
|
+
level.generation +%= 1;
|
|
223
|
+
|
|
224
|
+
level.key_range_latest.include(KeyRange{
|
|
225
|
+
.key_min = table.key_min,
|
|
226
|
+
.key_max = table.key_max,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (constants.verify) {
|
|
230
|
+
assert(level.contains(table));
|
|
231
|
+
|
|
232
|
+
// `keys` may have duplicate entries due to tables with the same key_max, but
|
|
233
|
+
// different snapshots.
|
|
234
|
+
maybe(absolute_index_keys != absolute_index_tables);
|
|
235
|
+
|
|
236
|
+
var keys_iterator =
|
|
237
|
+
level.keys.iterator_from_index(absolute_index_tables, .ascending);
|
|
238
|
+
var tables_iterator =
|
|
239
|
+
level.tables.iterator_from_index(absolute_index_keys, .ascending);
|
|
240
|
+
|
|
241
|
+
assert(keys_iterator.next().?.* == table.key_max);
|
|
242
|
+
assert(tables_iterator.next().?.key_max == table.key_max);
|
|
243
|
+
}
|
|
244
|
+
assert(level.keys.len() == level.tables.len());
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// Set snapshot_max for the given table in the ManifestLevel.
|
|
248
|
+
/// * The table is mutable so that this function can update its snapshot.
|
|
249
|
+
/// * Asserts that the table currently has snapshot_max of math.maxInt(u64).
|
|
250
|
+
/// * Asserts that the table exists in the manifest.
|
|
251
|
+
pub fn set_snapshot_max(
|
|
252
|
+
level: *ManifestLevel,
|
|
253
|
+
snapshot: u64,
|
|
254
|
+
table_ref: TableInfoReference,
|
|
255
|
+
) void {
|
|
256
|
+
var table = table_ref.table_info;
|
|
257
|
+
|
|
258
|
+
assert(table_ref.generation == level.generation);
|
|
259
|
+
if (constants.verify) {
|
|
260
|
+
assert(level.contains(table));
|
|
261
|
+
}
|
|
262
|
+
assert(snapshot < lsm.snapshot_latest);
|
|
263
|
+
assert(table.snapshot_max == math.maxInt(u64));
|
|
264
|
+
assert(table.key_min <= table.key_max);
|
|
265
|
+
|
|
266
|
+
table.snapshot_max = snapshot;
|
|
267
|
+
level.table_count_visible -= 1;
|
|
268
|
+
level.key_range_latest.exclude(KeyRange{
|
|
269
|
+
.key_min = table.key_min,
|
|
270
|
+
.key_max = table.key_max,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// Remove the given table.
|
|
275
|
+
/// The `table` parameter must *not* be a pointer into the `tables`' SegmentedArray memory.
|
|
276
|
+
pub fn remove_table(
|
|
277
|
+
level: *ManifestLevel,
|
|
278
|
+
node_pool: *NodePool,
|
|
279
|
+
table: *const TableInfo,
|
|
280
|
+
) void {
|
|
281
|
+
assert(level.keys.len() == level.tables.len());
|
|
282
|
+
assert(table.key_min <= table.key_max);
|
|
283
|
+
|
|
284
|
+
// Use `key_min` for both ends of the iterator; we are looking for a single table.
|
|
285
|
+
const cursor_start = level.iterator_start(table.key_min, table.key_min, .ascending).?;
|
|
286
|
+
|
|
287
|
+
var i = level.keys.absolute_index_for_cursor(cursor_start);
|
|
288
|
+
var tables = level.tables.iterator_from_index(i, .ascending);
|
|
289
|
+
const table_index_absolute = while (tables.next()) |level_table| : (i += 1) {
|
|
290
|
+
// The `table` parameter should *not* be a pointer into the `tables` SegmentedArray
|
|
291
|
+
// memory, since it will be invalidated by `tables.remove_elements()`.
|
|
292
|
+
assert(level_table != table);
|
|
293
|
+
|
|
294
|
+
if (level_table.equal(table)) break i;
|
|
295
|
+
assert(level_table.checksum != table.checksum);
|
|
296
|
+
assert(level_table.address != table.address);
|
|
297
|
+
} else {
|
|
298
|
+
@panic("ManifestLevel.remove_table: table not found");
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
level.generation +%= 1;
|
|
302
|
+
level.keys.remove_elements(node_pool, table_index_absolute, 1);
|
|
303
|
+
level.tables.remove_elements(node_pool, table_index_absolute, 1);
|
|
304
|
+
assert(level.keys.len() == level.tables.len());
|
|
305
|
+
|
|
306
|
+
if (table.visible(lsm.snapshot_latest)) {
|
|
307
|
+
level.table_count_visible -= 1;
|
|
308
|
+
|
|
309
|
+
level.key_range_latest.exclude(.{
|
|
310
|
+
.key_min = table.key_min,
|
|
311
|
+
.key_max = table.key_max,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// Returns True if the given key may be present in the ManifestLevel,
|
|
317
|
+
/// False if the key is guaranteed to not be present.
|
|
318
|
+
///
|
|
319
|
+
/// Our key range keeps track of tables that are visible to snapshot_latest, so it cannot
|
|
320
|
+
/// be relied upon for queries to older snapshots.
|
|
321
|
+
pub fn key_range_contains(level: *const ManifestLevel, snapshot: u64, key: Key) bool {
|
|
322
|
+
// TODO We currently assume that the snapshot passed in is the latest snapshot.
|
|
323
|
+
// This must be changed when persistent snapshots are implemented.
|
|
324
|
+
assert(snapshot < lsm.snapshot_latest);
|
|
325
|
+
return level.key_range_latest.contains(key);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
pub const Visibility = enum {
|
|
329
|
+
visible,
|
|
330
|
+
invisible,
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
pub const KeyRange = struct {
|
|
334
|
+
key_min: Key, // Inclusive.
|
|
335
|
+
key_max: Key, // Inclusive.
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
pub fn iterator(
|
|
339
|
+
level: *const ManifestLevel,
|
|
340
|
+
visibility: Visibility,
|
|
341
|
+
snapshots: []const u64,
|
|
342
|
+
direction: Direction,
|
|
343
|
+
key_range: ?KeyRange,
|
|
344
|
+
) Iterator {
|
|
345
|
+
for (snapshots) |snapshot| {
|
|
346
|
+
assert(snapshot <= lsm.snapshot_latest);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const inner = blk: {
|
|
350
|
+
if (key_range) |range| {
|
|
351
|
+
assert(range.key_min <= range.key_max);
|
|
352
|
+
|
|
353
|
+
if (level.iterator_start(range.key_min, range.key_max, direction)) |start| {
|
|
354
|
+
break :blk level.tables.iterator_from_index(
|
|
355
|
+
level.keys.absolute_index_for_cursor(start),
|
|
356
|
+
direction,
|
|
357
|
+
);
|
|
358
|
+
} else {
|
|
359
|
+
break :blk Tables.Iterator{
|
|
360
|
+
.array = &level.tables,
|
|
361
|
+
.direction = direction,
|
|
362
|
+
.cursor = .{ .node = 0, .relative_index = 0 },
|
|
363
|
+
.done = true,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
switch (direction) {
|
|
368
|
+
.ascending => break :blk level.tables.iterator_from_index(0, direction),
|
|
369
|
+
.descending => {
|
|
370
|
+
break :blk level.tables.iterator_from_cursor(
|
|
371
|
+
level.tables.last(),
|
|
372
|
+
.descending,
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
return .{
|
|
380
|
+
.level = level,
|
|
381
|
+
.inner = inner,
|
|
382
|
+
.visibility = visibility,
|
|
383
|
+
.snapshots = snapshots,
|
|
384
|
+
.direction = direction,
|
|
385
|
+
.key_range = key_range,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
pub const Iterator = struct {
|
|
390
|
+
level: *const ManifestLevel,
|
|
391
|
+
inner: Tables.Iterator,
|
|
392
|
+
visibility: Visibility,
|
|
393
|
+
snapshots: []const u64,
|
|
394
|
+
direction: Direction,
|
|
395
|
+
key_range: ?KeyRange,
|
|
396
|
+
|
|
397
|
+
pub fn next(it: *Iterator) ?*TableInfo {
|
|
398
|
+
while (it.inner.next()) |table| {
|
|
399
|
+
// We can't assert !it.inner.done as inner.next() may set done before returning.
|
|
400
|
+
|
|
401
|
+
// Skip tables that don't match the provided visibility interests.
|
|
402
|
+
switch (it.visibility) {
|
|
403
|
+
.invisible => blk: {
|
|
404
|
+
if (table.invisible(it.snapshots)) break :blk;
|
|
405
|
+
continue;
|
|
406
|
+
},
|
|
407
|
+
.visible => blk: {
|
|
408
|
+
for (it.snapshots) |snapshot| {
|
|
409
|
+
if (table.visible(snapshot)) break :blk;
|
|
410
|
+
}
|
|
411
|
+
continue;
|
|
412
|
+
},
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Filter the table using the key range if provided.
|
|
416
|
+
if (it.key_range) |key_range| {
|
|
417
|
+
switch (it.direction) {
|
|
418
|
+
.ascending => {
|
|
419
|
+
// Assert that the table is not out of bounds to the left.
|
|
420
|
+
//
|
|
421
|
+
// We can assert this as it is exactly the same key comparison when
|
|
422
|
+
// we binary search in iterator_start(), and since we move in
|
|
423
|
+
// ascending order this remains true beyond the first iteration.
|
|
424
|
+
assert(key_range.key_min <= table.key_max);
|
|
425
|
+
|
|
426
|
+
// Check if the table is out of bounds to the right.
|
|
427
|
+
if (table.key_min > key_range.key_max) {
|
|
428
|
+
it.inner.done = true;
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
.descending => {
|
|
433
|
+
// Check if the table is out of bounds to the right.
|
|
434
|
+
//
|
|
435
|
+
// Unlike in the ascending case, it is not guaranteed that
|
|
436
|
+
// table.key_min is less than or equal to key_range.key_max on the
|
|
437
|
+
// first iteration as the underlying SegmentedArray.search uses
|
|
438
|
+
// .upper_bound regardless of .direction.
|
|
439
|
+
if (table.key_min > key_range.key_max) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Check if the table is out of bounds to the left.
|
|
444
|
+
if (table.key_max < key_range.key_min) {
|
|
445
|
+
it.inner.done = true;
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return table;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
assert(it.inner.done);
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/// Returns the keys segmented array cursor at which iteration should be started.
|
|
461
|
+
/// May return null if there is nothing to iterate because we know for sure that the key
|
|
462
|
+
/// range is disjoint with the tables stored in this level.
|
|
463
|
+
///
|
|
464
|
+
/// However, the cursor returned is not guaranteed to be in range for the query as only
|
|
465
|
+
/// the key_max is stored in the index structures, not the key_min, and only the start
|
|
466
|
+
/// bound for the given direction is checked here.
|
|
467
|
+
fn iterator_start(
|
|
468
|
+
level: ManifestLevel,
|
|
469
|
+
key_min: Key,
|
|
470
|
+
key_max: Key,
|
|
471
|
+
direction: Direction,
|
|
472
|
+
) ?Keys.Cursor {
|
|
473
|
+
assert(key_min <= key_max);
|
|
474
|
+
assert(level.keys.len() == level.tables.len());
|
|
475
|
+
|
|
476
|
+
if (level.keys.len() == 0) return null;
|
|
477
|
+
|
|
478
|
+
// Ascending: Find the first table where table.key_max ≥ iterator.key_min.
|
|
479
|
+
// Descending: Find the first table where table.key_max ≥ iterator.key_max.
|
|
480
|
+
const target = level.keys.search(switch (direction) {
|
|
481
|
+
.ascending => key_min,
|
|
482
|
+
.descending => key_max,
|
|
483
|
+
});
|
|
484
|
+
assert(target.node <= level.keys.node_count);
|
|
485
|
+
|
|
486
|
+
if (level.keys.absolute_index_for_cursor(target) == level.keys.len()) {
|
|
487
|
+
return switch (direction) {
|
|
488
|
+
// The key_min of the target range is greater than the key_max of the last
|
|
489
|
+
// table in the level and we are ascending, so this range matches no tables
|
|
490
|
+
// on this level.
|
|
491
|
+
.ascending => null,
|
|
492
|
+
// The key_max of the target range is greater than the key_max of the last
|
|
493
|
+
// table in the level and we are descending, so we need to start iteration
|
|
494
|
+
// at the last table in the level.
|
|
495
|
+
.descending => level.keys.last(),
|
|
496
|
+
};
|
|
497
|
+
} else {
|
|
498
|
+
// Multiple tables in the level may share a key.
|
|
499
|
+
// Scan to the edge so that the iterator will cover them all.
|
|
500
|
+
return level.iterator_start_boundary(target, direction);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/// This function exists because there may be tables in the level with the same
|
|
505
|
+
/// key_max but non-overlapping snapshot visibility.
|
|
506
|
+
///
|
|
507
|
+
/// Put differently, there may be several tables with different snapshots but the same
|
|
508
|
+
/// `key_max`, and `iterator_start`'s binary search (`key_cursor`) may have landed in the
|
|
509
|
+
/// middle of them.
|
|
510
|
+
fn iterator_start_boundary(
|
|
511
|
+
level: ManifestLevel,
|
|
512
|
+
key_cursor: Keys.Cursor,
|
|
513
|
+
direction: Direction,
|
|
514
|
+
) Keys.Cursor {
|
|
515
|
+
var reverse = level.keys.iterator_from_cursor(key_cursor, direction.reverse());
|
|
516
|
+
assert(meta.eql(reverse.cursor, key_cursor));
|
|
517
|
+
|
|
518
|
+
// This cursor will always point to a key equal to start_key.
|
|
519
|
+
var adjusted = reverse.cursor;
|
|
520
|
+
const start_key = reverse.next().?.*;
|
|
521
|
+
assert(start_key == level.keys.element_at_cursor(adjusted));
|
|
522
|
+
|
|
523
|
+
var adjusted_next = reverse.cursor;
|
|
524
|
+
while (reverse.next()) |k| {
|
|
525
|
+
if (start_key != k.*) break;
|
|
526
|
+
adjusted = adjusted_next;
|
|
527
|
+
adjusted_next = reverse.cursor;
|
|
528
|
+
} else {
|
|
529
|
+
switch (direction) {
|
|
530
|
+
.ascending => assert(meta.eql(adjusted, level.keys.first())),
|
|
531
|
+
.descending => assert(meta.eql(adjusted, level.keys.last())),
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
assert(start_key == level.keys.element_at_cursor(adjusted));
|
|
535
|
+
|
|
536
|
+
return adjusted;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/// Returns a table which matches the given table *except possibly the snapshot_max*.
|
|
540
|
+
pub fn find(level: ManifestLevel, table: *const TableInfo) ?TableInfoReference {
|
|
541
|
+
const table_key =
|
|
542
|
+
KeyMaxSnapshotMin{ .key_max = table.key_max, .snapshot_min = table.snapshot_min };
|
|
543
|
+
const table_cursor = level.tables.search(table_key.key_from_value());
|
|
544
|
+
var level_tables = level.tables.iterator_from_cursor(table_cursor, .ascending);
|
|
545
|
+
const level_table = level_tables.next() orelse return null;
|
|
546
|
+
if (level_table.address == table.address and
|
|
547
|
+
level_table.checksum == table.checksum)
|
|
548
|
+
{
|
|
549
|
+
maybe(level_table.snapshot_max != table.snapshot_max);
|
|
550
|
+
return .{ .table_info = level_table, .generation = level.generation };
|
|
551
|
+
} else {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/// Returns whether the ManifestLevel contains the *exact* table.
|
|
557
|
+
pub fn contains(level: ManifestLevel, table: *const TableInfo) bool {
|
|
558
|
+
assert(constants.verify); // Currently only used for testing.
|
|
559
|
+
const table_found = level.find(table) orelse return false;
|
|
560
|
+
const table_exact = table.snapshot_max == table_found.table_info.snapshot_max;
|
|
561
|
+
assert(table_exact == table.equal(table_found.table_info));
|
|
562
|
+
return table_exact;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/// Given two levels (where A is the level on which this function
|
|
566
|
+
/// is invoked and B is the other level), finds a table in Level A that
|
|
567
|
+
/// overlaps with the least number of tables in Level B.
|
|
568
|
+
///
|
|
569
|
+
/// * Exits early if it finds a table that doesn't overlap with any
|
|
570
|
+
/// tables in the second level.
|
|
571
|
+
pub fn table_with_least_overlap(
|
|
572
|
+
level_a: *const ManifestLevel,
|
|
573
|
+
level_b: *const ManifestLevel,
|
|
574
|
+
snapshot: u64,
|
|
575
|
+
max_overlapping_tables: usize,
|
|
576
|
+
) ?LeastOverlapTable {
|
|
577
|
+
assert(max_overlapping_tables <= constants.lsm_growth_factor);
|
|
578
|
+
|
|
579
|
+
var optimal: ?LeastOverlapTable = null;
|
|
580
|
+
const snapshots = [1]u64{snapshot};
|
|
581
|
+
var iterations: usize = 0;
|
|
582
|
+
var it = level_a.iterator(
|
|
583
|
+
.visible,
|
|
584
|
+
&snapshots,
|
|
585
|
+
.ascending,
|
|
586
|
+
null, // All visible tables in the level therefore no KeyRange filter.
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
while (it.next()) |table| {
|
|
590
|
+
iterations += 1;
|
|
591
|
+
|
|
592
|
+
const range = level_b.tables_overlapping_with_key_range(
|
|
593
|
+
table.key_min,
|
|
594
|
+
table.key_max,
|
|
595
|
+
snapshot,
|
|
596
|
+
max_overlapping_tables,
|
|
597
|
+
) orelse continue;
|
|
598
|
+
assert(range.tables.count() <= max_overlapping_tables);
|
|
599
|
+
|
|
600
|
+
if (optimal == null or range.tables.count() < optimal.?.range.tables.count()) {
|
|
601
|
+
optimal = LeastOverlapTable{
|
|
602
|
+
.table = TableInfoReference{
|
|
603
|
+
.table_info = table,
|
|
604
|
+
.generation = level_a.generation,
|
|
605
|
+
},
|
|
606
|
+
.range = range,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
// If the table can be moved directly between levels then that is already optimal.
|
|
610
|
+
if (optimal.?.range.tables.empty()) break;
|
|
611
|
+
}
|
|
612
|
+
assert(iterations > 0);
|
|
613
|
+
assert(iterations == level_a.table_count_visible or
|
|
614
|
+
optimal.?.range.tables.empty());
|
|
615
|
+
|
|
616
|
+
return optimal.?;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/// Returns the next table in the range, after `key_exclusive` if provided.
|
|
620
|
+
///
|
|
621
|
+
/// * The table returned is visible to `snapshot`.
|
|
622
|
+
pub fn next_table(self: *const ManifestLevel, parameters: struct {
|
|
623
|
+
snapshot: u64,
|
|
624
|
+
key_min: Key,
|
|
625
|
+
key_max: Key,
|
|
626
|
+
key_exclusive: ?Key,
|
|
627
|
+
direction: Direction,
|
|
628
|
+
}) ?TableInfoReference {
|
|
629
|
+
const key_min = parameters.key_min;
|
|
630
|
+
const key_max = parameters.key_max;
|
|
631
|
+
const key_exclusive = parameters.key_exclusive;
|
|
632
|
+
const direction = parameters.direction;
|
|
633
|
+
const snapshot = parameters.snapshot;
|
|
634
|
+
const snapshots = [_]u64{snapshot};
|
|
635
|
+
|
|
636
|
+
assert(key_min <= key_max);
|
|
637
|
+
|
|
638
|
+
if (key_exclusive == null) {
|
|
639
|
+
var it = self.iterator(
|
|
640
|
+
.visible,
|
|
641
|
+
&snapshots,
|
|
642
|
+
direction,
|
|
643
|
+
KeyRange{ .key_min = key_min, .key_max = key_max },
|
|
644
|
+
);
|
|
645
|
+
if (it.next()) |table_info| {
|
|
646
|
+
return .{
|
|
647
|
+
.table_info = table_info,
|
|
648
|
+
.generation = self.generation,
|
|
649
|
+
};
|
|
650
|
+
} else {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
assert(key_min <= key_exclusive.?);
|
|
656
|
+
assert(key_exclusive.? <= key_max);
|
|
657
|
+
|
|
658
|
+
const key_min_exclusive = if (direction == .ascending) key_exclusive.? else key_min;
|
|
659
|
+
const key_max_exclusive = if (direction == .descending) key_exclusive.? else key_max;
|
|
660
|
+
assert(key_min_exclusive <= key_max_exclusive);
|
|
661
|
+
|
|
662
|
+
var it = self.iterator(
|
|
663
|
+
.visible,
|
|
664
|
+
&snapshots,
|
|
665
|
+
direction,
|
|
666
|
+
KeyRange{ .key_min = key_min_exclusive, .key_max = key_max_exclusive },
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
while (it.next()) |table| {
|
|
670
|
+
assert(table.visible(snapshot));
|
|
671
|
+
assert(table.key_min <= table.key_max);
|
|
672
|
+
assert(key_min_exclusive <= table.key_max);
|
|
673
|
+
assert(table.key_min <= key_max_exclusive);
|
|
674
|
+
|
|
675
|
+
// These conditions are required to avoid iterating over the same
|
|
676
|
+
// table twice. This is because the invoker sets key_exclusive to the
|
|
677
|
+
// key_max or key_max of the previous table returned by this function,
|
|
678
|
+
// based on the direction of iteration (ascending/descending).
|
|
679
|
+
// key_exclusive is then set as KeyRange.key_min or KeyRange.key_max for the next
|
|
680
|
+
// ManifestLevel query. This query would return the same table again,
|
|
681
|
+
// so it needs to be skipped.
|
|
682
|
+
const next = switch (direction) {
|
|
683
|
+
.ascending => table.key_min > key_exclusive.?,
|
|
684
|
+
.descending => table.key_max < key_exclusive.?,
|
|
685
|
+
};
|
|
686
|
+
if (next) {
|
|
687
|
+
return .{
|
|
688
|
+
.table_info = table,
|
|
689
|
+
.generation = self.generation,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/// Returns the smallest visible range of tables in the given level
|
|
698
|
+
/// that overlap with the given range: [key_min, key_max]
|
|
699
|
+
///
|
|
700
|
+
/// Returns null if the number of tables that intersect with the range intersects more than
|
|
701
|
+
/// max_overlapping_tables tables.
|
|
702
|
+
///
|
|
703
|
+
/// The range keys are guaranteed to encompass all the relevant level A and level B tables:
|
|
704
|
+
/// range.key_min = min(a.key_min, b.key_min)
|
|
705
|
+
/// range.key_max = max(a.key_max, b.key_max)
|
|
706
|
+
///
|
|
707
|
+
/// This last invariant is critical to ensuring that tombstones are dropped correctly.
|
|
708
|
+
///
|
|
709
|
+
/// * Assumption: Currently, we only support a maximum of lsm_growth_factor
|
|
710
|
+
/// overlapping tables. This is because OverlapRange.tables is a
|
|
711
|
+
/// BoundedArray of size lsm_growth_factor. This works with our current
|
|
712
|
+
/// compaction strategy that is guaranteed to choose a table with that
|
|
713
|
+
/// intersects with <= lsm_growth_factor tables in the next level.
|
|
714
|
+
pub fn tables_overlapping_with_key_range(
|
|
715
|
+
level: *const ManifestLevel,
|
|
716
|
+
key_min: Key,
|
|
717
|
+
key_max: Key,
|
|
718
|
+
snapshot: u64,
|
|
719
|
+
max_overlapping_tables: usize,
|
|
720
|
+
) ?OverlapRange {
|
|
721
|
+
assert(max_overlapping_tables <= constants.lsm_growth_factor);
|
|
722
|
+
|
|
723
|
+
var range = OverlapRange{
|
|
724
|
+
.key_min = key_min,
|
|
725
|
+
.key_max = key_max,
|
|
726
|
+
.tables = .{},
|
|
727
|
+
};
|
|
728
|
+
const snapshots = [1]u64{snapshot};
|
|
729
|
+
var it = level.iterator(
|
|
730
|
+
.visible,
|
|
731
|
+
&snapshots,
|
|
732
|
+
.ascending,
|
|
733
|
+
KeyRange{ .key_min = range.key_min, .key_max = range.key_max },
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
while (it.next()) |table| {
|
|
737
|
+
assert(table.visible(lsm.snapshot_latest));
|
|
738
|
+
assert(table.key_min <= table.key_max);
|
|
739
|
+
assert(range.key_min <= table.key_max);
|
|
740
|
+
assert(table.key_min <= range.key_max);
|
|
741
|
+
|
|
742
|
+
// The first iterated table.key_min/max may overlap range.key_min/max entirely.
|
|
743
|
+
if (table.key_min < range.key_min) {
|
|
744
|
+
range.key_min = table.key_min;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Thereafter, iterated tables may/may not extend the range in ascending order.
|
|
748
|
+
if (table.key_max > range.key_max) {
|
|
749
|
+
range.key_max = table.key_max;
|
|
750
|
+
}
|
|
751
|
+
if (range.tables.count() < max_overlapping_tables) {
|
|
752
|
+
const table_info_reference = TableInfoReference{
|
|
753
|
+
.table_info = table,
|
|
754
|
+
.generation = level.generation,
|
|
755
|
+
};
|
|
756
|
+
range.tables.push(table_info_reference);
|
|
757
|
+
} else {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
assert(range.key_min <= range.key_max);
|
|
762
|
+
assert(range.key_min <= key_min);
|
|
763
|
+
assert(range.tables.count() <= max_overlapping_tables);
|
|
764
|
+
assert(key_max <= range.key_max);
|
|
765
|
+
|
|
766
|
+
return range;
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
pub fn TestContextType(
|
|
772
|
+
comptime node_size: u32,
|
|
773
|
+
comptime Key: type,
|
|
774
|
+
comptime table_count_max_tree: u32,
|
|
775
|
+
) type {
|
|
776
|
+
return struct {
|
|
777
|
+
const TestContext = @This();
|
|
778
|
+
|
|
779
|
+
const testing = std.testing;
|
|
780
|
+
|
|
781
|
+
const log = false;
|
|
782
|
+
|
|
783
|
+
const Value = packed struct {
|
|
784
|
+
key: Key,
|
|
785
|
+
tombstone: bool,
|
|
786
|
+
padding: u63 = 0,
|
|
787
|
+
|
|
788
|
+
comptime {
|
|
789
|
+
assert(stdx.no_padding(Value));
|
|
790
|
+
assert(@bitSizeOf(Value) == @sizeOf(Value) * 8);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
inline fn key_from_value(value: *const Value) Key {
|
|
795
|
+
return value.key;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
inline fn tombstone_from_key(key: Key) Value {
|
|
799
|
+
return .{ .key = key, .tombstone = true };
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
inline fn tombstone(value: *const Value) bool {
|
|
803
|
+
return value.tombstone;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const Table = @import("table.zig").TableType(
|
|
807
|
+
Key,
|
|
808
|
+
Value,
|
|
809
|
+
key_from_value,
|
|
810
|
+
std.math.maxInt(Key),
|
|
811
|
+
tombstone,
|
|
812
|
+
tombstone_from_key,
|
|
813
|
+
1, // Doesn't matter for this test.
|
|
814
|
+
.general,
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
const TableInfo = @import("manifest.zig").TreeTableInfoType(Table);
|
|
818
|
+
const NodePoolType = @import("node_pool.zig").NodePoolType;
|
|
819
|
+
|
|
820
|
+
const TestPool = NodePoolType(node_size, @alignOf(TableInfo));
|
|
821
|
+
const TestLevel = ManifestLevelType(TestPool, Key, TableInfo, table_count_max_tree);
|
|
822
|
+
const KeyRange = TestLevel.KeyRange;
|
|
823
|
+
|
|
824
|
+
prng: *stdx.PRNG,
|
|
825
|
+
|
|
826
|
+
pool: TestPool,
|
|
827
|
+
level: TestLevel,
|
|
828
|
+
|
|
829
|
+
snapshot_max: u64 = 1,
|
|
830
|
+
snapshots: stdx.BoundedArrayType(u64, 8) = .{},
|
|
831
|
+
snapshot_tables: stdx.BoundedArrayType(std.ArrayList(TableInfo), 8) = .{},
|
|
832
|
+
|
|
833
|
+
/// Contains only tables with snapshot_max == lsm.snapshot_latest
|
|
834
|
+
reference: std.ArrayList(TableInfo),
|
|
835
|
+
|
|
836
|
+
inserts: u64 = 0,
|
|
837
|
+
removes: u64 = 0,
|
|
838
|
+
|
|
839
|
+
fn init(context: *TestContext, prng: *stdx.PRNG) !void {
|
|
840
|
+
context.* = .{
|
|
841
|
+
.prng = prng,
|
|
842
|
+
|
|
843
|
+
.pool = undefined,
|
|
844
|
+
.level = undefined,
|
|
845
|
+
.reference = undefined,
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
try context.pool.init(
|
|
849
|
+
testing.allocator,
|
|
850
|
+
TestLevel.Keys.node_count_max + TestLevel.Tables.node_count_max,
|
|
851
|
+
);
|
|
852
|
+
errdefer context.pool.deinit(testing.allocator);
|
|
853
|
+
|
|
854
|
+
try context.level.init(testing.allocator, &context.pool);
|
|
855
|
+
errdefer context.level.deinit(testing.allocator, &context.pool);
|
|
856
|
+
|
|
857
|
+
context.reference = std.ArrayList(TableInfo).init(testing.allocator);
|
|
858
|
+
errdefer context.reference.deinit();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
fn deinit(context: *TestContext) void {
|
|
862
|
+
context.level.deinit(testing.allocator, &context.pool);
|
|
863
|
+
context.pool.deinit(testing.allocator);
|
|
864
|
+
|
|
865
|
+
for (context.snapshot_tables.slice()) |tables| tables.deinit();
|
|
866
|
+
|
|
867
|
+
context.reference.deinit();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
fn run(context: *TestContext) !void {
|
|
871
|
+
const Action = enum { insert_tables, create_snapshot, delete_tables, drop_snapshot };
|
|
872
|
+
if (log) std.debug.print("\n", .{});
|
|
873
|
+
{
|
|
874
|
+
var i: usize = 0;
|
|
875
|
+
while (i < table_count_max_tree * 2) : (i += 1) {
|
|
876
|
+
switch (context.prng.enum_weighted(Action, .{
|
|
877
|
+
.insert_tables = 60,
|
|
878
|
+
.create_snapshot = 10,
|
|
879
|
+
.delete_tables = 25,
|
|
880
|
+
.drop_snapshot = 5,
|
|
881
|
+
})) {
|
|
882
|
+
.insert_tables => try context.insert_tables(),
|
|
883
|
+
.create_snapshot => try context.create_snapshot(),
|
|
884
|
+
.delete_tables => try context.delete_tables(),
|
|
885
|
+
.drop_snapshot => try context.drop_snapshot(),
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
{
|
|
891
|
+
var i: usize = 0;
|
|
892
|
+
while (i < table_count_max_tree * 2) : (i += 1) {
|
|
893
|
+
switch (context.prng.enum_weighted(Action, .{
|
|
894
|
+
.insert_tables = 35,
|
|
895
|
+
.create_snapshot = 5,
|
|
896
|
+
.delete_tables = 50,
|
|
897
|
+
.drop_snapshot = 10,
|
|
898
|
+
})) {
|
|
899
|
+
.insert_tables => try context.insert_tables(),
|
|
900
|
+
.create_snapshot => try context.create_snapshot(),
|
|
901
|
+
.delete_tables => try context.delete_tables(),
|
|
902
|
+
.drop_snapshot => try context.drop_snapshot(),
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
try context.remove_all();
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
fn insert_tables(context: *TestContext) !void {
|
|
911
|
+
const count_free = table_count_max_tree - context.level.keys.len();
|
|
912
|
+
|
|
913
|
+
if (count_free == 0) return;
|
|
914
|
+
|
|
915
|
+
var buffer: [13]TableInfo = undefined;
|
|
916
|
+
|
|
917
|
+
const count_max = @min(count_free, 13);
|
|
918
|
+
const count = context.prng.range_inclusive(u32, 1, count_max);
|
|
919
|
+
|
|
920
|
+
{
|
|
921
|
+
var key: Key = context.prng.int_inclusive(Key, table_count_max_tree * 64);
|
|
922
|
+
|
|
923
|
+
for (buffer[0..count]) |*table| {
|
|
924
|
+
table.* = context.random_greater_non_overlapping_table(key);
|
|
925
|
+
key = table.key_max;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
for (buffer[0..count]) |*table| {
|
|
930
|
+
context.level.insert_table(&context.pool, table);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
for (buffer[0..count]) |table| {
|
|
934
|
+
const index = binary_search.binary_search_values_upsert_index(
|
|
935
|
+
Key,
|
|
936
|
+
TableInfo,
|
|
937
|
+
key_min_from_table,
|
|
938
|
+
context.reference.items,
|
|
939
|
+
table.key_max,
|
|
940
|
+
.{},
|
|
941
|
+
);
|
|
942
|
+
// Can't be equal as the tables may not overlap
|
|
943
|
+
if (index < context.reference.items.len) {
|
|
944
|
+
assert(context.reference.items[index].key_min > table.key_max);
|
|
945
|
+
}
|
|
946
|
+
context.reference.insert(index, table) catch unreachable;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
context.inserts += count;
|
|
950
|
+
|
|
951
|
+
try context.verify();
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
fn random_greater_non_overlapping_table(context: *TestContext, key: Key) TableInfo {
|
|
955
|
+
var new_key_min = key + context.prng.range_inclusive(Key, 1, 31);
|
|
956
|
+
assert(new_key_min > key);
|
|
957
|
+
|
|
958
|
+
const i = binary_search.binary_search_values_upsert_index(
|
|
959
|
+
Key,
|
|
960
|
+
TableInfo,
|
|
961
|
+
key_min_from_table,
|
|
962
|
+
context.reference.items,
|
|
963
|
+
new_key_min,
|
|
964
|
+
.{},
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
if (i > 0) {
|
|
968
|
+
if (new_key_min <= context.reference.items[i - 1].key_max) {
|
|
969
|
+
new_key_min = context.reference.items[i - 1].key_max + 1;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const next_key_min = for (context.reference.items[i..]) |table| {
|
|
974
|
+
switch (std.math.order(new_key_min, table.key_min)) {
|
|
975
|
+
.lt => break table.key_min,
|
|
976
|
+
.eq => new_key_min = table.key_max + 1,
|
|
977
|
+
.gt => unreachable,
|
|
978
|
+
}
|
|
979
|
+
} else math.maxInt(Key);
|
|
980
|
+
|
|
981
|
+
const max_delta = @min(32, next_key_min - 1 - new_key_min);
|
|
982
|
+
const new_key_max = new_key_min + context.prng.int_inclusive(Key, max_delta);
|
|
983
|
+
|
|
984
|
+
return .{
|
|
985
|
+
.checksum = context.prng.int(u128),
|
|
986
|
+
.address = context.prng.int(u64),
|
|
987
|
+
.snapshot_min = context.take_snapshot(),
|
|
988
|
+
.key_min = new_key_min,
|
|
989
|
+
.key_max = new_key_max,
|
|
990
|
+
.value_count = context.prng.int(u32),
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/// See Manifest.take_snapshot()
|
|
995
|
+
fn take_snapshot(context: *TestContext) u64 {
|
|
996
|
+
// A snapshot cannot be 0 as this is a reserved value in the superblock.
|
|
997
|
+
assert(context.snapshot_max > 0);
|
|
998
|
+
// The constant snapshot_latest must compare greater than any issued snapshot.
|
|
999
|
+
// This also ensures that we are not about to overflow the u64 counter.
|
|
1000
|
+
assert(context.snapshot_max < lsm.snapshot_latest - 1);
|
|
1001
|
+
|
|
1002
|
+
context.snapshot_max += 1;
|
|
1003
|
+
|
|
1004
|
+
return context.snapshot_max;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
fn create_snapshot(context: *TestContext) !void {
|
|
1008
|
+
if (context.snapshots.full()) return;
|
|
1009
|
+
|
|
1010
|
+
context.snapshots.push(context.take_snapshot());
|
|
1011
|
+
|
|
1012
|
+
var tables = std.ArrayList(TableInfo).init(testing.allocator);
|
|
1013
|
+
try tables.insertSlice(0, context.reference.items);
|
|
1014
|
+
context.snapshot_tables.push(tables);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
fn drop_snapshot(context: *TestContext) !void {
|
|
1018
|
+
if (context.snapshots.empty()) return;
|
|
1019
|
+
|
|
1020
|
+
const index = context.prng.index(context.snapshots.const_slice());
|
|
1021
|
+
|
|
1022
|
+
_ = context.snapshots.swap_remove(index);
|
|
1023
|
+
var tables = context.snapshot_tables.swap_remove(index);
|
|
1024
|
+
defer tables.deinit();
|
|
1025
|
+
|
|
1026
|
+
// Use this memory as a scratch buffer since it's conveniently already allocated.
|
|
1027
|
+
tables.clearRetainingCapacity();
|
|
1028
|
+
|
|
1029
|
+
const snapshots = context.snapshots.slice();
|
|
1030
|
+
|
|
1031
|
+
// Ensure that iteration with a null key range in both directions is tested.
|
|
1032
|
+
if (context.prng.boolean()) {
|
|
1033
|
+
var it = context.level.iterator(.invisible, snapshots, .ascending, null);
|
|
1034
|
+
while (it.next()) |table| try tables.append(table.*);
|
|
1035
|
+
} else {
|
|
1036
|
+
var it = context.level.iterator(.invisible, snapshots, .descending, null);
|
|
1037
|
+
while (it.next()) |table| try tables.append(table.*);
|
|
1038
|
+
mem.reverse(TableInfo, tables.items);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (tables.items.len > 0) {
|
|
1042
|
+
for (tables.items) |*table| {
|
|
1043
|
+
context.level.remove_table(&context.pool, table);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
fn delete_tables(context: *TestContext) !void {
|
|
1049
|
+
const reference_len: u32 = @intCast(context.reference.items.len);
|
|
1050
|
+
if (reference_len == 0) return;
|
|
1051
|
+
|
|
1052
|
+
const count_max = @min(reference_len, 13);
|
|
1053
|
+
const count = context.prng.range_inclusive(u32, 1, count_max);
|
|
1054
|
+
|
|
1055
|
+
assert(context.reference.items.len <= table_count_max_tree);
|
|
1056
|
+
const index = context.prng.int_inclusive(u32, reference_len - count);
|
|
1057
|
+
|
|
1058
|
+
const snapshot = context.take_snapshot();
|
|
1059
|
+
|
|
1060
|
+
for (context.reference.items[index..][0..count]) |*table| {
|
|
1061
|
+
const cursor_start = context.level.iterator_start(
|
|
1062
|
+
table.key_min,
|
|
1063
|
+
table.key_min,
|
|
1064
|
+
.ascending,
|
|
1065
|
+
).?;
|
|
1066
|
+
const absolute_index = context.level.keys.absolute_index_for_cursor(cursor_start);
|
|
1067
|
+
|
|
1068
|
+
var it = context.level.tables.iterator_from_index(absolute_index, .ascending);
|
|
1069
|
+
while (it.next()) |level_table| {
|
|
1070
|
+
if (level_table.equal(table)) {
|
|
1071
|
+
context.level.set_snapshot_max(snapshot, .{
|
|
1072
|
+
.table_info = level_table,
|
|
1073
|
+
.generation = context.level.generation,
|
|
1074
|
+
});
|
|
1075
|
+
table.snapshot_max = snapshot;
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
for (context.snapshot_tables.slice()) |tables| {
|
|
1082
|
+
for (tables.items) |*table| {
|
|
1083
|
+
for (context.reference.items[index..][0..count]) |modified| {
|
|
1084
|
+
if (table.address == modified.address) {
|
|
1085
|
+
table.snapshot_max = snapshot;
|
|
1086
|
+
assert(table.equal(&modified));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
{
|
|
1093
|
+
var to_remove = std.ArrayList(TableInfo).init(testing.allocator);
|
|
1094
|
+
defer to_remove.deinit();
|
|
1095
|
+
|
|
1096
|
+
for (context.reference.items[index..][0..count]) |table| {
|
|
1097
|
+
if (table.invisible(context.snapshots.slice())) {
|
|
1098
|
+
try to_remove.append(table);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (log) {
|
|
1103
|
+
std.debug.print("Removing tables: ", .{});
|
|
1104
|
+
for (to_remove.items) |t| {
|
|
1105
|
+
std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
|
|
1106
|
+
}
|
|
1107
|
+
std.debug.print("\n", .{});
|
|
1108
|
+
std.debug.print("\nactual: ", .{});
|
|
1109
|
+
var it = context.level.iterator(
|
|
1110
|
+
.invisible,
|
|
1111
|
+
context.snapshots.slice(),
|
|
1112
|
+
.ascending,
|
|
1113
|
+
KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
|
|
1114
|
+
);
|
|
1115
|
+
while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
|
|
1116
|
+
std.debug.print("\n", .{});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (to_remove.items.len > 0) {
|
|
1120
|
+
for (to_remove.items) |*table| {
|
|
1121
|
+
context.level.remove_table(&context.pool, table);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
context.reference.replaceRange(index, count, &[0]TableInfo{}) catch unreachable;
|
|
1127
|
+
|
|
1128
|
+
context.removes += count;
|
|
1129
|
+
|
|
1130
|
+
try context.verify();
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
fn remove_all(context: *TestContext) !void {
|
|
1134
|
+
while (context.snapshots.count() > 0) try context.drop_snapshot();
|
|
1135
|
+
while (context.reference.items.len > 0) try context.delete_tables();
|
|
1136
|
+
|
|
1137
|
+
try testing.expectEqual(@as(u32, 0), context.level.keys.len());
|
|
1138
|
+
try testing.expectEqual(@as(u32, 0), context.level.tables.len());
|
|
1139
|
+
try testing.expect(context.inserts > 0);
|
|
1140
|
+
try testing.expect(context.inserts == context.removes);
|
|
1141
|
+
|
|
1142
|
+
if (log) {
|
|
1143
|
+
std.debug.print("\ninserts: {}, removes: {}\n", .{
|
|
1144
|
+
context.inserts,
|
|
1145
|
+
context.removes,
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
try context.verify();
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
fn verify(context: *TestContext) !void {
|
|
1153
|
+
try context.verify_snapshot(lsm.snapshot_latest, context.reference.items);
|
|
1154
|
+
|
|
1155
|
+
for (context.snapshots.slice(), 0..) |snapshot, i| {
|
|
1156
|
+
try context.verify_snapshot(snapshot, context.snapshot_tables.get(i).items);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
fn verify_snapshot(
|
|
1161
|
+
context: *TestContext,
|
|
1162
|
+
snapshot: u64,
|
|
1163
|
+
reference: []const TableInfo,
|
|
1164
|
+
) !void {
|
|
1165
|
+
if (log) {
|
|
1166
|
+
std.debug.print("\nsnapshot: {}\n", .{snapshot});
|
|
1167
|
+
std.debug.print("expect: ", .{});
|
|
1168
|
+
for (reference) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
|
|
1169
|
+
|
|
1170
|
+
std.debug.print("\nactual: ", .{});
|
|
1171
|
+
var it = context.level.iterator(
|
|
1172
|
+
.visible,
|
|
1173
|
+
@as(*const [1]u64, &snapshot),
|
|
1174
|
+
.ascending,
|
|
1175
|
+
KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
|
|
1176
|
+
);
|
|
1177
|
+
while (it.next()) |t| std.debug.print("[{},{}], ", .{ t.key_min, t.key_max });
|
|
1178
|
+
std.debug.print("\n", .{});
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
{
|
|
1182
|
+
var it = context.level.iterator(
|
|
1183
|
+
.visible,
|
|
1184
|
+
@as(*const [1]u64, &snapshot),
|
|
1185
|
+
.ascending,
|
|
1186
|
+
KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
|
|
1187
|
+
);
|
|
1188
|
+
|
|
1189
|
+
for (reference) |expect| {
|
|
1190
|
+
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1191
|
+
try testing.expectEqual(expect, actual.*);
|
|
1192
|
+
}
|
|
1193
|
+
try testing.expectEqual(@as(?*const TableInfo, null), it.next());
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
{
|
|
1197
|
+
var it = context.level.iterator(
|
|
1198
|
+
.visible,
|
|
1199
|
+
@as(*const [1]u64, &snapshot),
|
|
1200
|
+
.descending,
|
|
1201
|
+
KeyRange{ .key_min = 0, .key_max = math.maxInt(Key) },
|
|
1202
|
+
);
|
|
1203
|
+
|
|
1204
|
+
var i = reference.len;
|
|
1205
|
+
while (i > 0) {
|
|
1206
|
+
i -= 1;
|
|
1207
|
+
|
|
1208
|
+
const expect = reference[i];
|
|
1209
|
+
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1210
|
+
try testing.expectEqual(expect, actual.*);
|
|
1211
|
+
}
|
|
1212
|
+
try testing.expectEqual(@as(?*const TableInfo, null), it.next());
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
if (reference.len > 0) {
|
|
1216
|
+
const reference_len: u32 = @intCast(reference.len);
|
|
1217
|
+
const start = context.prng.int_inclusive(u32, reference_len - 1);
|
|
1218
|
+
const end = context.prng.range_inclusive(u32, start, reference_len - 1);
|
|
1219
|
+
|
|
1220
|
+
const key_min = reference[start].key_min;
|
|
1221
|
+
const key_max = reference[end].key_max;
|
|
1222
|
+
|
|
1223
|
+
{
|
|
1224
|
+
var it = context.level.iterator(
|
|
1225
|
+
.visible,
|
|
1226
|
+
@as(*const [1]u64, &snapshot),
|
|
1227
|
+
.ascending,
|
|
1228
|
+
KeyRange{ .key_min = key_min, .key_max = key_max },
|
|
1229
|
+
);
|
|
1230
|
+
|
|
1231
|
+
for (reference[start .. end + 1]) |expect| {
|
|
1232
|
+
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1233
|
+
try testing.expectEqual(expect, actual.*);
|
|
1234
|
+
}
|
|
1235
|
+
try testing.expectEqual(@as(?*const TableInfo, null), it.next());
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
{
|
|
1239
|
+
var it = context.level.iterator(
|
|
1240
|
+
.visible,
|
|
1241
|
+
@as(*const [1]u64, &snapshot),
|
|
1242
|
+
.descending,
|
|
1243
|
+
KeyRange{ .key_min = key_min, .key_max = key_max },
|
|
1244
|
+
);
|
|
1245
|
+
|
|
1246
|
+
var i = end + 1;
|
|
1247
|
+
while (i > start) {
|
|
1248
|
+
i -= 1;
|
|
1249
|
+
|
|
1250
|
+
const expect = reference[i];
|
|
1251
|
+
const actual = it.next() orelse return error.TestUnexpectedResult;
|
|
1252
|
+
try testing.expectEqual(expect, actual.*);
|
|
1253
|
+
}
|
|
1254
|
+
try testing.expectEqual(@as(?*const TableInfo, null), it.next());
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
inline fn key_min_from_table(table: *const TableInfo) Key {
|
|
1260
|
+
return table.key_min;
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
test "ManifestLevel" {
|
|
1266
|
+
const seed = 42;
|
|
1267
|
+
|
|
1268
|
+
var prng = stdx.PRNG.from_seed(seed);
|
|
1269
|
+
const Options = struct {
|
|
1270
|
+
key_type: type,
|
|
1271
|
+
node_size: u32,
|
|
1272
|
+
table_count_max_tree: u32,
|
|
1273
|
+
};
|
|
1274
|
+
|
|
1275
|
+
inline for (.{
|
|
1276
|
+
Options{ .key_type = u64, .node_size = 256, .table_count_max_tree = 33 },
|
|
1277
|
+
Options{ .key_type = u64, .node_size = 256, .table_count_max_tree = 34 },
|
|
1278
|
+
Options{ .key_type = u64, .node_size = 256, .table_count_max_tree = 1024 },
|
|
1279
|
+
Options{ .key_type = u64, .node_size = 512, .table_count_max_tree = 1024 },
|
|
1280
|
+
Options{ .key_type = u64, .node_size = 1024, .table_count_max_tree = 1024 },
|
|
1281
|
+
}) |options| {
|
|
1282
|
+
const TestContext = TestContextType(
|
|
1283
|
+
options.node_size,
|
|
1284
|
+
options.key_type,
|
|
1285
|
+
options.table_count_max_tree,
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
var context: TestContext = undefined;
|
|
1289
|
+
try context.init(&prng);
|
|
1290
|
+
defer context.deinit();
|
|
1291
|
+
|
|
1292
|
+
try context.run();
|
|
1293
|
+
}
|
|
1294
|
+
}
|