// Copyright (c) 2017, Xiaomi, Inc. All rights reserved. // This source code is licensed under the Apache License Version 2.0, which // can be found in the LICENSE file in the root directory of this source tree. #include "pegasus_server_test_base.h" #include "server/pegasus_manual_compact_service.h" #include namespace pegasus { namespace server { class manual_compact_service_test : public pegasus_server_test_base { public: std::unique_ptr manual_compact_svc; static const uint64_t compacted_ts = 1500000000; // 2017.07.14 10:40:00 CST public: manual_compact_service_test() { start(); manual_compact_svc = dsn::make_unique(_server.get()); } void set_compact_time(int64_t ts) { manual_compact_svc->_manual_compact_last_finish_time_ms.store( static_cast(ts * 1000)); } void set_mock_now(uint64_t mock_now_sec) { manual_compact_svc->_mock_now_timestamp = mock_now_sec * 1000; } void check_compact_disabled(const std::map &envs, bool ok) { ASSERT_EQ(ok, manual_compact_svc->check_compact_disabled(envs)) << dsn::utils::kv_map_to_string(envs, ';', '='); } void check_once_compact(const std::map &envs, bool ok) { ASSERT_EQ(ok, manual_compact_svc->check_once_compact(envs)) << dsn::utils::kv_map_to_string(envs, ';', '='); } void check_periodic_compact(const std::map &envs, bool ok) { ASSERT_EQ(ok, manual_compact_svc->check_periodic_compact(envs)) << dsn::utils::kv_map_to_string(envs, ';', '='); } void extract_manual_compact_opts(const std::map &envs, const std::string &key_prefix, rocksdb::CompactRangeOptions &options) { manual_compact_svc->extract_manual_compact_opts(envs, key_prefix, options); } void set_num_level(int level) { _server->_data_cf_opts.num_levels = level; } void check_manual_compact_state(bool ok, const std::string &msg = "") { ASSERT_EQ(ok, manual_compact_svc->check_manual_compact_state()) << msg; } void manual_compact(uint64_t mock_now_sec, uint64_t time_cost_sec) { set_mock_now(mock_now_sec); uint64_t start = manual_compact_svc->now_timestamp(); manual_compact_svc->_manual_compact_start_running_time_ms.store(start); // do compacting... set_mock_now(mock_now_sec + time_cost_sec); uint64_t finish = manual_compact_svc->now_timestamp(); manual_compact_svc->_manual_compact_last_finish_time_ms.store(finish); manual_compact_svc->_manual_compact_last_time_used_ms.store(finish - start); manual_compact_svc->_manual_compact_enqueue_time_ms.store(0); } void set_manual_compact_interval(int sec) { manual_compact_svc->_manual_compact_min_interval_seconds = sec; } }; TEST_F(manual_compact_service_test, check_compact_disabled) { std::map envs; check_compact_disabled(envs, false); envs[MANUAL_COMPACT_DISABLED_KEY] = ""; check_compact_disabled(envs, false); envs[MANUAL_COMPACT_DISABLED_KEY] = "true"; check_compact_disabled(envs, true); envs[MANUAL_COMPACT_DISABLED_KEY] = "false"; check_compact_disabled(envs, false); envs[MANUAL_COMPACT_DISABLED_KEY] = "1"; check_compact_disabled(envs, false); envs[MANUAL_COMPACT_DISABLED_KEY] = "0"; check_compact_disabled(envs, false); envs[MANUAL_COMPACT_DISABLED_KEY] = "abc"; check_compact_disabled(envs, false); } TEST_F(manual_compact_service_test, check_once_compact) { // suppose compacted at 1500000000 set_compact_time(compacted_ts); // invalid trigger time std::map envs; check_once_compact(envs, false); envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = ""; check_once_compact(envs, false); envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = "abc"; check_once_compact(envs, false); envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = "-1"; check_once_compact(envs, false); // has been compacted envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = std::to_string(compacted_ts - 1); check_once_compact(envs, false); envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = std::to_string(compacted_ts); check_once_compact(envs, false); // has not been compacted envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = std::to_string(compacted_ts + 1); check_once_compact(envs, true); envs[MANUAL_COMPACT_ONCE_TRIGGER_TIME_KEY] = std::to_string(dsn_now_ms() / 1000); check_once_compact(envs, true); } TEST_F(manual_compact_service_test, check_periodic_compact) { std::map envs; // invalid trigger time format check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = ""; check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = ","; check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "12:oo"; check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = std::to_string(compacted_ts); check_periodic_compact(envs, false); // suppose compacted at 10:00 set_compact_time(dsn::utils::hh_mm_today_to_unix_sec("10:00")); // has been compacted envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "9:00"; check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "3:00,9:00"; check_periodic_compact(envs, false); envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "10:00"; check_periodic_compact(envs, false); // suppose compacted at 09:00 set_compact_time(dsn::utils::hh_mm_today_to_unix_sec("09:00")); // single compact time envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "10:00"; set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("08:00")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("09:30")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("10:30")); check_periodic_compact(envs, true); // multiple compact time envs[MANUAL_COMPACT_PERIODIC_TRIGGER_TIME_KEY] = "10:00,21:00"; set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("08:00")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("09:30")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("10:30")); check_periodic_compact(envs, true); // suppose compacted at 11:00 set_compact_time(dsn::utils::hh_mm_today_to_unix_sec("11:00")); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("11:01")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("20:30")); check_periodic_compact(envs, false); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("21:01")); check_periodic_compact(envs, true); // suppose compacted at 21:50 set_compact_time(dsn::utils::hh_mm_today_to_unix_sec("21:50")); set_mock_now((uint64_t)dsn::utils::hh_mm_today_to_unix_sec("22:00")); check_periodic_compact(envs, false); } TEST_F(manual_compact_service_test, extract_manual_compact_opts) { // init _db max level set_num_level(7); std::map envs; rocksdb::CompactRangeOptions out; extract_manual_compact_opts(envs, MANUAL_COMPACT_ONCE_KEY_PREFIX, out); ASSERT_EQ(out.target_level, -1); ASSERT_EQ(out.bottommost_level_compaction, rocksdb::BottommostLevelCompaction::kSkip); envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_TARGET_LEVEL_KEY] = "2"; envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_BOTTOMMOST_LEVEL_COMPACTION_KEY] = MANUAL_COMPACT_BOTTOMMOST_LEVEL_COMPACTION_FORCE; extract_manual_compact_opts(envs, MANUAL_COMPACT_ONCE_KEY_PREFIX, out); ASSERT_EQ(out.target_level, 2); ASSERT_EQ(out.bottommost_level_compaction, rocksdb::BottommostLevelCompaction::kForce); envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_TARGET_LEVEL_KEY] = "-1"; envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_BOTTOMMOST_LEVEL_COMPACTION_KEY] = MANUAL_COMPACT_BOTTOMMOST_LEVEL_COMPACTION_SKIP; extract_manual_compact_opts(envs, MANUAL_COMPACT_ONCE_KEY_PREFIX, out); ASSERT_EQ(out.target_level, -1); ASSERT_EQ(out.bottommost_level_compaction, rocksdb::BottommostLevelCompaction::kSkip); envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_TARGET_LEVEL_KEY] = "-2"; envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_BOTTOMMOST_LEVEL_COMPACTION_KEY] = "nonono"; extract_manual_compact_opts(envs, MANUAL_COMPACT_ONCE_KEY_PREFIX, out); ASSERT_EQ(out.target_level, -1); ASSERT_EQ(out.bottommost_level_compaction, rocksdb::BottommostLevelCompaction::kSkip); envs[MANUAL_COMPACT_ONCE_KEY_PREFIX + MANUAL_COMPACT_TARGET_LEVEL_KEY] = "8"; extract_manual_compact_opts(envs, MANUAL_COMPACT_ONCE_KEY_PREFIX, out); ASSERT_EQ(out.target_level, -1); } TEST_F(manual_compact_service_test, check_manual_compact_state_0_interval) { set_manual_compact_interval(0); uint64_t first_time = 1500000000; set_mock_now(first_time); check_manual_compact_state(true, "1st start ok"); check_manual_compact_state(false, "1st start not ok"); manual_compact(first_time, 1); check_manual_compact_state(true, "2nd start ok"); check_manual_compact_state(false, "2nd start not ok"); } TEST_F(manual_compact_service_test, check_manual_compact_state_1h_interval) { set_manual_compact_interval(3600); uint64_t first_time = 1500000000; set_mock_now(first_time); check_manual_compact_state(true, "1st start ok"); check_manual_compact_state(false, "1st start not ok"); manual_compact(first_time, 10); // cost 10 seconds set_mock_now(first_time + 1800); check_manual_compact_state(false, "1800s past"); set_mock_now(first_time + 3609); check_manual_compact_state(false, "3609s past"); set_mock_now(first_time + 3610); check_manual_compact_state(false, "3610s past"); set_mock_now(first_time + 3611); check_manual_compact_state(true, "3611s past, start ok"); check_manual_compact_state(false, "3611s past, start not ok"); } } // namespace server } // namespace pegasus