summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/options/options_settable_test.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rocksdb/options/options_settable_test.cc621
1 files changed, 621 insertions, 0 deletions
diff --git a/src/rocksdb/options/options_settable_test.cc b/src/rocksdb/options/options_settable_test.cc
new file mode 100644
index 000000000..63e9721ca
--- /dev/null
+++ b/src/rocksdb/options/options_settable_test.cc
@@ -0,0 +1,621 @@
+// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
+// This source code is licensed under both the GPLv2 (found in the
+// COPYING file in the root directory) and Apache 2.0 License
+// (found in the LICENSE.Apache file in the root directory).
+//
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include <cstring>
+
+#include "options/cf_options.h"
+#include "options/db_options.h"
+#include "options/options_helper.h"
+#include "rocksdb/convenience.h"
+#include "test_util/testharness.h"
+
+#ifndef GFLAGS
+bool FLAGS_enable_print = false;
+#else
+#include "util/gflags_compat.h"
+using GFLAGS_NAMESPACE::ParseCommandLineFlags;
+DEFINE_bool(enable_print, false, "Print options generated to console.");
+#endif // GFLAGS
+
+namespace ROCKSDB_NAMESPACE {
+
+// Verify options are settable from options strings.
+// We take the approach that depends on compiler behavior that copy constructor
+// won't touch implicit padding bytes, so that the test is fragile.
+// As a result, we only run the tests to verify new fields in options are
+// settable through string on limited platforms as it depends on behavior of
+// compilers.
+#ifndef ROCKSDB_LITE
+#if defined OS_LINUX || defined OS_WIN
+#ifndef __clang__
+#ifndef ROCKSDB_UBSAN_RUN
+
+class OptionsSettableTest : public testing::Test {
+ public:
+ OptionsSettableTest() {}
+};
+
+const char kSpecialChar = 'z';
+using OffsetGap = std::vector<std::pair<size_t, size_t>>;
+
+void FillWithSpecialChar(char* start_ptr, size_t total_size,
+ const OffsetGap& excluded,
+ char special_char = kSpecialChar) {
+ size_t offset = 0;
+ // The excluded vector contains pairs of bytes, (first, second).
+ // The first bytes are all set to the special char (represented as 'c' below).
+ // The second bytes are simply skipped (padding bytes).
+ // ccccc[skipped]cccccccc[skiped]cccccccc[skipped]
+ for (auto& pair : excluded) {
+ std::memset(start_ptr + offset, special_char, pair.first - offset);
+ offset = pair.first + pair.second;
+ }
+ // The rest of the structure is filled with the special characters.
+ // ccccc[skipped]cccccccc[skiped]cccccccc[skipped]cccccccccccccccc
+ std::memset(start_ptr + offset, special_char, total_size - offset);
+}
+
+int NumUnsetBytes(char* start_ptr, size_t total_size,
+ const OffsetGap& excluded) {
+ int total_unset_bytes_base = 0;
+ size_t offset = 0;
+ for (auto& pair : excluded) {
+ // The first part of the structure contains memory spaces that can be
+ // set (pair.first), and memory spaces that cannot be set (pair.second).
+ // Therefore total_unset_bytes_base only agregates bytes set to kSpecialChar
+ // in the pair.first bytes, but skips the pair.second bytes (padding bytes).
+ for (char* ptr = start_ptr + offset; ptr < start_ptr + pair.first; ptr++) {
+ if (*ptr == kSpecialChar) {
+ total_unset_bytes_base++;
+ }
+ }
+ offset = pair.first + pair.second;
+ }
+ // Then total_unset_bytes_base aggregates the bytes
+ // set to kSpecialChar in the rest of the structure
+ for (char* ptr = start_ptr + offset; ptr < start_ptr + total_size; ptr++) {
+ if (*ptr == kSpecialChar) {
+ total_unset_bytes_base++;
+ }
+ }
+ return total_unset_bytes_base;
+}
+
+// Return true iff two structs are the same except excluded fields.
+bool CompareBytes(char* start_ptr1, char* start_ptr2, size_t total_size,
+ const OffsetGap& excluded) {
+ size_t offset = 0;
+ for (auto& pair : excluded) {
+ for (; offset < pair.first; offset++) {
+ if (*(start_ptr1 + offset) != *(start_ptr2 + offset)) {
+ return false;
+ }
+ }
+ offset = pair.first + pair.second;
+ }
+ for (; offset < total_size; offset++) {
+ if (*(start_ptr1 + offset) != *(start_ptr2 + offset)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// If the test fails, likely a new option is added to BlockBasedTableOptions
+// but it cannot be set through GetBlockBasedTableOptionsFromString(), or the
+// test is not updated accordingly.
+// After adding an option, we need to make sure it is settable by
+// GetBlockBasedTableOptionsFromString() and add the option to the input string
+// passed to the GetBlockBasedTableOptionsFromString() in this test.
+// If it is a complicated type, you also need to add the field to
+// kBbtoExcluded, and maybe add customized verification for it.
+TEST_F(OptionsSettableTest, BlockBasedTableOptionsAllFieldsSettable) {
+ // Items in the form of <offset, size>. Need to be in ascending order
+ // and not overlapping. Need to update if new option to be excluded is added
+ // (e.g, pointer-type)
+ const OffsetGap kBbtoExcluded = {
+ {offsetof(struct BlockBasedTableOptions, flush_block_policy_factory),
+ sizeof(std::shared_ptr<FlushBlockPolicyFactory>)},
+ {offsetof(struct BlockBasedTableOptions, block_cache),
+ sizeof(std::shared_ptr<Cache>)},
+ {offsetof(struct BlockBasedTableOptions, persistent_cache),
+ sizeof(std::shared_ptr<PersistentCache>)},
+ {offsetof(struct BlockBasedTableOptions, block_cache_compressed),
+ sizeof(std::shared_ptr<Cache>)},
+ {offsetof(struct BlockBasedTableOptions, cache_usage_options),
+ sizeof(CacheUsageOptions)},
+ {offsetof(struct BlockBasedTableOptions, filter_policy),
+ sizeof(std::shared_ptr<const FilterPolicy>)},
+ };
+
+ // In this test, we catch a new option of BlockBasedTableOptions that is not
+ // settable through GetBlockBasedTableOptionsFromString().
+ // We count padding bytes of the option struct, and assert it to be the same
+ // as unset bytes of an option struct initialized by
+ // GetBlockBasedTableOptionsFromString().
+
+ char* bbto_ptr = new char[sizeof(BlockBasedTableOptions)];
+
+ // Count padding bytes by setting all bytes in the memory to a special char,
+ // copy a well constructed struct to this memory and see how many special
+ // bytes left.
+ BlockBasedTableOptions* bbto = new (bbto_ptr) BlockBasedTableOptions();
+ FillWithSpecialChar(bbto_ptr, sizeof(BlockBasedTableOptions), kBbtoExcluded);
+ // It based on the behavior of compiler that padding bytes are not changed
+ // when copying the struct. It's prone to failure when compiler behavior
+ // changes. We verify there is unset bytes to detect the case.
+ *bbto = BlockBasedTableOptions();
+ int unset_bytes_base =
+ NumUnsetBytes(bbto_ptr, sizeof(BlockBasedTableOptions), kBbtoExcluded);
+ ASSERT_GT(unset_bytes_base, 0);
+ bbto->~BlockBasedTableOptions();
+
+ // Construct the base option passed into
+ // GetBlockBasedTableOptionsFromString().
+ bbto = new (bbto_ptr) BlockBasedTableOptions();
+ FillWithSpecialChar(bbto_ptr, sizeof(BlockBasedTableOptions), kBbtoExcluded);
+ // This option is not setable:
+ bbto->use_delta_encoding = true;
+
+ char* new_bbto_ptr = new char[sizeof(BlockBasedTableOptions)];
+ BlockBasedTableOptions* new_bbto =
+ new (new_bbto_ptr) BlockBasedTableOptions();
+ FillWithSpecialChar(new_bbto_ptr, sizeof(BlockBasedTableOptions),
+ kBbtoExcluded);
+
+ // Need to update the option string if a new option is added.
+ ASSERT_OK(GetBlockBasedTableOptionsFromString(
+ *bbto,
+ "cache_index_and_filter_blocks=1;"
+ "cache_index_and_filter_blocks_with_high_priority=true;"
+ "metadata_cache_options={top_level_index_pinning=kFallback;"
+ "partition_pinning=kAll;"
+ "unpartitioned_pinning=kFlushedAndSimilar;};"
+ "pin_l0_filter_and_index_blocks_in_cache=1;"
+ "pin_top_level_index_and_filter=1;"
+ "index_type=kHashSearch;"
+ "data_block_index_type=kDataBlockBinaryAndHash;"
+ "index_shortening=kNoShortening;"
+ "data_block_hash_table_util_ratio=0.75;"
+ "checksum=kxxHash;no_block_cache=1;"
+ "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
+ "block_size_deviation=8;block_restart_interval=4; "
+ "metadata_block_size=1024;"
+ "partition_filters=false;"
+ "optimize_filters_for_memory=true;"
+ "index_block_restart_interval=4;"
+ "filter_policy=bloomfilter:4:true;whole_key_filtering=1;detect_filter_"
+ "construct_corruption=false;"
+ "format_version=1;"
+ "verify_compression=true;read_amp_bytes_per_bit=0;"
+ "enable_index_compression=false;"
+ "block_align=true;"
+ "max_auto_readahead_size=0;"
+ "prepopulate_block_cache=kDisable;"
+ "initial_auto_readahead_size=0;"
+ "num_file_reads_for_auto_readahead=0",
+ new_bbto));
+
+ ASSERT_EQ(unset_bytes_base,
+ NumUnsetBytes(new_bbto_ptr, sizeof(BlockBasedTableOptions),
+ kBbtoExcluded));
+
+ ASSERT_TRUE(new_bbto->block_cache.get() != nullptr);
+ ASSERT_TRUE(new_bbto->block_cache_compressed.get() != nullptr);
+ ASSERT_TRUE(new_bbto->filter_policy.get() != nullptr);
+
+ bbto->~BlockBasedTableOptions();
+ new_bbto->~BlockBasedTableOptions();
+
+ delete[] bbto_ptr;
+ delete[] new_bbto_ptr;
+}
+
+// If the test fails, likely a new option is added to DBOptions
+// but it cannot be set through GetDBOptionsFromString(), or the test is not
+// updated accordingly.
+// After adding an option, we need to make sure it is settable by
+// GetDBOptionsFromString() and add the option to the input string passed to
+// DBOptionsFromString()in this test.
+// If it is a complicated type, you also need to add the field to
+// kDBOptionsExcluded, and maybe add customized verification for it.
+TEST_F(OptionsSettableTest, DBOptionsAllFieldsSettable) {
+ const OffsetGap kDBOptionsExcluded = {
+ {offsetof(struct DBOptions, env), sizeof(Env*)},
+ {offsetof(struct DBOptions, rate_limiter),
+ sizeof(std::shared_ptr<RateLimiter>)},
+ {offsetof(struct DBOptions, sst_file_manager),
+ sizeof(std::shared_ptr<SstFileManager>)},
+ {offsetof(struct DBOptions, info_log), sizeof(std::shared_ptr<Logger>)},
+ {offsetof(struct DBOptions, statistics),
+ sizeof(std::shared_ptr<Statistics>)},
+ {offsetof(struct DBOptions, db_paths), sizeof(std::vector<DbPath>)},
+ {offsetof(struct DBOptions, db_log_dir), sizeof(std::string)},
+ {offsetof(struct DBOptions, wal_dir), sizeof(std::string)},
+ {offsetof(struct DBOptions, write_buffer_manager),
+ sizeof(std::shared_ptr<WriteBufferManager>)},
+ {offsetof(struct DBOptions, listeners),
+ sizeof(std::vector<std::shared_ptr<EventListener>>)},
+ {offsetof(struct DBOptions, row_cache), sizeof(std::shared_ptr<Cache>)},
+ {offsetof(struct DBOptions, wal_filter), sizeof(const WalFilter*)},
+ {offsetof(struct DBOptions, file_checksum_gen_factory),
+ sizeof(std::shared_ptr<FileChecksumGenFactory>)},
+ {offsetof(struct DBOptions, db_host_id), sizeof(std::string)},
+ {offsetof(struct DBOptions, checksum_handoff_file_types),
+ sizeof(FileTypeSet)},
+ {offsetof(struct DBOptions, compaction_service),
+ sizeof(std::shared_ptr<CompactionService>)},
+ };
+
+ char* options_ptr = new char[sizeof(DBOptions)];
+
+ // Count padding bytes by setting all bytes in the memory to a special char,
+ // copy a well constructed struct to this memory and see how many special
+ // bytes left.
+ DBOptions* options = new (options_ptr) DBOptions();
+ FillWithSpecialChar(options_ptr, sizeof(DBOptions), kDBOptionsExcluded);
+ // It based on the behavior of compiler that padding bytes are not changed
+ // when copying the struct. It's prone to failure when compiler behavior
+ // changes. We verify there is unset bytes to detect the case.
+ *options = DBOptions();
+ int unset_bytes_base =
+ NumUnsetBytes(options_ptr, sizeof(DBOptions), kDBOptionsExcluded);
+ ASSERT_GT(unset_bytes_base, 0);
+ options->~DBOptions();
+
+ options = new (options_ptr) DBOptions();
+ FillWithSpecialChar(options_ptr, sizeof(DBOptions), kDBOptionsExcluded);
+
+ char* new_options_ptr = new char[sizeof(DBOptions)];
+ DBOptions* new_options = new (new_options_ptr) DBOptions();
+ FillWithSpecialChar(new_options_ptr, sizeof(DBOptions), kDBOptionsExcluded);
+
+ // Need to update the option string if a new option is added.
+ ASSERT_OK(
+ GetDBOptionsFromString(*options,
+ "wal_bytes_per_sync=4295048118;"
+ "delete_obsolete_files_period_micros=4294967758;"
+ "WAL_ttl_seconds=4295008036;"
+ "WAL_size_limit_MB=4295036161;"
+ "max_write_batch_group_size_bytes=1048576;"
+ "wal_dir=path/to/wal_dir;"
+ "db_write_buffer_size=2587;"
+ "max_subcompactions=64330;"
+ "table_cache_numshardbits=28;"
+ "max_open_files=72;"
+ "max_file_opening_threads=35;"
+ "max_background_jobs=8;"
+ "max_background_compactions=33;"
+ "use_fsync=true;"
+ "use_adaptive_mutex=false;"
+ "max_total_wal_size=4295005604;"
+ "compaction_readahead_size=0;"
+ "keep_log_file_num=4890;"
+ "skip_stats_update_on_db_open=false;"
+ "skip_checking_sst_file_sizes_on_db_open=false;"
+ "max_manifest_file_size=4295009941;"
+ "db_log_dir=path/to/db_log_dir;"
+ "writable_file_max_buffer_size=1048576;"
+ "paranoid_checks=true;"
+ "flush_verify_memtable_count=true;"
+ "track_and_verify_wals_in_manifest=true;"
+ "verify_sst_unique_id_in_manifest=true;"
+ "is_fd_close_on_exec=false;"
+ "bytes_per_sync=4295013613;"
+ "strict_bytes_per_sync=true;"
+ "enable_thread_tracking=false;"
+ "recycle_log_file_num=0;"
+ "create_missing_column_families=true;"
+ "log_file_time_to_roll=3097;"
+ "max_background_flushes=35;"
+ "create_if_missing=false;"
+ "error_if_exists=true;"
+ "delayed_write_rate=4294976214;"
+ "manifest_preallocation_size=1222;"
+ "allow_mmap_writes=false;"
+ "stats_dump_period_sec=70127;"
+ "stats_persist_period_sec=54321;"
+ "persist_stats_to_disk=true;"
+ "stats_history_buffer_size=14159;"
+ "allow_fallocate=true;"
+ "allow_mmap_reads=false;"
+ "use_direct_reads=false;"
+ "use_direct_io_for_flush_and_compaction=false;"
+ "max_log_file_size=4607;"
+ "random_access_max_buffer_size=1048576;"
+ "advise_random_on_open=true;"
+ "fail_if_options_file_error=false;"
+ "enable_pipelined_write=false;"
+ "unordered_write=false;"
+ "allow_concurrent_memtable_write=true;"
+ "wal_recovery_mode=kPointInTimeRecovery;"
+ "enable_write_thread_adaptive_yield=true;"
+ "write_thread_slow_yield_usec=5;"
+ "write_thread_max_yield_usec=1000;"
+ "access_hint_on_compaction_start=NONE;"
+ "info_log_level=DEBUG_LEVEL;"
+ "dump_malloc_stats=false;"
+ "allow_2pc=false;"
+ "avoid_flush_during_recovery=false;"
+ "avoid_flush_during_shutdown=false;"
+ "allow_ingest_behind=false;"
+ "concurrent_prepare=false;"
+ "two_write_queues=false;"
+ "manual_wal_flush=false;"
+ "wal_compression=kZSTD;"
+ "seq_per_batch=false;"
+ "atomic_flush=false;"
+ "avoid_unnecessary_blocking_io=false;"
+ "log_readahead_size=0;"
+ "write_dbid_to_manifest=false;"
+ "best_efforts_recovery=false;"
+ "max_bgerror_resume_count=2;"
+ "bgerror_resume_retry_interval=1000000;"
+ "db_host_id=hostname;"
+ "lowest_used_cache_tier=kNonVolatileBlockTier;"
+ "allow_data_in_errors=false;"
+ "enforce_single_del_contracts=false;",
+ new_options));
+
+ ASSERT_EQ(unset_bytes_base, NumUnsetBytes(new_options_ptr, sizeof(DBOptions),
+ kDBOptionsExcluded));
+
+ options->~DBOptions();
+ new_options->~DBOptions();
+
+ delete[] options_ptr;
+ delete[] new_options_ptr;
+}
+
+// If the test fails, likely a new option is added to ColumnFamilyOptions
+// but it cannot be set through GetColumnFamilyOptionsFromString(), or the
+// test is not updated accordingly.
+// After adding an option, we need to make sure it is settable by
+// GetColumnFamilyOptionsFromString() and add the option to the input
+// string passed to GetColumnFamilyOptionsFromString() in this test.
+// If it is a complicated type, you also need to add the field to
+// kColumnFamilyOptionsExcluded, and maybe add customized verification
+// for it.
+TEST_F(OptionsSettableTest, ColumnFamilyOptionsAllFieldsSettable) {
+ // options in the excluded set need to appear in the same order as in
+ // ColumnFamilyOptions.
+ const OffsetGap kColumnFamilyOptionsExcluded = {
+ {offsetof(struct ColumnFamilyOptions, inplace_callback),
+ sizeof(UpdateStatus(*)(char*, uint32_t*, Slice, std::string*))},
+ {offsetof(struct ColumnFamilyOptions,
+ memtable_insert_with_hint_prefix_extractor),
+ sizeof(std::shared_ptr<const SliceTransform>)},
+ {offsetof(struct ColumnFamilyOptions, compression_per_level),
+ sizeof(std::vector<CompressionType>)},
+ {offsetof(struct ColumnFamilyOptions,
+ max_bytes_for_level_multiplier_additional),
+ sizeof(std::vector<int>)},
+ {offsetof(struct ColumnFamilyOptions, memtable_factory),
+ sizeof(std::shared_ptr<MemTableRepFactory>)},
+ {offsetof(struct ColumnFamilyOptions,
+ table_properties_collector_factories),
+ sizeof(ColumnFamilyOptions::TablePropertiesCollectorFactories)},
+ {offsetof(struct ColumnFamilyOptions, preclude_last_level_data_seconds),
+ sizeof(uint64_t)},
+ {offsetof(struct ColumnFamilyOptions, preserve_internal_time_seconds),
+ sizeof(uint64_t)},
+ {offsetof(struct ColumnFamilyOptions, blob_cache),
+ sizeof(std::shared_ptr<Cache>)},
+ {offsetof(struct ColumnFamilyOptions, comparator), sizeof(Comparator*)},
+ {offsetof(struct ColumnFamilyOptions, merge_operator),
+ sizeof(std::shared_ptr<MergeOperator>)},
+ {offsetof(struct ColumnFamilyOptions, compaction_filter),
+ sizeof(const CompactionFilter*)},
+ {offsetof(struct ColumnFamilyOptions, compaction_filter_factory),
+ sizeof(std::shared_ptr<CompactionFilterFactory>)},
+ {offsetof(struct ColumnFamilyOptions, prefix_extractor),
+ sizeof(std::shared_ptr<const SliceTransform>)},
+ {offsetof(struct ColumnFamilyOptions, snap_refresh_nanos),
+ sizeof(uint64_t)},
+ {offsetof(struct ColumnFamilyOptions, table_factory),
+ sizeof(std::shared_ptr<TableFactory>)},
+ {offsetof(struct ColumnFamilyOptions, cf_paths),
+ sizeof(std::vector<DbPath>)},
+ {offsetof(struct ColumnFamilyOptions, compaction_thread_limiter),
+ sizeof(std::shared_ptr<ConcurrentTaskLimiter>)},
+ {offsetof(struct ColumnFamilyOptions, sst_partitioner_factory),
+ sizeof(std::shared_ptr<SstPartitionerFactory>)},
+ };
+
+ char* options_ptr = new char[sizeof(ColumnFamilyOptions)];
+
+ // Count padding bytes by setting all bytes in the memory to a special char,
+ // copy a well constructed struct to this memory and see how many special
+ // bytes left.
+ FillWithSpecialChar(options_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded);
+
+ // Invoke a user-defined constructor in the hope that it does not overwrite
+ // padding bytes. Note that previously we relied on the implicitly-defined
+ // copy-assignment operator (i.e., `*options = ColumnFamilyOptions();`) here,
+ // which did in fact modify padding bytes.
+ ColumnFamilyOptions* options = new (options_ptr) ColumnFamilyOptions();
+
+ int unset_bytes_base = NumUnsetBytes(options_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded);
+ ASSERT_GT(unset_bytes_base, 0);
+ options->~ColumnFamilyOptions();
+
+ options = new (options_ptr) ColumnFamilyOptions();
+ FillWithSpecialChar(options_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded);
+
+ // Following options are not settable through
+ // GetColumnFamilyOptionsFromString():
+ options->compaction_options_universal = CompactionOptionsUniversal();
+ options->num_levels = 42; // Initialize options for MutableCF
+ options->compaction_filter = nullptr;
+ options->sst_partitioner_factory = nullptr;
+
+ char* new_options_ptr = new char[sizeof(ColumnFamilyOptions)];
+ ColumnFamilyOptions* new_options =
+ new (new_options_ptr) ColumnFamilyOptions();
+ FillWithSpecialChar(new_options_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded);
+
+ // Need to update the option string if a new option is added.
+ ASSERT_OK(GetColumnFamilyOptionsFromString(
+ *options,
+ "compaction_filter_factory=mpudlojcujCompactionFilterFactory;"
+ "table_factory=PlainTable;"
+ "prefix_extractor=rocksdb.CappedPrefix.13;"
+ "comparator=leveldb.BytewiseComparator;"
+ "compression_per_level=kBZip2Compression:kBZip2Compression:"
+ "kBZip2Compression:kNoCompression:kZlibCompression:kBZip2Compression:"
+ "kSnappyCompression;"
+ "max_bytes_for_level_base=986;"
+ "bloom_locality=8016;"
+ "target_file_size_base=4294976376;"
+ "memtable_huge_page_size=2557;"
+ "max_successive_merges=5497;"
+ "max_sequential_skip_in_iterations=4294971408;"
+ "arena_block_size=1893;"
+ "target_file_size_multiplier=35;"
+ "min_write_buffer_number_to_merge=9;"
+ "max_write_buffer_number=84;"
+ "write_buffer_size=1653;"
+ "max_compaction_bytes=64;"
+ "ignore_max_compaction_bytes_for_input=true;"
+ "max_bytes_for_level_multiplier=60;"
+ "memtable_factory=SkipListFactory;"
+ "compression=kNoCompression;"
+ "compression_opts=5:6:7:8:9:10:true:11:false;"
+ "bottommost_compression_opts=4:5:6:7:8:9:true:10:true;"
+ "bottommost_compression=kDisableCompressionOption;"
+ "level0_stop_writes_trigger=33;"
+ "num_levels=99;"
+ "level0_slowdown_writes_trigger=22;"
+ "level0_file_num_compaction_trigger=14;"
+ "compaction_filter=urxcqstuwnCompactionFilter;"
+ "soft_pending_compaction_bytes_limit=0;"
+ "max_write_buffer_number_to_maintain=84;"
+ "max_write_buffer_size_to_maintain=2147483648;"
+ "merge_operator=aabcxehazrMergeOperator;"
+ "memtable_prefix_bloom_size_ratio=0.4642;"
+ "memtable_whole_key_filtering=true;"
+ "memtable_insert_with_hint_prefix_extractor=rocksdb.CappedPrefix.13;"
+ "check_flush_compaction_key_order=false;"
+ "paranoid_file_checks=true;"
+ "force_consistency_checks=true;"
+ "inplace_update_num_locks=7429;"
+ "experimental_mempurge_threshold=0.0001;"
+ "optimize_filters_for_hits=false;"
+ "level_compaction_dynamic_level_bytes=false;"
+ "level_compaction_dynamic_file_size=true;"
+ "inplace_update_support=false;"
+ "compaction_style=kCompactionStyleFIFO;"
+ "compaction_pri=kMinOverlappingRatio;"
+ "hard_pending_compaction_bytes_limit=0;"
+ "disable_auto_compactions=false;"
+ "report_bg_io_stats=true;"
+ "ttl=60;"
+ "periodic_compaction_seconds=3600;"
+ "sample_for_compression=0;"
+ "enable_blob_files=true;"
+ "min_blob_size=256;"
+ "blob_file_size=1000000;"
+ "blob_compression_type=kBZip2Compression;"
+ "enable_blob_garbage_collection=true;"
+ "blob_garbage_collection_age_cutoff=0.5;"
+ "blob_garbage_collection_force_threshold=0.75;"
+ "blob_compaction_readahead_size=262144;"
+ "blob_file_starting_level=1;"
+ "prepopulate_blob_cache=kDisable;"
+ "bottommost_temperature=kWarm;"
+ "last_level_temperature=kWarm;"
+ "preclude_last_level_data_seconds=86400;"
+ "preserve_internal_time_seconds=86400;"
+ "compaction_options_fifo={max_table_files_size=3;allow_"
+ "compaction=false;age_for_warm=1;};"
+ "blob_cache=1M;"
+ "memtable_protection_bytes_per_key=2;",
+ new_options));
+
+ ASSERT_NE(new_options->blob_cache.get(), nullptr);
+
+ ASSERT_EQ(unset_bytes_base,
+ NumUnsetBytes(new_options_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded));
+
+ ColumnFamilyOptions rnd_filled_options = *new_options;
+
+ options->~ColumnFamilyOptions();
+ new_options->~ColumnFamilyOptions();
+
+ delete[] options_ptr;
+ delete[] new_options_ptr;
+
+ // Test copying to mutabable and immutable options and copy back the mutable
+ // part.
+ const OffsetGap kMutableCFOptionsExcluded = {
+ {offsetof(struct MutableCFOptions, prefix_extractor),
+ sizeof(std::shared_ptr<const SliceTransform>)},
+ {offsetof(struct MutableCFOptions,
+ max_bytes_for_level_multiplier_additional),
+ sizeof(std::vector<int>)},
+ {offsetof(struct MutableCFOptions, compression_per_level),
+ sizeof(std::vector<CompressionType>)},
+ {offsetof(struct MutableCFOptions, max_file_size),
+ sizeof(std::vector<uint64_t>)},
+ };
+
+ // For all memory used for options, pre-fill every char. Otherwise, the
+ // padding bytes might be different so that byte-wise comparison doesn't
+ // general equal results even if objects are equal.
+ const char kMySpecialChar = 'x';
+ char* mcfo1_ptr = new char[sizeof(MutableCFOptions)];
+ FillWithSpecialChar(mcfo1_ptr, sizeof(MutableCFOptions),
+ kMutableCFOptionsExcluded, kMySpecialChar);
+ char* mcfo2_ptr = new char[sizeof(MutableCFOptions)];
+ FillWithSpecialChar(mcfo2_ptr, sizeof(MutableCFOptions),
+ kMutableCFOptionsExcluded, kMySpecialChar);
+
+ // A clean column family options is constructed after filling the same special
+ // char as the initial one. So that the padding bytes are the same.
+ char* cfo_clean_ptr = new char[sizeof(ColumnFamilyOptions)];
+ FillWithSpecialChar(cfo_clean_ptr, sizeof(ColumnFamilyOptions),
+ kColumnFamilyOptionsExcluded);
+ rnd_filled_options.num_levels = 66;
+ ColumnFamilyOptions* cfo_clean = new (cfo_clean_ptr) ColumnFamilyOptions();
+
+ MutableCFOptions* mcfo1 =
+ new (mcfo1_ptr) MutableCFOptions(rnd_filled_options);
+ ColumnFamilyOptions cfo_back = BuildColumnFamilyOptions(*cfo_clean, *mcfo1);
+ MutableCFOptions* mcfo2 = new (mcfo2_ptr) MutableCFOptions(cfo_back);
+
+ ASSERT_TRUE(CompareBytes(mcfo1_ptr, mcfo2_ptr, sizeof(MutableCFOptions),
+ kMutableCFOptionsExcluded));
+
+ cfo_clean->~ColumnFamilyOptions();
+ mcfo1->~MutableCFOptions();
+ mcfo2->~MutableCFOptions();
+ delete[] mcfo1_ptr;
+ delete[] mcfo2_ptr;
+ delete[] cfo_clean_ptr;
+}
+#endif // !ROCKSDB_UBSAN_RUN
+#endif // !__clang__
+#endif // OS_LINUX || OS_WIN
+#endif // !ROCKSDB_LITE
+
+} // namespace ROCKSDB_NAMESPACE
+
+int main(int argc, char** argv) {
+ ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
+ ::testing::InitGoogleTest(&argc, argv);
+#ifdef GFLAGS
+ ParseCommandLineFlags(&argc, &argv, true);
+#endif // GFLAGS
+ return RUN_ALL_TESTS();
+}