tigerbeetle 0.0.36 → 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/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/platforms.rb +9 -0
- data/lib/tigerbeetle/version.rb +1 -1
- data/tigerbeetle.gemspec +22 -5
- metadata +242 -3
- data/ext/tb_client/pkg.tar.gz +0 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
//! Verify deterministic storage.
|
|
2
|
+
//!
|
|
3
|
+
//! At each replica compact and checkpoint, check that storage is byte-for-byte identical across
|
|
4
|
+
//! replicas.
|
|
5
|
+
//!
|
|
6
|
+
//! Areas verified between compaction bars:
|
|
7
|
+
//! - Acquired Grid blocks (when ¬syncing) (excluding an open manifest block)
|
|
8
|
+
//!
|
|
9
|
+
//! Areas verified at checkpoint:
|
|
10
|
+
//! - SuperBlock vsr_state.checkpoint
|
|
11
|
+
//! - ClientReplies (when repair finishes)
|
|
12
|
+
//! - Acquired Grid blocks (when syncing finishes)
|
|
13
|
+
//!
|
|
14
|
+
//! Areas not verified:
|
|
15
|
+
//! - SuperBlock headers, which hold replica-specific state.
|
|
16
|
+
//! - WAL headers, which may differ because the WAL writes deliberately corrupt redundant headers
|
|
17
|
+
//! to faulty slots to ensure recovery is consistent.
|
|
18
|
+
//! - WAL prepares — a replica can commit + checkpoint an op before it is persisted to the WAL.
|
|
19
|
+
//! (The primary can commit from the pipeline-queue, backups can commit from the pipeline-cache.)
|
|
20
|
+
//! - Non-allocated Grid blocks, which may differ due to state sync.
|
|
21
|
+
const std = @import("std");
|
|
22
|
+
const assert = std.debug.assert;
|
|
23
|
+
const log = std.log.scoped(.storage_checker);
|
|
24
|
+
|
|
25
|
+
const constants = @import("../../constants.zig");
|
|
26
|
+
const stdx = @import("stdx");
|
|
27
|
+
const vsr = @import("../../vsr.zig");
|
|
28
|
+
const schema = @import("../../lsm/schema.zig");
|
|
29
|
+
const Storage = @import("../storage.zig").Storage;
|
|
30
|
+
|
|
31
|
+
/// After each compaction bar, save the cumulative hash of all acquired grid blocks.
|
|
32
|
+
/// (Excluding the open manifest log block, if any.)
|
|
33
|
+
///
|
|
34
|
+
/// This is sparse – not every compaction is necessarily recorded.
|
|
35
|
+
/// For example, the StorageChecker will not check the grid if the replica is still state syncing,
|
|
36
|
+
/// which may cause a bar to be skipped over.
|
|
37
|
+
const Compactions = std.AutoHashMap(u64, u128);
|
|
38
|
+
|
|
39
|
+
/// Maps from op_checkpoint to cumulative storage checksum.
|
|
40
|
+
///
|
|
41
|
+
/// Not every checkpoint is necessarily recorded — a replica calls on_checkpoint *at most* once.
|
|
42
|
+
/// For example, a replica will not call on_checkpoint if it crashes (during a checkpoint) after
|
|
43
|
+
/// writing 2 superblock copies. (This could be repeated by other replicas, causing a checkpoint
|
|
44
|
+
/// op to be skipped in Checkpoints).
|
|
45
|
+
const Checkpoints = std.AutoHashMap(u64, Checkpoint);
|
|
46
|
+
|
|
47
|
+
const CheckpointArea = enum {
|
|
48
|
+
superblock_checkpoint,
|
|
49
|
+
client_replies,
|
|
50
|
+
grid,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const Checkpoint = std.enums.EnumMap(CheckpointArea, u128);
|
|
54
|
+
|
|
55
|
+
pub const StorageChecker = struct {
|
|
56
|
+
const SuperBlock = vsr.SuperBlockType(Storage);
|
|
57
|
+
compactions: Compactions,
|
|
58
|
+
checkpoints: Checkpoints,
|
|
59
|
+
|
|
60
|
+
free_set: vsr.FreeSet,
|
|
61
|
+
free_set_blocks_acquired_encoded: []align(@alignOf(u64)) u8,
|
|
62
|
+
free_set_blocks_released_encoded: []align(@alignOf(u64)) u8,
|
|
63
|
+
|
|
64
|
+
client_sessions: vsr.ClientSessions,
|
|
65
|
+
client_sessions_buffer: []align(@sizeOf(u256)) u8,
|
|
66
|
+
|
|
67
|
+
pub fn init(allocator: std.mem.Allocator) !StorageChecker {
|
|
68
|
+
var compactions = Compactions.init(allocator);
|
|
69
|
+
errdefer compactions.deinit();
|
|
70
|
+
|
|
71
|
+
var checkpoints = Checkpoints.init(allocator);
|
|
72
|
+
errdefer checkpoints.deinit();
|
|
73
|
+
|
|
74
|
+
var free_set = try vsr.FreeSet.init(
|
|
75
|
+
allocator,
|
|
76
|
+
.{
|
|
77
|
+
.grid_size_limit = Storage.grid_blocks_max * constants.block_size,
|
|
78
|
+
.blocks_released_prior_checkpoint_durability_max = 0,
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
errdefer free_set.deinit(allocator);
|
|
82
|
+
|
|
83
|
+
var client_sessions = try vsr.ClientSessions.init(allocator);
|
|
84
|
+
errdefer client_sessions.deinit(allocator);
|
|
85
|
+
|
|
86
|
+
const free_set_size = free_set.encode_size_max();
|
|
87
|
+
|
|
88
|
+
const free_set_blocks_acquired_encoded =
|
|
89
|
+
try allocator.alignedAlloc(u8, @alignOf(u64), free_set_size);
|
|
90
|
+
errdefer allocator.free(free_set_blocks_acquired_encoded);
|
|
91
|
+
|
|
92
|
+
const free_set_blocks_released_encoded =
|
|
93
|
+
try allocator.alignedAlloc(u8, @alignOf(u64), free_set_size);
|
|
94
|
+
errdefer allocator.free(free_set_blocks_released_encoded);
|
|
95
|
+
|
|
96
|
+
const client_sessions_buffer =
|
|
97
|
+
try allocator.alignedAlloc(u8, @sizeOf(u256), vsr.ClientSessions.encode_size);
|
|
98
|
+
errdefer allocator.free(client_sessions_buffer);
|
|
99
|
+
|
|
100
|
+
return StorageChecker{
|
|
101
|
+
.compactions = compactions,
|
|
102
|
+
.checkpoints = checkpoints,
|
|
103
|
+
.free_set = free_set,
|
|
104
|
+
.free_set_blocks_acquired_encoded = free_set_blocks_acquired_encoded,
|
|
105
|
+
.free_set_blocks_released_encoded = free_set_blocks_released_encoded,
|
|
106
|
+
.client_sessions = client_sessions,
|
|
107
|
+
.client_sessions_buffer = client_sessions_buffer,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub fn deinit(checker: *StorageChecker, allocator: std.mem.Allocator) void {
|
|
112
|
+
allocator.free(checker.client_sessions_buffer);
|
|
113
|
+
allocator.free(checker.free_set_blocks_acquired_encoded);
|
|
114
|
+
allocator.free(checker.free_set_blocks_released_encoded);
|
|
115
|
+
checker.client_sessions.deinit(allocator);
|
|
116
|
+
checker.free_set.deinit(allocator);
|
|
117
|
+
checker.checkpoints.deinit();
|
|
118
|
+
checker.compactions.deinit();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub fn replica_compact(
|
|
122
|
+
checker: *StorageChecker,
|
|
123
|
+
comptime Replica: type,
|
|
124
|
+
replica: *const Replica,
|
|
125
|
+
) !void {
|
|
126
|
+
const superblock: *const SuperBlock = &replica.superblock;
|
|
127
|
+
// If we are recovering from a crash, don't test the checksum until we are caught up.
|
|
128
|
+
// Until then our grid's checksum is too far ahead.
|
|
129
|
+
if (superblock.working.vsr_state.op_compacted(replica.commit_min)) return;
|
|
130
|
+
// If we are syncing, our grid will not be up to date.
|
|
131
|
+
if (superblock.working.vsr_state.sync_op_max > 0) return;
|
|
132
|
+
|
|
133
|
+
const bar_beat_count = constants.lsm_compaction_ops;
|
|
134
|
+
if ((replica.commit_min + 1) % bar_beat_count != 0) return;
|
|
135
|
+
|
|
136
|
+
const checksum = checker.checksum_grid(
|
|
137
|
+
@TypeOf(replica.state_machine.forest),
|
|
138
|
+
&replica.state_machine.forest,
|
|
139
|
+
.free_set_from_memory,
|
|
140
|
+
);
|
|
141
|
+
log.debug("{?}: replica_compact: op={} area=grid checksum={x:0>32}", .{
|
|
142
|
+
superblock.replica_index,
|
|
143
|
+
replica.commit_min,
|
|
144
|
+
checksum,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (checker.compactions.get(replica.commit_min)) |checksum_expect| {
|
|
148
|
+
if (checksum_expect != checksum) {
|
|
149
|
+
log.err("{?}: replica_compact: mismatch " ++
|
|
150
|
+
"area=grid expect={x:0>32} actual={x:0>32}", .{
|
|
151
|
+
superblock.replica_index,
|
|
152
|
+
checksum_expect,
|
|
153
|
+
checksum,
|
|
154
|
+
});
|
|
155
|
+
return error.StorageMismatch;
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
try checker.compactions.putNoClobber(replica.commit_min, checksum);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
pub fn replica_checkpoint(
|
|
163
|
+
checker: *StorageChecker,
|
|
164
|
+
comptime Replica: type,
|
|
165
|
+
replica: *const Replica,
|
|
166
|
+
) !void {
|
|
167
|
+
replica.assert_free_set_consistent();
|
|
168
|
+
|
|
169
|
+
const syncing = replica.superblock.working.vsr_state.sync_op_max > 0;
|
|
170
|
+
try checker.check(
|
|
171
|
+
"replica_checkpoint",
|
|
172
|
+
@TypeOf(replica.state_machine.forest),
|
|
173
|
+
&replica.state_machine.forest,
|
|
174
|
+
std.enums.EnumSet(CheckpointArea).init(.{
|
|
175
|
+
.superblock_checkpoint = true,
|
|
176
|
+
.client_replies = !syncing,
|
|
177
|
+
.grid = !syncing,
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (!syncing) assert(checker.checkpoints.count() > 0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// Invoked when both superblock and content sync is complete.
|
|
185
|
+
pub fn replica_sync(
|
|
186
|
+
checker: *StorageChecker,
|
|
187
|
+
comptime Replica: type,
|
|
188
|
+
replica: *const Replica,
|
|
189
|
+
) !void {
|
|
190
|
+
try checker.check(
|
|
191
|
+
"replica_sync",
|
|
192
|
+
@TypeOf(replica.state_machine.forest),
|
|
193
|
+
&replica.state_machine.forest,
|
|
194
|
+
std.enums.EnumSet(CheckpointArea).init(.{
|
|
195
|
+
.superblock_checkpoint = true,
|
|
196
|
+
// The replica may have have already committed some additional prepares atop the
|
|
197
|
+
// checkpoint, so its client-replies zone will have mutated.
|
|
198
|
+
.client_replies = false,
|
|
199
|
+
.grid = true,
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn check(
|
|
205
|
+
checker: *StorageChecker,
|
|
206
|
+
caller: []const u8,
|
|
207
|
+
comptime Forest: type,
|
|
208
|
+
forest: *const Forest,
|
|
209
|
+
areas: std.enums.EnumSet(CheckpointArea),
|
|
210
|
+
) !void {
|
|
211
|
+
const superblock: *const SuperBlock = forest.grid.superblock;
|
|
212
|
+
const op_checkpoint = superblock.working.vsr_state.checkpoint.header.op;
|
|
213
|
+
|
|
214
|
+
const checkpoint_actual = checkpoint: {
|
|
215
|
+
var checkpoint = Checkpoint.init(.{
|
|
216
|
+
.superblock_checkpoint = null,
|
|
217
|
+
.client_replies = null,
|
|
218
|
+
.grid = null,
|
|
219
|
+
});
|
|
220
|
+
if (areas.contains(.superblock_checkpoint)) {
|
|
221
|
+
checkpoint.put(
|
|
222
|
+
.superblock_checkpoint,
|
|
223
|
+
vsr.checksum(std.mem.asBytes(&superblock.working.vsr_state.checkpoint)),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (areas.contains(.client_replies)) {
|
|
227
|
+
checkpoint.put(.client_replies, checker.checksum_client_replies(superblock));
|
|
228
|
+
}
|
|
229
|
+
if (areas.contains(.grid)) {
|
|
230
|
+
checkpoint.put(.grid, checker.checksum_grid(Forest, forest, .free_set_from_disk));
|
|
231
|
+
}
|
|
232
|
+
break :checkpoint checkpoint;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
for (std.enums.values(CheckpointArea)) |area| {
|
|
236
|
+
log.debug("{}: {s}: checkpoint={} area={s} value={?x:0>32}", .{
|
|
237
|
+
superblock.replica_index.?,
|
|
238
|
+
caller,
|
|
239
|
+
op_checkpoint,
|
|
240
|
+
@tagName(area),
|
|
241
|
+
checkpoint_actual.get(area),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (checker.checkpoints.getPtr(op_checkpoint)) |checkpoint_expect| {
|
|
246
|
+
var mismatch: bool = false;
|
|
247
|
+
for (std.enums.values(CheckpointArea)) |area| {
|
|
248
|
+
const checksum_actual = checkpoint_actual.get(area) orelse continue;
|
|
249
|
+
if (checkpoint_expect.fetchPut(area, checksum_actual)) |checksum_expect| {
|
|
250
|
+
if (checksum_expect != checksum_actual) {
|
|
251
|
+
log.warn("{}: {s}: mismatch " ++
|
|
252
|
+
"area={s} expect={x:0>32} actual={x:0>32}", .{
|
|
253
|
+
superblock.replica_index.?,
|
|
254
|
+
caller,
|
|
255
|
+
@tagName(area),
|
|
256
|
+
checksum_expect,
|
|
257
|
+
checksum_actual,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
mismatch = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (mismatch) return error.StorageMismatch;
|
|
265
|
+
} else {
|
|
266
|
+
// This replica is the first to reach op_checkpoint.
|
|
267
|
+
// Save its state for other replicas to check themselves against.
|
|
268
|
+
try checker.checkpoints.putNoClobber(op_checkpoint, checkpoint_actual);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
fn checksum_client_replies(checker: *StorageChecker, superblock: *const SuperBlock) u128 {
|
|
273
|
+
assert(superblock.working.vsr_state.sync_op_max == 0);
|
|
274
|
+
|
|
275
|
+
const client_sessions_size = superblock.working.vsr_state.checkpoint.client_sessions_size;
|
|
276
|
+
if (client_sessions_size > 0) {
|
|
277
|
+
const checkpoint = &superblock.working.vsr_state.checkpoint;
|
|
278
|
+
var client_sessions_block: vsr.BlockReference = .{
|
|
279
|
+
.address = checkpoint.client_sessions_last_block_address,
|
|
280
|
+
.checksum = checkpoint.client_sessions_last_block_checksum,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
var client_sessions_cursor: usize = client_sessions_size;
|
|
284
|
+
while (true) {
|
|
285
|
+
const block =
|
|
286
|
+
superblock.storage.grid_block(client_sessions_block.address).?;
|
|
287
|
+
assert(schema.header_from_block(block).checksum == client_sessions_block.checksum);
|
|
288
|
+
|
|
289
|
+
const block_body = schema.TrailerNode.body(block);
|
|
290
|
+
client_sessions_cursor -= block_body.len;
|
|
291
|
+
stdx.copy_disjoint(
|
|
292
|
+
.inexact,
|
|
293
|
+
u8,
|
|
294
|
+
checker.client_sessions_buffer[client_sessions_cursor..],
|
|
295
|
+
block_body,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
client_sessions_block = schema.TrailerNode.previous(block) orelse break;
|
|
299
|
+
}
|
|
300
|
+
assert(client_sessions_cursor == 0);
|
|
301
|
+
}
|
|
302
|
+
assert(vsr.checksum(checker.client_sessions_buffer[0..client_sessions_size]) ==
|
|
303
|
+
superblock.working.vsr_state.checkpoint.client_sessions_checksum);
|
|
304
|
+
|
|
305
|
+
checker.client_sessions.decode(checker.client_sessions_buffer[0..client_sessions_size]);
|
|
306
|
+
defer checker.client_sessions.reset();
|
|
307
|
+
|
|
308
|
+
var checksum = vsr.ChecksumStream.init();
|
|
309
|
+
for (checker.client_sessions.entries, 0..) |client_session, slot| {
|
|
310
|
+
if (client_session.session == 0) {
|
|
311
|
+
// Empty slot.
|
|
312
|
+
} else {
|
|
313
|
+
assert(client_session.header.command == .reply);
|
|
314
|
+
|
|
315
|
+
assert(client_session.header.size >= @sizeOf(vsr.Header));
|
|
316
|
+
if (client_session.header.size == @sizeOf(vsr.Header)) {
|
|
317
|
+
// ClientReplies won't store this entry.
|
|
318
|
+
} else {
|
|
319
|
+
const reply = superblock.storage.area_memory(
|
|
320
|
+
.{ .client_replies = .{ .slot = slot } },
|
|
321
|
+
)[0..vsr.sector_ceil(client_session.header.size)];
|
|
322
|
+
|
|
323
|
+
const reply_header =
|
|
324
|
+
std.mem.bytesAsValue(vsr.Header, reply[0..@sizeOf(vsr.Header)]);
|
|
325
|
+
|
|
326
|
+
assert(reply_header.checksum == client_session.header.checksum);
|
|
327
|
+
checksum.add(reply);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return checksum.checksum();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
fn read_free_set_bitset(
|
|
335
|
+
checker: *StorageChecker,
|
|
336
|
+
superblock: *const SuperBlock,
|
|
337
|
+
bitset: vsr.FreeSet.BitsetKind,
|
|
338
|
+
) void {
|
|
339
|
+
const free_set_reference = superblock.working.free_set_reference(bitset);
|
|
340
|
+
|
|
341
|
+
const free_set_buffer: []align(@alignOf(u64)) u8 = switch (bitset) {
|
|
342
|
+
.blocks_acquired => checker.free_set_blocks_acquired_encoded,
|
|
343
|
+
.blocks_released => checker.free_set_blocks_released_encoded,
|
|
344
|
+
};
|
|
345
|
+
const free_set_size = free_set_reference.trailer_size;
|
|
346
|
+
const free_set_checksum = free_set_reference.checksum;
|
|
347
|
+
|
|
348
|
+
if (free_set_size > 0) {
|
|
349
|
+
// Read free set from the grid by manually following the linked list of blocks.
|
|
350
|
+
// Note that free set is written in direct order, and must be read backwards.
|
|
351
|
+
var free_set_block: ?vsr.BlockReference = .{
|
|
352
|
+
.address = free_set_reference.last_block_address,
|
|
353
|
+
.checksum = free_set_reference.last_block_checksum,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const free_set_block_count =
|
|
357
|
+
stdx.div_ceil(free_set_size, constants.block_size - @sizeOf(vsr.Header));
|
|
358
|
+
|
|
359
|
+
var free_set_cursor: usize = free_set_size;
|
|
360
|
+
for (0..free_set_block_count) |_| {
|
|
361
|
+
const block = superblock.storage.grid_block(free_set_block.?.address).?;
|
|
362
|
+
assert(schema.header_from_block(block).checksum == free_set_block.?.checksum);
|
|
363
|
+
|
|
364
|
+
const encoded_words = schema.TrailerNode.body(block);
|
|
365
|
+
free_set_cursor -= encoded_words.len;
|
|
366
|
+
stdx.copy_disjoint(
|
|
367
|
+
.inexact,
|
|
368
|
+
u8,
|
|
369
|
+
free_set_buffer[free_set_cursor..],
|
|
370
|
+
encoded_words,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
free_set_block = schema.TrailerNode.previous(block);
|
|
374
|
+
}
|
|
375
|
+
assert(free_set_block == null);
|
|
376
|
+
assert(free_set_cursor == 0);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
assert(vsr.checksum(free_set_buffer[0..free_set_size]) == free_set_checksum);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
fn checksum_grid(
|
|
383
|
+
checker: *StorageChecker,
|
|
384
|
+
comptime Forest: type,
|
|
385
|
+
forest: *const Forest,
|
|
386
|
+
source: enum { free_set_from_disk, free_set_from_memory },
|
|
387
|
+
) u128 {
|
|
388
|
+
const superblock: *const SuperBlock = forest.grid.superblock;
|
|
389
|
+
const manifest_log = &forest.manifest_log;
|
|
390
|
+
const free_set = switch (source) {
|
|
391
|
+
.free_set_from_memory => forest.grid.free_set,
|
|
392
|
+
.free_set_from_disk => blk: {
|
|
393
|
+
checker.read_free_set_bitset(superblock, .blocks_acquired);
|
|
394
|
+
checker.read_free_set_bitset(superblock, .blocks_released);
|
|
395
|
+
const free_set_blocks_acquired_size =
|
|
396
|
+
superblock.working.free_set_reference(.blocks_acquired).trailer_size;
|
|
397
|
+
const free_set_blocks_released_size =
|
|
398
|
+
superblock.working.free_set_reference(.blocks_released).trailer_size;
|
|
399
|
+
checker.free_set.decode_chunks(
|
|
400
|
+
&.{checker.free_set_blocks_acquired_encoded[0..free_set_blocks_acquired_size]},
|
|
401
|
+
&.{checker.free_set_blocks_released_encoded[0..free_set_blocks_released_size]},
|
|
402
|
+
);
|
|
403
|
+
checker.free_set.opened = true;
|
|
404
|
+
break :blk checker.free_set;
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
defer checker.free_set.reset();
|
|
408
|
+
|
|
409
|
+
var blocks_acquired = free_set.blocks_acquired.iterator(.{});
|
|
410
|
+
var blocks_missing: usize = 0;
|
|
411
|
+
|
|
412
|
+
var stream = vsr.ChecksumStream.init();
|
|
413
|
+
|
|
414
|
+
while (blocks_acquired.next()) |block_address_index| {
|
|
415
|
+
const block_address: u64 = block_address_index + 1;
|
|
416
|
+
|
|
417
|
+
// Calculate the checksum over acquired, unreleased blocks, as the state of released
|
|
418
|
+
// blocks is uncertain during state sync. State sync involves syncing the FreeSet
|
|
419
|
+
// encoded in a replica's superblock at checkpoint, and the current grid state, both of
|
|
420
|
+
// which may not be in sync. Blocks marked released in the FreeSet encoded in the
|
|
421
|
+
// superblock are freed at checkpoint durability, and may be overwritten.
|
|
422
|
+
if (free_set.is_released(block_address)) continue;
|
|
423
|
+
|
|
424
|
+
// The StorageChecker must skip checking open ManifestLog blocks, these have not been
|
|
425
|
+
// flushed yet – until they are written, their content in the grid is undefined.
|
|
426
|
+
var manifest_log_open_blocks = manifest_log.blocks.iterator();
|
|
427
|
+
while (manifest_log_open_blocks.next()) |open_block| {
|
|
428
|
+
const open_block_header =
|
|
429
|
+
std.mem.bytesAsValue(vsr.Header.Block, open_block[0..@sizeOf(vsr.Header)]);
|
|
430
|
+
assert(open_block_header.address > 0);
|
|
431
|
+
if (block_address == open_block_header.address) break;
|
|
432
|
+
} else {
|
|
433
|
+
const block = superblock.storage.grid_block(block_address) orelse {
|
|
434
|
+
log.err("{}: checksum_grid: missing block_address={}", .{
|
|
435
|
+
superblock.replica_index.?,
|
|
436
|
+
block_address,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
blocks_missing += 1;
|
|
440
|
+
continue;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const block_header = schema.header_from_block(block);
|
|
444
|
+
assert(block_header.address == block_address);
|
|
445
|
+
|
|
446
|
+
stream.add(block[0..block_header.size]);
|
|
447
|
+
// Extra guard against identical blocks:
|
|
448
|
+
stream.add(std.mem.asBytes(&block_address));
|
|
449
|
+
|
|
450
|
+
// Grid block sector padding is zeroed:
|
|
451
|
+
assert(stdx.zeroed(block[block_header.size..vsr.sector_ceil(block_header.size)]));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
assert(blocks_missing == 0);
|
|
455
|
+
|
|
456
|
+
return stream.checksum();
|
|
457
|
+
}
|
|
458
|
+
};
|