tigerbeetle 0.0.40 → 0.17.8
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/LICENSE +0 -25
- data/README.md +670 -80
- data/docs/migration.md +201 -0
- data/sig/tigerbeetle.rbs +271 -0
- data/src/ext/tigerbeetle/extconf.rb +47 -0
- data/src/ext/tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
- data/src/ext/tigerbeetle/lib/aarch64-linux-musl/libtb_client.so +0 -0
- data/src/ext/tigerbeetle/lib/aarch64-macos/libtb_client.dylib +0 -0
- data/src/ext/tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so +0 -0
- data/src/ext/tigerbeetle/lib/x86_64-linux-musl/libtb_client.so +0 -0
- data/src/ext/tigerbeetle/lib/x86_64-macos/libtb_client.dylib +0 -0
- data/src/ext/tigerbeetle/lib/x86_64-windows/tb_client.dll +0 -0
- data/src/ext/tigerbeetle/rb_tb_gen.h +458 -0
- data/{ext/tb_client/tigerbeetle/src/clients/rust/assets → src/ext/tigerbeetle}/tb_client.h +18 -16
- data/src/ext/tigerbeetle/tigerbeetle.c +310 -0
- data/src/tigerbeetle/bindings.rb +347 -0
- data/src/tigerbeetle/client.rb +129 -0
- data/src/tigerbeetle/completion_dispatcher.rb +108 -0
- data/src/tigerbeetle/id.rb +40 -0
- data/src/tigerbeetle/tb.rb +3 -0
- data/src/tigerbeetle/version.rb +3 -0
- data/src/tigerbeetle.rb +39 -0
- metadata +33 -350
- data/CHANGELOG.md +0 -162
- data/ext/tb_client/extconf.rb +0 -41
- data/ext/tb_client/tigerbeetle/LICENSE +0 -177
- data/ext/tb_client/tigerbeetle/build.zig +0 -2296
- data/ext/tb_client/tigerbeetle/src/aof.zig +0 -1000
- data/ext/tb_client/tigerbeetle/src/build/fetch.zig +0 -112
- data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +0 -808
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +0 -1283
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +0 -1704
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +0 -341
- data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +0 -1450
- data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +0 -1659
- data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +0 -406
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +0 -1092
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +0 -286
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +0 -158
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +0 -229
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +0 -110
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +0 -386
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +0 -34
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +0 -281
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +0 -312
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +0 -138
- data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +0 -466
- data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +0 -157
- data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +0 -90
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +0 -203
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +0 -79
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +0 -542
- data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +0 -109
- data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +0 -86
- data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +0 -370
- data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +0 -386
- data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +0 -167
- data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +0 -126
- data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +0 -996
- data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +0 -748
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +0 -3238
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +0 -1718
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +0 -190
- data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +0 -104
- data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +0 -75
- data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +0 -522
- data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +0 -267
- data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +0 -3
- data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +0 -379
- data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +0 -131
- data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +0 -63
- data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +0 -588
- data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +0 -73
- data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +0 -106
- data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +0 -305
- data/ext/tb_client/tigerbeetle/src/config.zig +0 -296
- data/ext/tb_client/tigerbeetle/src/constants.zig +0 -790
- data/ext/tb_client/tigerbeetle/src/copyhound.zig +0 -202
- data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +0 -72
- data/ext/tb_client/tigerbeetle/src/direction.zig +0 -120
- data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +0 -158
- data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +0 -156
- data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +0 -252
- data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +0 -313
- data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +0 -87
- data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +0 -63
- data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +0 -47
- data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +0 -28
- data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +0 -61
- data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +0 -169
- data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +0 -46
- data/ext/tb_client/tigerbeetle/src/ewah.zig +0 -445
- data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +0 -128
- data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +0 -171
- data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +0 -179
- data/ext/tb_client/tigerbeetle/src/integration_tests.zig +0 -662
- data/ext/tb_client/tigerbeetle/src/io/common.zig +0 -155
- data/ext/tb_client/tigerbeetle/src/io/darwin.zig +0 -1093
- data/ext/tb_client/tigerbeetle/src/io/linux.zig +0 -1880
- data/ext/tb_client/tigerbeetle/src/io/test.zig +0 -1005
- data/ext/tb_client/tigerbeetle/src/io/windows.zig +0 -1598
- data/ext/tb_client/tigerbeetle/src/io.zig +0 -34
- data/ext/tb_client/tigerbeetle/src/iops.zig +0 -134
- data/ext/tb_client/tigerbeetle/src/list.zig +0 -236
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +0 -848
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +0 -179
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +0 -424
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +0 -420
- data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +0 -2114
- data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +0 -185
- data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +0 -1146
- data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +0 -1102
- data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +0 -200
- data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +0 -1495
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +0 -739
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +0 -166
- data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +0 -754
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +0 -1294
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +0 -510
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +0 -1241
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -628
- data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +0 -247
- data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +0 -116
- data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +0 -543
- data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +0 -938
- data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +0 -293
- data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +0 -359
- data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +0 -99
- data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +0 -17
- data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +0 -962
- data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +0 -617
- data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +0 -84
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +0 -1500
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -149
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -7
- data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +0 -865
- data/ext/tb_client/tigerbeetle/src/lsm/table.zig +0 -607
- data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +0 -843
- data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +0 -90
- data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +0 -40
- data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +0 -629
- data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +0 -933
- data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +0 -534
- data/ext/tb_client/tigerbeetle/src/message_buffer.zig +0 -469
- data/ext/tb_client/tigerbeetle/src/message_bus.zig +0 -1219
- data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +0 -936
- data/ext/tb_client/tigerbeetle/src/message_pool.zig +0 -343
- data/ext/tb_client/tigerbeetle/src/multiversion.zig +0 -2195
- data/ext/tb_client/tigerbeetle/src/queue.zig +0 -390
- data/ext/tb_client/tigerbeetle/src/repl/completion.zig +0 -201
- data/ext/tb_client/tigerbeetle/src/repl/parser.zig +0 -1356
- data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +0 -496
- data/ext/tb_client/tigerbeetle/src/repl.zig +0 -1034
- data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +0 -973
- data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +0 -1866
- data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +0 -304
- data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +0 -227
- data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +0 -658
- data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +0 -466
- data/ext/tb_client/tigerbeetle/src/scripts/release.zig +0 -1058
- data/ext/tb_client/tigerbeetle/src/scripts.zig +0 -105
- data/ext/tb_client/tigerbeetle/src/shell.zig +0 -1195
- data/ext/tb_client/tigerbeetle/src/stack.zig +0 -260
- data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +0 -911
- data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +0 -2079
- data/ext/tb_client/tigerbeetle/src/state_machine.zig +0 -4872
- data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +0 -288
- data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +0 -3128
- data/ext/tb_client/tigerbeetle/src/static_allocator.zig +0 -82
- data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +0 -157
- data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +0 -292
- data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +0 -65
- data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +0 -1414
- data/ext/tb_client/tigerbeetle/src/stdx/huge_page_allocator.zig +0 -115
- data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +0 -92
- data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +0 -677
- data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +0 -336
- data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +0 -511
- data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +0 -112
- data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +0 -1163
- data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +0 -142
- data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +0 -361
- data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +0 -275
- data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +0 -295
- data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +0 -436
- data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +0 -48
- data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +0 -402
- data/ext/tb_client/tigerbeetle/src/storage.zig +0 -489
- data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +0 -180
- data/ext/tb_client/tigerbeetle/src/testing/bench.zig +0 -146
- data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +0 -53
- data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +0 -61
- data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +0 -76
- data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +0 -110
- data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +0 -412
- data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +0 -331
- data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -458
- data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +0 -1198
- data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +0 -128
- data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +0 -181
- data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +0 -144
- data/ext/tb_client/tigerbeetle/src/testing/id.zig +0 -97
- data/ext/tb_client/tigerbeetle/src/testing/io.zig +0 -317
- data/ext/tb_client/tigerbeetle/src/testing/marks.zig +0 -126
- data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +0 -533
- data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +0 -154
- data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +0 -389
- data/ext/tb_client/tigerbeetle/src/testing/storage.zig +0 -1247
- data/ext/tb_client/tigerbeetle/src/testing/table.zig +0 -249
- data/ext/tb_client/tigerbeetle/src/testing/time.zig +0 -98
- data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +0 -212
- data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +0 -26
- data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +0 -579
- data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +0 -39
- data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +0 -214
- data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +0 -34
- data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +0 -785
- data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +0 -543
- data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +0 -181
- data/ext/tb_client/tigerbeetle/src/tidy.zig +0 -1449
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +0 -227
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +0 -1069
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +0 -1422
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +0 -1658
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +0 -518
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +0 -36
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +0 -646
- data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +0 -958
- data/ext/tb_client/tigerbeetle/src/time.zig +0 -236
- data/ext/tb_client/tigerbeetle/src/trace/event.zig +0 -745
- data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +0 -462
- data/ext/tb_client/tigerbeetle/src/trace.zig +0 -556
- data/ext/tb_client/tigerbeetle/src/unit_tests.zig +0 -321
- data/ext/tb_client/tigerbeetle/src/vopr.zig +0 -1785
- data/ext/tb_client/tigerbeetle/src/vortex.zig +0 -101
- data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +0 -473
- data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +0 -208
- data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +0 -43
- data/ext/tb_client/tigerbeetle/src/vsr/client.zig +0 -768
- data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +0 -532
- data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +0 -338
- data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +0 -1019
- data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +0 -279
- data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +0 -1381
- data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +0 -315
- data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +0 -1460
- data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +0 -757
- data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +0 -797
- data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +0 -2586
- data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +0 -308
- data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +0 -1777
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +0 -715
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +0 -185
- data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +0 -333
- data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +0 -12356
- data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +0 -416
- data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +0 -165
- data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +0 -2928
- data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +0 -1075
- data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +0 -1603
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -484
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +0 -405
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -355
- data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +0 -29
- data/ext/tb_client/tigerbeetle/src/vsr.zig +0 -1727
- data/lib/tb_client/shared_lib.rb +0 -66
- data/lib/tb_client.rb +0 -282
- data/lib/tigerbeetle/account.rb +0 -38
- data/lib/tigerbeetle/account_balance.rb +0 -23
- data/lib/tigerbeetle/account_filter.rb +0 -31
- data/lib/tigerbeetle/atomic_counter.rb +0 -14
- data/lib/tigerbeetle/client.rb +0 -214
- data/lib/tigerbeetle/converters/account.rb +0 -63
- data/lib/tigerbeetle/converters/account_balance.rb +0 -31
- data/lib/tigerbeetle/converters/account_filter.rb +0 -32
- data/lib/tigerbeetle/converters/base.rb +0 -35
- data/lib/tigerbeetle/converters/create_accounts_result.rb +0 -21
- data/lib/tigerbeetle/converters/create_transfers_result.rb +0 -21
- data/lib/tigerbeetle/converters/query_filter.rb +0 -33
- data/lib/tigerbeetle/converters/time.rb +0 -23
- data/lib/tigerbeetle/converters/transfer.rb +0 -64
- data/lib/tigerbeetle/converters/uint_128.rb +0 -24
- data/lib/tigerbeetle/converters.rb +0 -12
- data/lib/tigerbeetle/error.rb +0 -4
- data/lib/tigerbeetle/id.rb +0 -30
- data/lib/tigerbeetle/platforms.rb +0 -9
- data/lib/tigerbeetle/query_filter.rb +0 -31
- data/lib/tigerbeetle/request.rb +0 -7
- data/lib/tigerbeetle/transfer.rb +0 -40
- data/lib/tigerbeetle/version.rb +0 -4
- data/lib/tigerbeetle.rb +0 -13
- data/tigerbeetle.gemspec +0 -60
|
@@ -1,1102 +0,0 @@
|
|
|
1
|
-
// TODO Test scope_open/scope_close.
|
|
2
|
-
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
const assert = std.debug.assert;
|
|
5
|
-
|
|
6
|
-
const constants = @import("../constants.zig");
|
|
7
|
-
const fixtures = @import("../testing/fixtures.zig");
|
|
8
|
-
const fuzz = @import("../testing/fuzz.zig");
|
|
9
|
-
const stdx = @import("stdx");
|
|
10
|
-
const vsr = @import("../vsr.zig");
|
|
11
|
-
|
|
12
|
-
const log = std.log.scoped(.lsm_forest_fuzz);
|
|
13
|
-
const tb = @import("../tigerbeetle.zig");
|
|
14
|
-
|
|
15
|
-
const TimeSim = @import("../testing/time.zig").TimeSim;
|
|
16
|
-
const Account = @import("../tigerbeetle.zig").Account;
|
|
17
|
-
const Storage = @import("../testing/storage.zig").Storage;
|
|
18
|
-
const StateMachine = @import("../state_machine.zig").StateMachineType(Storage);
|
|
19
|
-
const Reservation = @import("../vsr/free_set.zig").Reservation;
|
|
20
|
-
const GridType = @import("../vsr/grid.zig").GridType;
|
|
21
|
-
const ScanRangeType = @import("../lsm/scan_range.zig").ScanRangeType;
|
|
22
|
-
const EvaluateNext = @import("../lsm/scan_range.zig").EvaluateNext;
|
|
23
|
-
const ScanLookupType = @import("../lsm/scan_lookup.zig").ScanLookupType;
|
|
24
|
-
const TimestampRange = @import("timestamp_range.zig").TimestampRange;
|
|
25
|
-
const Direction = @import("../direction.zig").Direction;
|
|
26
|
-
const Forest = StateMachine.Forest;
|
|
27
|
-
|
|
28
|
-
const Grid = GridType(Storage);
|
|
29
|
-
const SuperBlock = vsr.SuperBlockType(Storage);
|
|
30
|
-
|
|
31
|
-
const FuzzOpAction = union(enum) {
|
|
32
|
-
compact: struct {
|
|
33
|
-
checkpoint: bool,
|
|
34
|
-
checkpoint_durable: bool,
|
|
35
|
-
},
|
|
36
|
-
put_account: Account,
|
|
37
|
-
get_account: u128,
|
|
38
|
-
exists_account: u64,
|
|
39
|
-
scan_account: ScanParams,
|
|
40
|
-
};
|
|
41
|
-
const FuzzOpActionTag = std.meta.Tag(FuzzOpAction);
|
|
42
|
-
|
|
43
|
-
const FuzzOpModifier = union(enum) {
|
|
44
|
-
normal,
|
|
45
|
-
crash_after_ticks: usize,
|
|
46
|
-
};
|
|
47
|
-
const FuzzOpModifierTag = std.meta.Tag(FuzzOpModifier);
|
|
48
|
-
|
|
49
|
-
const FuzzOp = struct {
|
|
50
|
-
op: u64,
|
|
51
|
-
action: FuzzOpAction,
|
|
52
|
-
modifier: FuzzOpModifier,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const GrooveAccounts = type: {
|
|
56
|
-
const forest: Forest = undefined;
|
|
57
|
-
break :type @TypeOf(forest.grooves.accounts);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const ScanParams = struct {
|
|
61
|
-
index: std.meta.FieldEnum(GrooveAccounts.IndexTrees),
|
|
62
|
-
min: u128, // Type-erased field min.
|
|
63
|
-
max: u128, // Type-erased field max.
|
|
64
|
-
direction: Direction,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const Environment = struct {
|
|
68
|
-
const node_count = 1024;
|
|
69
|
-
// This is the smallest size that set_associative_cache will allow us.
|
|
70
|
-
const cache_entries_max = GrooveAccounts.ObjectsCache.Cache.value_count_max_multiple;
|
|
71
|
-
const forest_options = StateMachine.forest_options(.{
|
|
72
|
-
.batch_size_limit = constants.message_body_size_max,
|
|
73
|
-
.lsm_forest_compaction_block_count = Forest.Options.compaction_block_count_min,
|
|
74
|
-
.lsm_forest_node_count = node_count,
|
|
75
|
-
.cache_entries_accounts = cache_entries_max,
|
|
76
|
-
.cache_entries_transfers = cache_entries_max,
|
|
77
|
-
.cache_entries_transfers_pending = cache_entries_max,
|
|
78
|
-
.log_trace = true,
|
|
79
|
-
.aof_recovery = false,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const free_set_fragments_max = 2048;
|
|
83
|
-
const free_set_fragment_size = 67;
|
|
84
|
-
|
|
85
|
-
// We must call compact after every 'batch'.
|
|
86
|
-
// Every `lsm_compaction_ops` batches may put/remove `value_count_max` values per index.
|
|
87
|
-
// Every `FuzzOp.put_account` issues one remove and one put per index.
|
|
88
|
-
const puts_since_compact_max = @divTrunc(
|
|
89
|
-
Forest.groove_config.accounts.ObjectTree.Table.value_count_max,
|
|
90
|
-
2 * constants.lsm_compaction_ops,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const State = enum {
|
|
94
|
-
init,
|
|
95
|
-
forest_init,
|
|
96
|
-
forest_open,
|
|
97
|
-
fuzzing,
|
|
98
|
-
forest_compact,
|
|
99
|
-
grid_checkpoint,
|
|
100
|
-
forest_checkpoint,
|
|
101
|
-
superblock_checkpoint,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
state: State,
|
|
105
|
-
storage: *Storage,
|
|
106
|
-
time_sim: TimeSim,
|
|
107
|
-
trace: Storage.Tracer,
|
|
108
|
-
superblock: SuperBlock,
|
|
109
|
-
superblock_context: SuperBlock.Context,
|
|
110
|
-
grid: Grid,
|
|
111
|
-
forest: Forest,
|
|
112
|
-
checkpoint_op: ?u64,
|
|
113
|
-
ticks_remaining: usize,
|
|
114
|
-
scan_lookup_buffer: []tb.Account,
|
|
115
|
-
|
|
116
|
-
fn init(env: *Environment, gpa: std.mem.Allocator, storage: *Storage) !void {
|
|
117
|
-
env.storage = storage;
|
|
118
|
-
|
|
119
|
-
env.time_sim = fixtures.init_time(.{});
|
|
120
|
-
env.trace = try fixtures.init_tracer(gpa, env.time_sim.time(), .{});
|
|
121
|
-
|
|
122
|
-
env.superblock = try fixtures.init_superblock(gpa, env.storage, .{});
|
|
123
|
-
|
|
124
|
-
env.grid = try fixtures.init_grid(gpa, &env.trace, &env.superblock, .{
|
|
125
|
-
.blocks_released_prior_checkpoint_durability_max = Forest
|
|
126
|
-
.compaction_blocks_released_per_pipeline_max(),
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
env.scan_lookup_buffer = try gpa.alloc(
|
|
130
|
-
tb.Account,
|
|
131
|
-
StateMachine.batch_max.create_accounts,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
env.forest = undefined;
|
|
135
|
-
env.checkpoint_op = null;
|
|
136
|
-
env.ticks_remaining = std.math.maxInt(usize);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
fn deinit(env: *Environment, gpa: std.mem.Allocator) void {
|
|
140
|
-
env.superblock.deinit(gpa);
|
|
141
|
-
env.grid.deinit(gpa);
|
|
142
|
-
env.trace.deinit(gpa);
|
|
143
|
-
gpa.free(env.scan_lookup_buffer);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
pub fn run(gpa: std.mem.Allocator, storage: *Storage, fuzz_ops: []const FuzzOp) !void {
|
|
147
|
-
var env: Environment = undefined;
|
|
148
|
-
env.state = .init;
|
|
149
|
-
try env.init(gpa, storage);
|
|
150
|
-
defer env.deinit(gpa);
|
|
151
|
-
|
|
152
|
-
try env.open(gpa);
|
|
153
|
-
defer env.close(gpa);
|
|
154
|
-
|
|
155
|
-
try env.apply(gpa, fuzz_ops);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
fn change_state(env: *Environment, current_state: State, next_state: State) void {
|
|
159
|
-
assert(env.state == current_state);
|
|
160
|
-
env.state = next_state;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
fn tick_until_state_change(env: *Environment, current_state: State, next_state: State) !void {
|
|
164
|
-
while (true) {
|
|
165
|
-
if (env.state != current_state) break;
|
|
166
|
-
|
|
167
|
-
if (env.ticks_remaining == 0) return error.OutOfTicks;
|
|
168
|
-
env.ticks_remaining -= 1;
|
|
169
|
-
env.storage.run();
|
|
170
|
-
}
|
|
171
|
-
assert(env.state == next_state);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
fn open(env: *Environment, gpa: std.mem.Allocator) !void {
|
|
175
|
-
fixtures.open_superblock(&env.superblock);
|
|
176
|
-
fixtures.open_grid(&env.grid);
|
|
177
|
-
|
|
178
|
-
env.change_state(.init, .forest_init);
|
|
179
|
-
try env.forest.init(gpa, &env.grid, .{
|
|
180
|
-
// TODO Test that the same sequence of events applied to forests with different
|
|
181
|
-
// compaction_blocks result in identical grids.
|
|
182
|
-
.compaction_block_count = Forest.Options.compaction_block_count_min,
|
|
183
|
-
.node_count = node_count,
|
|
184
|
-
}, forest_options);
|
|
185
|
-
env.change_state(.forest_init, .forest_open);
|
|
186
|
-
env.forest.open(forest_open_callback);
|
|
187
|
-
|
|
188
|
-
try env.tick_until_state_change(.forest_open, .fuzzing);
|
|
189
|
-
|
|
190
|
-
if (env.grid.free_set.count_acquired() == 0) {
|
|
191
|
-
// Only run this once, to avoid acquiring an ever-increasing number of (never
|
|
192
|
-
// to-be-released) blocks on every restart.
|
|
193
|
-
env.fragmentate_free_set();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/// Allocate a sparse subset of grid blocks to make sure that the encoded free set needs more
|
|
198
|
-
/// than one block to exercise the block linked list logic from CheckpointTrailer.
|
|
199
|
-
fn fragmentate_free_set(env: *Environment) void {
|
|
200
|
-
assert(env.grid.free_set.count_acquired() == 0);
|
|
201
|
-
assert(free_set_fragments_max * free_set_fragment_size <= env.grid.free_set.count_free());
|
|
202
|
-
|
|
203
|
-
var reservations: [free_set_fragments_max]Reservation = undefined;
|
|
204
|
-
for (&reservations) |*reservation| {
|
|
205
|
-
reservation.* = env.grid.reserve(free_set_fragment_size);
|
|
206
|
-
}
|
|
207
|
-
for (reservations) |reservation| {
|
|
208
|
-
_ = env.grid.free_set.acquire(reservation).?;
|
|
209
|
-
}
|
|
210
|
-
for (reservations) |reservation| {
|
|
211
|
-
env.grid.free_set.forfeit(reservation);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
fn close(env: *Environment, gpa: std.mem.Allocator) void {
|
|
216
|
-
env.forest.deinit(gpa);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
fn forest_open_callback(forest: *Forest) void {
|
|
220
|
-
const env: *Environment = @fieldParentPtr("forest", forest);
|
|
221
|
-
env.change_state(.forest_open, .fuzzing);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
pub fn compact(env: *Environment, op: u64) !void {
|
|
225
|
-
env.change_state(.fuzzing, .forest_compact);
|
|
226
|
-
env.forest.compact(forest_compact_callback, op);
|
|
227
|
-
try env.tick_until_state_change(.forest_compact, .fuzzing);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
fn forest_compact_callback(forest: *Forest) void {
|
|
231
|
-
const env: *Environment = @fieldParentPtr("forest", forest);
|
|
232
|
-
env.change_state(.forest_compact, .fuzzing);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
pub fn checkpoint(env: *Environment, op: u64) !void {
|
|
236
|
-
assert(env.checkpoint_op == null);
|
|
237
|
-
env.checkpoint_op = op - constants.lsm_compaction_ops;
|
|
238
|
-
|
|
239
|
-
env.change_state(.fuzzing, .forest_checkpoint);
|
|
240
|
-
env.forest.checkpoint(forest_checkpoint_callback);
|
|
241
|
-
try env.tick_until_state_change(.forest_checkpoint, .grid_checkpoint);
|
|
242
|
-
|
|
243
|
-
env.grid.checkpoint(grid_checkpoint_callback);
|
|
244
|
-
try env.tick_until_state_change(.grid_checkpoint, .superblock_checkpoint);
|
|
245
|
-
|
|
246
|
-
env.superblock.checkpoint(superblock_checkpoint_callback, &env.superblock_context, .{
|
|
247
|
-
.header = header: {
|
|
248
|
-
var header = vsr.Header.Prepare.root(fixtures.cluster);
|
|
249
|
-
header.op = env.checkpoint_op.?;
|
|
250
|
-
header.set_checksum();
|
|
251
|
-
break :header header;
|
|
252
|
-
},
|
|
253
|
-
.view_attributes = null,
|
|
254
|
-
.manifest_references = env.forest.manifest_log.checkpoint_references(),
|
|
255
|
-
.free_set_references = .{
|
|
256
|
-
.blocks_acquired = env.grid
|
|
257
|
-
.free_set_checkpoint_blocks_acquired.checkpoint_reference(),
|
|
258
|
-
.blocks_released = env.grid
|
|
259
|
-
.free_set_checkpoint_blocks_released.checkpoint_reference(),
|
|
260
|
-
},
|
|
261
|
-
.client_sessions_reference = .{
|
|
262
|
-
.last_block_checksum = 0,
|
|
263
|
-
.last_block_address = 0,
|
|
264
|
-
.trailer_size = 0,
|
|
265
|
-
.checksum = vsr.checksum(&.{}),
|
|
266
|
-
},
|
|
267
|
-
.commit_max = env.checkpoint_op.? + 1,
|
|
268
|
-
.sync_op_min = 0,
|
|
269
|
-
.sync_op_max = 0,
|
|
270
|
-
.storage_size = vsr.superblock.data_file_size_min +
|
|
271
|
-
(env.grid.free_set.highest_address_acquired() orelse 0) * constants.block_size,
|
|
272
|
-
.release = vsr.Release.minimum,
|
|
273
|
-
});
|
|
274
|
-
try env.tick_until_state_change(.superblock_checkpoint, .fuzzing);
|
|
275
|
-
|
|
276
|
-
env.grid.mark_checkpoint_not_durable();
|
|
277
|
-
env.checkpoint_op = null;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
fn grid_checkpoint_callback(grid: *Grid) void {
|
|
281
|
-
const env: *Environment = @fieldParentPtr("grid", grid);
|
|
282
|
-
assert(env.checkpoint_op != null);
|
|
283
|
-
env.change_state(.grid_checkpoint, .superblock_checkpoint);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
fn forest_checkpoint_callback(forest: *Forest) void {
|
|
287
|
-
const env: *Environment = @fieldParentPtr("forest", forest);
|
|
288
|
-
assert(env.checkpoint_op != null);
|
|
289
|
-
env.change_state(.forest_checkpoint, .grid_checkpoint);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
fn superblock_checkpoint_callback(superblock_context: *SuperBlock.Context) void {
|
|
293
|
-
const env: *Environment = @fieldParentPtr("superblock_context", superblock_context);
|
|
294
|
-
env.change_state(.superblock_checkpoint, .fuzzing);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
fn prefetch_account(env: *Environment, id: u128, snapshot: u64) !void {
|
|
298
|
-
const Context = struct {
|
|
299
|
-
_id: u128,
|
|
300
|
-
_snapshot: u64,
|
|
301
|
-
_groove_accounts: *GrooveAccounts,
|
|
302
|
-
|
|
303
|
-
finished: bool = false,
|
|
304
|
-
prefetch_context: GrooveAccounts.PrefetchContext = undefined,
|
|
305
|
-
|
|
306
|
-
fn prefetch_start(getter: *@This()) void {
|
|
307
|
-
const groove = getter._groove_accounts;
|
|
308
|
-
groove.prefetch_setup(getter._snapshot);
|
|
309
|
-
groove.prefetch_enqueue(getter._id);
|
|
310
|
-
groove.prefetch(@This().prefetch_callback, &getter.prefetch_context);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
fn prefetch_callback(prefetch_context: *GrooveAccounts.PrefetchContext) void {
|
|
314
|
-
const context: *@This() = @fieldParentPtr("prefetch_context", prefetch_context);
|
|
315
|
-
assert(!context.finished);
|
|
316
|
-
context.finished = true;
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
var context = Context{
|
|
321
|
-
._id = id,
|
|
322
|
-
._snapshot = snapshot,
|
|
323
|
-
._groove_accounts = &env.forest.grooves.accounts,
|
|
324
|
-
};
|
|
325
|
-
context.prefetch_start();
|
|
326
|
-
while (!context.finished) {
|
|
327
|
-
if (env.ticks_remaining == 0) return error.OutOfTicks;
|
|
328
|
-
env.ticks_remaining -= 1;
|
|
329
|
-
env.storage.run();
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
fn prefetch_exists_account(env: *Environment, timestamp: u64, snapshot: u64) !void {
|
|
334
|
-
const Context = struct {
|
|
335
|
-
_timestamp: u64,
|
|
336
|
-
_snapshot: u64,
|
|
337
|
-
_groove_accounts: *GrooveAccounts,
|
|
338
|
-
|
|
339
|
-
finished: bool = false,
|
|
340
|
-
prefetch_context: GrooveAccounts.PrefetchContext = undefined,
|
|
341
|
-
|
|
342
|
-
fn prefetch_start(getter: *@This()) void {
|
|
343
|
-
const groove = getter._groove_accounts;
|
|
344
|
-
groove.prefetch_setup(getter._snapshot);
|
|
345
|
-
groove.prefetch_enqueue_by_timestamp(getter._timestamp);
|
|
346
|
-
groove.prefetch(@This().prefetch_callback, &getter.prefetch_context);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
fn prefetch_callback(prefetch_context: *GrooveAccounts.PrefetchContext) void {
|
|
350
|
-
const context: *@This() = @fieldParentPtr("prefetch_context", prefetch_context);
|
|
351
|
-
assert(!context.finished);
|
|
352
|
-
context.finished = true;
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
var context = Context{
|
|
357
|
-
._snapshot = snapshot,
|
|
358
|
-
._timestamp = timestamp,
|
|
359
|
-
._groove_accounts = &env.forest.grooves.accounts,
|
|
360
|
-
};
|
|
361
|
-
context.prefetch_start();
|
|
362
|
-
while (!context.finished) {
|
|
363
|
-
if (env.ticks_remaining == 0) return error.OutOfTicks;
|
|
364
|
-
env.ticks_remaining -= 1;
|
|
365
|
-
env.storage.run();
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
fn put_account(env: *Environment, a: *const Account, maybe_old: ?Account) void {
|
|
370
|
-
if (maybe_old) |*old| {
|
|
371
|
-
env.forest.grooves.accounts.update(.{ .old = old, .new = a });
|
|
372
|
-
} else {
|
|
373
|
-
env.forest.grooves.accounts.insert(a);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
fn get_account(env: *Environment, id: u128) ?Account {
|
|
378
|
-
return switch (env.forest.grooves.accounts.get(id)) {
|
|
379
|
-
.found_object => |a| a,
|
|
380
|
-
.found_orphaned_id => unreachable,
|
|
381
|
-
.not_found => null,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
fn exists(env: *Environment, timestamp: u64) bool {
|
|
386
|
-
return env.forest.grooves.accounts.exists(timestamp);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
fn ScannerIndexType(comptime index: std.meta.FieldEnum(GrooveAccounts.IndexTrees)) type {
|
|
390
|
-
const Tree = @FieldType(GrooveAccounts.IndexTrees, @tagName(index));
|
|
391
|
-
const Value = Tree.Table.Value;
|
|
392
|
-
const Index = GrooveAccounts.IndexTreeFieldHelperType(@tagName(index)).Index;
|
|
393
|
-
|
|
394
|
-
const ScanRange = ScanRangeType(
|
|
395
|
-
Tree,
|
|
396
|
-
Storage,
|
|
397
|
-
void,
|
|
398
|
-
struct {
|
|
399
|
-
inline fn value_next(_: void, _: *const Value) EvaluateNext {
|
|
400
|
-
return .include_and_continue;
|
|
401
|
-
}
|
|
402
|
-
}.value_next,
|
|
403
|
-
struct {
|
|
404
|
-
inline fn timestamp_from_value(_: void, value: *const Value) u64 {
|
|
405
|
-
return value.timestamp;
|
|
406
|
-
}
|
|
407
|
-
}.timestamp_from_value,
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
const ScanLookup = ScanLookupType(
|
|
411
|
-
GrooveAccounts,
|
|
412
|
-
ScanRange,
|
|
413
|
-
Storage,
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
return struct {
|
|
417
|
-
const ScannerIndex = @This();
|
|
418
|
-
|
|
419
|
-
lookup: ScanLookup = undefined,
|
|
420
|
-
result: ?[]const tb.Account = null,
|
|
421
|
-
|
|
422
|
-
fn scan(
|
|
423
|
-
self: *ScannerIndex,
|
|
424
|
-
env: *Environment,
|
|
425
|
-
params: ScanParams,
|
|
426
|
-
snapshot: u64,
|
|
427
|
-
) ![]const tb.Account {
|
|
428
|
-
const min: Index, const max: Index = switch (Index) {
|
|
429
|
-
void => range: {
|
|
430
|
-
assert(params.min == 0);
|
|
431
|
-
assert(params.max == 0);
|
|
432
|
-
break :range .{ {}, {} };
|
|
433
|
-
},
|
|
434
|
-
else => range: {
|
|
435
|
-
const min: Index = @intCast(params.min);
|
|
436
|
-
const max: Index = @intCast(params.max);
|
|
437
|
-
assert(min <= max);
|
|
438
|
-
break :range .{ min, max };
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const scan_buffer_pool = &env.forest.scan_buffer_pool;
|
|
443
|
-
const groove_accounts = &env.forest.grooves.accounts;
|
|
444
|
-
defer scan_buffer_pool.reset();
|
|
445
|
-
|
|
446
|
-
// It's not expected to exceed `lsm_scans_max` here.
|
|
447
|
-
const scan_buffer = scan_buffer_pool.acquire() catch unreachable;
|
|
448
|
-
|
|
449
|
-
var scan_range = ScanRange.init(
|
|
450
|
-
{},
|
|
451
|
-
&@field(groove_accounts.indexes, @tagName(index)),
|
|
452
|
-
scan_buffer,
|
|
453
|
-
snapshot,
|
|
454
|
-
Value.key_from_value(&.{
|
|
455
|
-
.field = min,
|
|
456
|
-
.timestamp = TimestampRange.timestamp_min,
|
|
457
|
-
}),
|
|
458
|
-
Value.key_from_value(&.{
|
|
459
|
-
.field = max,
|
|
460
|
-
.timestamp = TimestampRange.timestamp_max,
|
|
461
|
-
}),
|
|
462
|
-
params.direction,
|
|
463
|
-
);
|
|
464
|
-
|
|
465
|
-
self.lookup = ScanLookup.init(groove_accounts, &scan_range);
|
|
466
|
-
self.lookup.read(env.scan_lookup_buffer, &scan_lookup_callback);
|
|
467
|
-
|
|
468
|
-
while (self.result == null) {
|
|
469
|
-
if (env.ticks_remaining == 0) return error.OutOfTicks;
|
|
470
|
-
env.ticks_remaining -= 1;
|
|
471
|
-
env.storage.run();
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return self.result.?;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
fn scan_lookup_callback(lookup: *ScanLookup, result: []const tb.Account) void {
|
|
478
|
-
const self: *ScannerIndex = @fieldParentPtr("lookup", lookup);
|
|
479
|
-
assert(self.result == null);
|
|
480
|
-
self.result = result;
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
fn scan_accounts(env: *Environment, params: ScanParams, snapshot: u64) ![]const tb.Account {
|
|
486
|
-
switch (params.index) {
|
|
487
|
-
inline else => |index| {
|
|
488
|
-
const Scanner = ScannerIndexType(index);
|
|
489
|
-
var scanner = Scanner{};
|
|
490
|
-
return try scanner.scan(env, params, snapshot);
|
|
491
|
-
},
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// The forest should behave like a simple key-value data-structure.
|
|
496
|
-
const Model = struct {
|
|
497
|
-
const Map = std.hash_map.AutoHashMap(u128, Account);
|
|
498
|
-
const Set = std.hash_map.AutoHashMap(u64, void);
|
|
499
|
-
const LogEntry = struct { op: u64, account: Account };
|
|
500
|
-
const Log = std.fifo.LinearFifo(LogEntry, .Dynamic);
|
|
501
|
-
|
|
502
|
-
// Represents persistent state:
|
|
503
|
-
checkpointed: struct {
|
|
504
|
-
objects: Map,
|
|
505
|
-
timestamps: Set,
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
// Represents in-memory state:
|
|
509
|
-
log: Log,
|
|
510
|
-
|
|
511
|
-
pub fn init(gpa: std.mem.Allocator) Model {
|
|
512
|
-
return .{
|
|
513
|
-
.checkpointed = .{
|
|
514
|
-
.objects = Map.init(gpa),
|
|
515
|
-
.timestamps = Set.init(gpa),
|
|
516
|
-
},
|
|
517
|
-
.log = Log.init(gpa),
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
pub fn deinit(model: *Model) void {
|
|
522
|
-
model.checkpointed.objects.deinit();
|
|
523
|
-
model.checkpointed.timestamps.deinit();
|
|
524
|
-
model.log.deinit();
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
pub fn put_account(model: *Model, account: *const Account, op: u64) !void {
|
|
528
|
-
try model.log.writeItem(.{ .op = op, .account = account.* });
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
pub fn get_account(model: *const Model, id: u128) ?Account {
|
|
532
|
-
return model.get_account_from_log(.{ .id = id }) orelse
|
|
533
|
-
model.checkpointed.objects.get(id);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
pub fn exists_account(model: *const Model, timestamp: u64) bool {
|
|
537
|
-
return model.get_account_from_log(.{ .timestamp = timestamp }) != null or
|
|
538
|
-
model.checkpointed.timestamps.contains(timestamp);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
fn get_account_from_log(
|
|
542
|
-
model: *const Model,
|
|
543
|
-
key: union(enum) { id: u128, timestamp: u64 },
|
|
544
|
-
) ?Account {
|
|
545
|
-
var latest_op: ?u64 = null;
|
|
546
|
-
const log_size = model.log.readableLength();
|
|
547
|
-
var log_left = log_size;
|
|
548
|
-
while (log_left > 0) : (log_left -= 1) {
|
|
549
|
-
const entry = model.log.peekItem(log_left - 1); // most recent first
|
|
550
|
-
if (latest_op == null) {
|
|
551
|
-
latest_op = entry.op;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
assert(latest_op.? >= entry.op);
|
|
555
|
-
|
|
556
|
-
if (switch (key) {
|
|
557
|
-
.id => |id| entry.account.id == id,
|
|
558
|
-
.timestamp => |timestamp| entry.account.timestamp == timestamp,
|
|
559
|
-
}) {
|
|
560
|
-
return entry.account;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return null;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
pub fn checkpoint(model: *Model, op: u64) !void {
|
|
567
|
-
const checkpointable = op - (op % constants.lsm_compaction_ops) -| 1;
|
|
568
|
-
const log_size = model.log.readableLength();
|
|
569
|
-
var log_index: usize = 0;
|
|
570
|
-
while (log_index < log_size) : (log_index += 1) {
|
|
571
|
-
const entry = model.log.peekItem(log_index);
|
|
572
|
-
if (entry.op > checkpointable) {
|
|
573
|
-
break;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
try model.checkpointed.objects.put(entry.account.id, entry.account);
|
|
577
|
-
try model.checkpointed.timestamps.put(entry.account.timestamp, {});
|
|
578
|
-
}
|
|
579
|
-
model.log.discard(log_index);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
pub fn storage_reset(model: *Model) void {
|
|
583
|
-
model.log.discard(model.log.readableLength());
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
fn apply(env: *Environment, gpa: std.mem.Allocator, fuzz_ops: []const FuzzOp) !void {
|
|
588
|
-
var model = Model.init(gpa);
|
|
589
|
-
defer model.deinit();
|
|
590
|
-
|
|
591
|
-
for (fuzz_ops, 0..) |fuzz_op, fuzz_op_index| {
|
|
592
|
-
assert(env.state == .fuzzing);
|
|
593
|
-
log.debug("Running fuzz_ops[{}/{}] == {}", .{
|
|
594
|
-
fuzz_op_index,
|
|
595
|
-
fuzz_ops.len,
|
|
596
|
-
fuzz_op.action,
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
const storage_size_used = env.storage.size_used();
|
|
600
|
-
log.debug("storage.size_used = {}/{}", .{ storage_size_used, env.storage.size });
|
|
601
|
-
|
|
602
|
-
const model_size = brk: {
|
|
603
|
-
const account_count = model.log.readableLength() +
|
|
604
|
-
model.checkpointed.objects.count();
|
|
605
|
-
break :brk account_count * @sizeOf(Account);
|
|
606
|
-
};
|
|
607
|
-
// NOTE: This isn't accurate anymore because the model can contain multiple copies of
|
|
608
|
-
// an account in the log
|
|
609
|
-
log.debug("space_amplification ~= {d:.2}", .{
|
|
610
|
-
@as(f64, @floatFromInt(storage_size_used)) / @as(f64, @floatFromInt(model_size)),
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
// Apply fuzz_op to the forest and the model.
|
|
614
|
-
try env.apply_op(gpa, fuzz_op, &model);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
log.debug("Applied all ops", .{});
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
fn apply_op(env: *Environment, gpa: std.mem.Allocator, fuzz_op: FuzzOp, model: *Model) !void {
|
|
621
|
-
switch (fuzz_op.modifier) {
|
|
622
|
-
.normal => {
|
|
623
|
-
env.ticks_remaining = std.math.maxInt(usize);
|
|
624
|
-
env.apply_op_action(fuzz_op, model) catch |err| {
|
|
625
|
-
switch (err) {
|
|
626
|
-
error.OutOfTicks => unreachable,
|
|
627
|
-
else => return err,
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
},
|
|
631
|
-
.crash_after_ticks => |ticks_remaining| {
|
|
632
|
-
env.ticks_remaining = ticks_remaining;
|
|
633
|
-
env.apply_op_action(fuzz_op, model) catch |err| {
|
|
634
|
-
switch (err) {
|
|
635
|
-
error.OutOfTicks => {},
|
|
636
|
-
else => return err,
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
env.ticks_remaining = std.math.maxInt(usize);
|
|
640
|
-
|
|
641
|
-
env.storage.log_pending_io();
|
|
642
|
-
env.close(gpa);
|
|
643
|
-
env.deinit(gpa);
|
|
644
|
-
env.storage.reset();
|
|
645
|
-
|
|
646
|
-
env.state = .init;
|
|
647
|
-
try env.init(gpa, env.storage);
|
|
648
|
-
|
|
649
|
-
try env.open(gpa);
|
|
650
|
-
|
|
651
|
-
// TODO: currently this checks that everything added to the LSM after checkpoint
|
|
652
|
-
// resets to the last checkpoint on crash by looking through what's been added
|
|
653
|
-
// afterwards. This won't work if we add account removal to the fuzzer though.
|
|
654
|
-
const snapshot = blk: {
|
|
655
|
-
if (vsr.Checkpoint.trigger_for_checkpoint(
|
|
656
|
-
env.superblock.working.vsr_state.checkpoint.header.op,
|
|
657
|
-
)) |trigger| {
|
|
658
|
-
break :blk trigger + 1;
|
|
659
|
-
} else {
|
|
660
|
-
break :blk 0;
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
const log_size = model.log.readableLength();
|
|
664
|
-
var log_index: usize = 0;
|
|
665
|
-
while (log_index < log_size) : (log_index += 1) {
|
|
666
|
-
const entry = model.log.peekItem(log_index);
|
|
667
|
-
const id = entry.account.id;
|
|
668
|
-
if (model.checkpointed.objects.get(id)) |*checkpointed_account| {
|
|
669
|
-
try env.prefetch_account(id, snapshot);
|
|
670
|
-
if (env.get_account(id)) |lsm_account| {
|
|
671
|
-
assert(stdx.equal_bytes(Account, &lsm_account, checkpointed_account));
|
|
672
|
-
} else {
|
|
673
|
-
std.debug.panic(
|
|
674
|
-
"Account checkpointed but not in lsm after crash.\n {}\n",
|
|
675
|
-
.{checkpointed_account},
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// There are strict limits around how many values can be prefetched by one
|
|
680
|
-
// commit, see `stash_value_count_max` in groove.zig. Thus, we need to make
|
|
681
|
-
// sure we manually call groove.objects_cache.compact() every
|
|
682
|
-
// `stash_value_count_max` operations here. This is specific to this fuzzing
|
|
683
|
-
// code.
|
|
684
|
-
const groove_stash_value_count_max =
|
|
685
|
-
env.forest.grooves.accounts.objects_cache.options.stash_value_count_max;
|
|
686
|
-
|
|
687
|
-
if (log_index % groove_stash_value_count_max == 0) {
|
|
688
|
-
env.forest.grooves.accounts.objects_cache.compact();
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
model.storage_reset();
|
|
693
|
-
},
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
fn apply_op_action(env: *Environment, fuzz_op: FuzzOp, model: *Model) !void {
|
|
698
|
-
const snapshot = if (env.superblock.working.vsr_state.op_compacted(fuzz_op.op))
|
|
699
|
-
vsr.Checkpoint.trigger_for_checkpoint(
|
|
700
|
-
env.superblock.working.vsr_state.checkpoint.header.op,
|
|
701
|
-
).? + 1
|
|
702
|
-
else
|
|
703
|
-
fuzz_op.op;
|
|
704
|
-
|
|
705
|
-
switch (fuzz_op.action) {
|
|
706
|
-
.compact => |c| {
|
|
707
|
-
|
|
708
|
-
// Checkpoint is marked durable *before* a replica compacts the (pipeline + 1)ᵗʰ
|
|
709
|
-
// op. This is because `blocks_released_prior_checkpoint_durability` in FreeSet
|
|
710
|
-
// is sized according to the maximum number of blocks released by a pipeline of ops
|
|
711
|
-
// (see `blocks_released_prior_checkpoint_durability_max` in vsr.zig).
|
|
712
|
-
if (c.checkpoint_durable) env.grid.free_set.mark_checkpoint_durable();
|
|
713
|
-
|
|
714
|
-
try env.compact(fuzz_op.op);
|
|
715
|
-
if (c.checkpoint) {
|
|
716
|
-
assert(!c.checkpoint_durable);
|
|
717
|
-
try model.checkpoint(fuzz_op.op);
|
|
718
|
-
try env.checkpoint(fuzz_op.op);
|
|
719
|
-
}
|
|
720
|
-
},
|
|
721
|
-
.put_account => |account| {
|
|
722
|
-
// The forest requires prefetch before put.
|
|
723
|
-
try env.prefetch_account(account.id, snapshot);
|
|
724
|
-
const lsm_account = env.get_account(account.id);
|
|
725
|
-
|
|
726
|
-
env.put_account(&account, lsm_account);
|
|
727
|
-
try model.put_account(&account, fuzz_op.op);
|
|
728
|
-
},
|
|
729
|
-
.get_account => |id| {
|
|
730
|
-
// Get account from lsm.
|
|
731
|
-
try env.prefetch_account(id, snapshot);
|
|
732
|
-
const lsm_account = env.get_account(id);
|
|
733
|
-
|
|
734
|
-
// Compare result to model.
|
|
735
|
-
const model_account = model.get_account(id);
|
|
736
|
-
if (model_account == null) {
|
|
737
|
-
assert(lsm_account == null);
|
|
738
|
-
} else {
|
|
739
|
-
assert(stdx.equal_bytes(Account, &model_account.?, &lsm_account.?));
|
|
740
|
-
}
|
|
741
|
-
},
|
|
742
|
-
.exists_account => |timestamp| {
|
|
743
|
-
try env.prefetch_exists_account(timestamp, snapshot);
|
|
744
|
-
const lsm_found = env.exists(timestamp);
|
|
745
|
-
const model_found = model.exists_account(timestamp);
|
|
746
|
-
assert(lsm_found == model_found);
|
|
747
|
-
},
|
|
748
|
-
.scan_account => |params| {
|
|
749
|
-
const accounts = try env.scan_accounts(params, snapshot);
|
|
750
|
-
|
|
751
|
-
var timestamp_last: ?u64 = null;
|
|
752
|
-
var prefix_last: ?u128 = null;
|
|
753
|
-
|
|
754
|
-
// Asserting the positive space:
|
|
755
|
-
// all objects found by the scan must exist in our model.
|
|
756
|
-
for (accounts) |*account| {
|
|
757
|
-
const prefix_current: u128 = switch (params.index) {
|
|
758
|
-
.imported => index: {
|
|
759
|
-
assert(params.min == 0);
|
|
760
|
-
assert(params.max == 0);
|
|
761
|
-
assert(prefix_last == null);
|
|
762
|
-
assert(account.flags.imported);
|
|
763
|
-
break :index undefined;
|
|
764
|
-
},
|
|
765
|
-
.closed => index: {
|
|
766
|
-
assert(params.min == 0);
|
|
767
|
-
assert(params.max == 0);
|
|
768
|
-
assert(prefix_last == null);
|
|
769
|
-
assert(account.flags.closed);
|
|
770
|
-
break :index undefined;
|
|
771
|
-
},
|
|
772
|
-
inline else => |field| index: {
|
|
773
|
-
const Helper = GrooveAccounts.IndexTreeFieldHelperType(@tagName(field));
|
|
774
|
-
comptime assert(Helper.Index != void);
|
|
775
|
-
|
|
776
|
-
const value = Helper.index_from_object(account).?;
|
|
777
|
-
assert(value >= params.min and value <= params.max);
|
|
778
|
-
break :index value;
|
|
779
|
-
},
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
const model_account = model.get_account(account.id).?;
|
|
783
|
-
assert(model_account.id == account.id);
|
|
784
|
-
assert(model_account.user_data_128 == account.user_data_128);
|
|
785
|
-
assert(model_account.user_data_64 == account.user_data_64);
|
|
786
|
-
assert(model_account.user_data_32 == account.user_data_32);
|
|
787
|
-
assert(model_account.timestamp == account.timestamp);
|
|
788
|
-
assert(model_account.ledger == account.ledger);
|
|
789
|
-
assert(model_account.code == account.code);
|
|
790
|
-
assert(stdx.equal_bytes(
|
|
791
|
-
tb.AccountFlags,
|
|
792
|
-
&model_account.flags,
|
|
793
|
-
&account.flags,
|
|
794
|
-
));
|
|
795
|
-
|
|
796
|
-
if (params.min == params.max) {
|
|
797
|
-
// If exact match (min == max), it's expected to be sorted by timestamp.
|
|
798
|
-
if (timestamp_last) |timestamp| {
|
|
799
|
-
switch (params.direction) {
|
|
800
|
-
.ascending => assert(account.timestamp > timestamp),
|
|
801
|
-
.descending => assert(account.timestamp < timestamp),
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
timestamp_last = account.timestamp;
|
|
805
|
-
} else {
|
|
806
|
-
assert(params.index != .imported);
|
|
807
|
-
|
|
808
|
-
// If not exact, it's expected to be sorted by prefix and then timestamp.
|
|
809
|
-
if (prefix_last) |prefix| {
|
|
810
|
-
// If range (between min .. max), it's expected to be sorted by prefix.
|
|
811
|
-
switch (params.direction) {
|
|
812
|
-
.ascending => assert(prefix_current >= prefix),
|
|
813
|
-
.descending => assert(prefix_current <= prefix),
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
if (prefix_current == prefix) {
|
|
817
|
-
if (timestamp_last) |timestamp| {
|
|
818
|
-
switch (params.direction) {
|
|
819
|
-
.ascending => assert(account.timestamp > timestamp),
|
|
820
|
-
.descending => assert(account.timestamp < timestamp),
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
timestamp_last = account.timestamp;
|
|
824
|
-
} else {
|
|
825
|
-
timestamp_last = null;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
prefix_last = prefix_current;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
},
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
fn random_id(prng: *stdx.PRNG, comptime Int: type) Int {
|
|
837
|
-
return fuzz.random_id(prng, Int, .{
|
|
838
|
-
.average_hot = 8,
|
|
839
|
-
.average_cold = Environment.cache_entries_max,
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
pub fn generate_fuzz_ops(
|
|
844
|
-
gpa: std.mem.Allocator,
|
|
845
|
-
prng: *stdx.PRNG,
|
|
846
|
-
fuzz_op_count: usize,
|
|
847
|
-
) ![]const FuzzOp {
|
|
848
|
-
log.info("fuzz_op_count = {}", .{fuzz_op_count});
|
|
849
|
-
|
|
850
|
-
const fuzz_ops = try gpa.alloc(FuzzOp, fuzz_op_count);
|
|
851
|
-
errdefer gpa.free(fuzz_ops);
|
|
852
|
-
|
|
853
|
-
const action_weights = stdx.PRNG.EnumWeightsType(FuzzOpActionTag){
|
|
854
|
-
// Maybe compact more often than forced to by `puts_since_compact`.
|
|
855
|
-
.compact = if (prng.boolean()) 0 else 1,
|
|
856
|
-
// Always do puts.
|
|
857
|
-
.put_account = constants.lsm_compaction_ops * 2,
|
|
858
|
-
// Maybe do some gets.
|
|
859
|
-
.get_account = if (prng.boolean()) 0 else constants.lsm_compaction_ops,
|
|
860
|
-
// Maybe do some exists.
|
|
861
|
-
.exists_account = if (prng.boolean()) 0 else constants.lsm_compaction_ops,
|
|
862
|
-
// Maybe do some scans.
|
|
863
|
-
.scan_account = if (prng.boolean()) 0 else constants.lsm_compaction_ops,
|
|
864
|
-
};
|
|
865
|
-
log.info("action_weights = {:.2}", .{action_weights});
|
|
866
|
-
|
|
867
|
-
const modifier_weights = stdx.PRNG.EnumWeightsType(FuzzOpModifierTag){
|
|
868
|
-
.normal = 100,
|
|
869
|
-
// Maybe crash and recover from the last checkpoint a few times per fuzzer run.
|
|
870
|
-
.crash_after_ticks = if (prng.boolean()) 0 else 1,
|
|
871
|
-
};
|
|
872
|
-
log.info("modifier_weights = {:.2}", .{modifier_weights});
|
|
873
|
-
|
|
874
|
-
log.info("puts_since_compact_max = {}", .{Environment.puts_since_compact_max});
|
|
875
|
-
|
|
876
|
-
var id_to_account = std.hash_map.AutoHashMap(u128, Account).init(gpa);
|
|
877
|
-
defer id_to_account.deinit();
|
|
878
|
-
|
|
879
|
-
var op: u64 = 1;
|
|
880
|
-
var persisted_op: u64 = 0;
|
|
881
|
-
var puts_since_compact: usize = 0;
|
|
882
|
-
for (fuzz_ops, 0..) |*fuzz_op, fuzz_op_index| {
|
|
883
|
-
const too_many_puts = puts_since_compact >= Environment.puts_since_compact_max;
|
|
884
|
-
const action_tag: FuzzOpActionTag = if (too_many_puts)
|
|
885
|
-
// We have to compact before doing any other operations.
|
|
886
|
-
.compact
|
|
887
|
-
else
|
|
888
|
-
// Otherwise pick a prng FuzzOp.
|
|
889
|
-
prng.enum_weighted(FuzzOpActionTag, action_weights);
|
|
890
|
-
const action = switch (action_tag) {
|
|
891
|
-
.compact => action: {
|
|
892
|
-
const action = generate_compact(.{ .op = op, .persisted_op = persisted_op });
|
|
893
|
-
if (action.compact.checkpoint) {
|
|
894
|
-
assert(!action.compact.checkpoint_durable);
|
|
895
|
-
persisted_op = op - constants.lsm_compaction_ops;
|
|
896
|
-
}
|
|
897
|
-
break :action action;
|
|
898
|
-
},
|
|
899
|
-
.put_account => action: {
|
|
900
|
-
const action = generate_put_account(
|
|
901
|
-
prng,
|
|
902
|
-
&id_to_account,
|
|
903
|
-
fuzz_op_index + 1, // Timestamp cannot be zero.
|
|
904
|
-
);
|
|
905
|
-
try id_to_account.put(action.put_account.id, action.put_account);
|
|
906
|
-
break :action action;
|
|
907
|
-
},
|
|
908
|
-
.get_account => FuzzOpAction{ .get_account = random_id(prng, u128) },
|
|
909
|
-
.exists_account => FuzzOpAction{
|
|
910
|
-
// Not all ops generate accounts, so the timestamp may or may not be found.
|
|
911
|
-
.exists_account = prng.range_inclusive(
|
|
912
|
-
u64,
|
|
913
|
-
TimestampRange.timestamp_min,
|
|
914
|
-
fuzz_op_index + 1,
|
|
915
|
-
),
|
|
916
|
-
},
|
|
917
|
-
.scan_account => blk: {
|
|
918
|
-
@setEvalBranchQuota(10_000);
|
|
919
|
-
const Index = std.meta.FieldEnum(GrooveAccounts.IndexTrees);
|
|
920
|
-
const index = prng.enum_uniform(Index);
|
|
921
|
-
break :blk switch (index) {
|
|
922
|
-
inline else => |field| {
|
|
923
|
-
const Helper = GrooveAccounts.IndexTreeFieldHelperType(@tagName(field));
|
|
924
|
-
const min: u128, const max: u128 = switch (Helper.Index) {
|
|
925
|
-
void => .{ 0, 0 },
|
|
926
|
-
else => range: {
|
|
927
|
-
var min = random_id(prng, Helper.Index);
|
|
928
|
-
var max = if (prng.boolean()) min else random_id(
|
|
929
|
-
prng,
|
|
930
|
-
Helper.Index,
|
|
931
|
-
);
|
|
932
|
-
if (min > max) std.mem.swap(Helper.Index, &min, &max);
|
|
933
|
-
assert(min <= max);
|
|
934
|
-
break :range .{ min, max };
|
|
935
|
-
},
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
break :blk FuzzOpAction{
|
|
939
|
-
.scan_account = .{
|
|
940
|
-
.index = index,
|
|
941
|
-
.min = min,
|
|
942
|
-
.max = max,
|
|
943
|
-
.direction = prng.enum_uniform(Direction),
|
|
944
|
-
},
|
|
945
|
-
};
|
|
946
|
-
},
|
|
947
|
-
};
|
|
948
|
-
},
|
|
949
|
-
};
|
|
950
|
-
|
|
951
|
-
// TODO(jamii)
|
|
952
|
-
// Currently, crashing is only interesting during a compact.
|
|
953
|
-
// But once we have concurrent compaction, crashing at any point can be interesting.
|
|
954
|
-
//
|
|
955
|
-
// TODO(jamii)
|
|
956
|
-
// If we crash during a checkpoint, on restart we should either:
|
|
957
|
-
// * See the state from that checkpoint.
|
|
958
|
-
// * See the state from the previous checkpoint.
|
|
959
|
-
// But this is difficult to test, so for now we'll avoid it.
|
|
960
|
-
const modifier_tag = if (action == .compact and !action.compact.checkpoint)
|
|
961
|
-
prng.enum_weighted(FuzzOpModifierTag, modifier_weights)
|
|
962
|
-
else
|
|
963
|
-
FuzzOpModifierTag.normal;
|
|
964
|
-
const modifier = switch (modifier_tag) {
|
|
965
|
-
.normal => FuzzOpModifier{ .normal = {} },
|
|
966
|
-
.crash_after_ticks => FuzzOpModifier{
|
|
967
|
-
.crash_after_ticks = fuzz.random_int_exponential(
|
|
968
|
-
prng,
|
|
969
|
-
usize,
|
|
970
|
-
io_latency_mean_ticks,
|
|
971
|
-
),
|
|
972
|
-
},
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
fuzz_op.* = .{
|
|
976
|
-
.op = op,
|
|
977
|
-
.action = action,
|
|
978
|
-
.modifier = modifier,
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
switch (modifier) {
|
|
982
|
-
.normal => {},
|
|
983
|
-
.crash_after_ticks => op = persisted_op,
|
|
984
|
-
}
|
|
985
|
-
switch (action) {
|
|
986
|
-
.compact => {
|
|
987
|
-
op += 1;
|
|
988
|
-
puts_since_compact = 0;
|
|
989
|
-
},
|
|
990
|
-
.put_account => puts_since_compact += 1,
|
|
991
|
-
.get_account => {},
|
|
992
|
-
.exists_account => {},
|
|
993
|
-
.scan_account => {},
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
return fuzz_ops;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
fn generate_compact(options: struct { op: u64, persisted_op: u64 }) FuzzOpAction {
|
|
1001
|
-
const checkpoint =
|
|
1002
|
-
// Can only checkpoint on the last beat of the bar.
|
|
1003
|
-
options.op % constants.lsm_compaction_ops == constants.lsm_compaction_ops - 1 and
|
|
1004
|
-
options.op > constants.lsm_compaction_ops and
|
|
1005
|
-
// Never checkpoint at the same op twice
|
|
1006
|
-
options.op > options.persisted_op + constants.lsm_compaction_ops and
|
|
1007
|
-
// Checkpoint at the normal rate.
|
|
1008
|
-
// TODO Make LSM (and this fuzzer) unaware of VSR's checkpoint schedule.
|
|
1009
|
-
options.op == vsr.Checkpoint.trigger_for_checkpoint(
|
|
1010
|
-
vsr.Checkpoint.checkpoint_after(options.persisted_op),
|
|
1011
|
-
);
|
|
1012
|
-
|
|
1013
|
-
// Checkpoint is considered durable when a replica is committing/compacting the (pipeline + 1)ᵗʰ
|
|
1014
|
-
// prepare after checkpoint trigger. See `op_repair_min` in `replica.zig` for more context.
|
|
1015
|
-
const checkpoint_durable = checkpoint_durable: {
|
|
1016
|
-
if (vsr.Checkpoint.trigger_for_checkpoint(options.persisted_op)) |trigger| {
|
|
1017
|
-
if (options.op == trigger + constants.pipeline_prepare_queue_max + 1)
|
|
1018
|
-
break :checkpoint_durable true
|
|
1019
|
-
else
|
|
1020
|
-
break :checkpoint_durable false;
|
|
1021
|
-
} else {
|
|
1022
|
-
assert(options.persisted_op == 0);
|
|
1023
|
-
if (options.op == 1)
|
|
1024
|
-
break :checkpoint_durable true
|
|
1025
|
-
else
|
|
1026
|
-
break :checkpoint_durable false;
|
|
1027
|
-
}
|
|
1028
|
-
};
|
|
1029
|
-
|
|
1030
|
-
return FuzzOpAction{ .compact = .{
|
|
1031
|
-
.checkpoint = checkpoint,
|
|
1032
|
-
.checkpoint_durable = checkpoint_durable,
|
|
1033
|
-
} };
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
fn generate_put_account(
|
|
1037
|
-
prng: *stdx.PRNG,
|
|
1038
|
-
id_to_account: *const std.AutoHashMap(u128, Account),
|
|
1039
|
-
timestamp: u64,
|
|
1040
|
-
) FuzzOpAction {
|
|
1041
|
-
const id = random_id(prng, u128);
|
|
1042
|
-
var account = id_to_account.get(id) orelse Account{
|
|
1043
|
-
.id = id,
|
|
1044
|
-
// `timestamp` must be unique.
|
|
1045
|
-
.timestamp = timestamp,
|
|
1046
|
-
.user_data_128 = random_id(prng, u128),
|
|
1047
|
-
.user_data_64 = random_id(prng, u64),
|
|
1048
|
-
.user_data_32 = random_id(prng, u32),
|
|
1049
|
-
.reserved = 0,
|
|
1050
|
-
.ledger = random_id(prng, u32),
|
|
1051
|
-
.code = random_id(prng, u16),
|
|
1052
|
-
.flags = .{
|
|
1053
|
-
.debits_must_not_exceed_credits = prng.boolean(),
|
|
1054
|
-
.credits_must_not_exceed_debits = prng.boolean(),
|
|
1055
|
-
.imported = prng.boolean(),
|
|
1056
|
-
.closed = prng.boolean(),
|
|
1057
|
-
},
|
|
1058
|
-
.debits_pending = 0,
|
|
1059
|
-
.debits_posted = 0,
|
|
1060
|
-
.credits_pending = 0,
|
|
1061
|
-
.credits_posted = 0,
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
// These are the only fields we are allowed to change on existing accounts.
|
|
1065
|
-
account.debits_pending = prng.int(u64);
|
|
1066
|
-
account.debits_posted = prng.int(u64);
|
|
1067
|
-
account.credits_pending = prng.int(u64);
|
|
1068
|
-
account.credits_posted = prng.int(u64);
|
|
1069
|
-
return FuzzOpAction{ .put_account = account };
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
const io_latency_mean_ticks = 20;
|
|
1073
|
-
const io_latency_mean_ms: u64 = io_latency_mean_ticks * constants.tick_ms;
|
|
1074
|
-
|
|
1075
|
-
pub fn main(gpa: std.mem.Allocator, fuzz_args: fuzz.FuzzArgs) !void {
|
|
1076
|
-
var prng = stdx.PRNG.from_seed(fuzz_args.seed);
|
|
1077
|
-
|
|
1078
|
-
const fuzz_op_count = @min(
|
|
1079
|
-
fuzz_args.events_max orelse @as(usize, 1E7),
|
|
1080
|
-
fuzz.random_int_exponential(&prng, usize, 1E6),
|
|
1081
|
-
);
|
|
1082
|
-
|
|
1083
|
-
const fuzz_ops = try generate_fuzz_ops(gpa, &prng, fuzz_op_count);
|
|
1084
|
-
defer gpa.free(fuzz_ops);
|
|
1085
|
-
|
|
1086
|
-
// Init mocked storage.
|
|
1087
|
-
var storage = try fixtures.init_storage(gpa, .{
|
|
1088
|
-
.seed = prng.int(u64),
|
|
1089
|
-
.size = constants.storage_size_limit_default,
|
|
1090
|
-
.read_latency_min = .{ .ns = 0 },
|
|
1091
|
-
.read_latency_mean = fuzz.range_inclusive_ms(&prng, 0, io_latency_mean_ms),
|
|
1092
|
-
.write_latency_min = .{ .ns = 0 },
|
|
1093
|
-
.write_latency_mean = fuzz.range_inclusive_ms(&prng, 0, io_latency_mean_ms),
|
|
1094
|
-
});
|
|
1095
|
-
defer storage.deinit(gpa);
|
|
1096
|
-
|
|
1097
|
-
try fixtures.storage_format(gpa, &storage, .{});
|
|
1098
|
-
|
|
1099
|
-
try Environment.run(gpa, &storage, fuzz_ops);
|
|
1100
|
-
|
|
1101
|
-
log.info("Passed!", .{});
|
|
1102
|
-
}
|