/* * Copyright (C) 2017 ScyllaDB */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #include #include "seastarx.hh" #include "tests/simple_schema.hh" #include "tests/cql_test_env.hh" #include "core/app-template.hh" #include "database.hh" #include "db/config.hh" #include "partition_slice_builder.hh" #include "utils/int_range.hh" #include "utils/div_ceil.hh" #include #include "disk-error-handler.hh" logging::logger test_log("test"); thread_local disk_error_signal_type commit_error; thread_local disk_error_signal_type general_disk_error; static thread_local bool cancelled = false; using namespace std::chrono_literals; template class monotonic_counter { std::function _getter; T _prev; public: monotonic_counter(std::function getter) : _getter(std::move(getter)) { _prev = _getter(); } // Return change in value since the last call to change() or rate(). auto change() { auto now = _getter(); return now - std::exchange(_prev, now); } }; int main(int argc, char** argv) { namespace bpo = boost::program_options; app_template app; app.add_options() ("trace", "Enables trace-level logging for the test actions") ("no-reads", "Disable reads during the test") ("seconds", bpo::value()->default_value(60), "Duration [s] after which the test terminates with a success") ; return app.run(argc, argv, [&app] { if (app.configuration().count("trace")) { test_log.set_level(seastar::log_level::trace); } db::config cfg; cfg.enable_commitlog = false; cfg.enable_cache = true; return do_with_cql_env_thread([&app] (cql_test_env& env) { auto reads_enabled = !app.configuration().count("no-reads"); auto seconds = app.configuration()["seconds"].as(); simple_schema ss; engine().at_exit([] { cancelled = true; return make_ready_future(); }); timer<> completion_timer; completion_timer.set_callback([&] { test_log.info("Test done."); cancelled = true; }); completion_timer.arm(std::chrono::seconds(seconds)); env.execute_cql(ss.cql()).get(); database& db = env.local_db(); auto s = db.find_schema(ss.schema()->ks_name(), ss.schema()->cf_name()); column_family& cf = db.find_column_family(s->id()); cf.set_compaction_strategy(sstables::compaction_strategy_type::null); uint64_t mutations = 0; uint64_t reads = 0; utils::estimated_histogram reads_hist; utils::estimated_histogram writes_hist; timer<> stats_printer; monotonic_counter reads_ctr([&] { return reads; }); monotonic_counter mutations_ctr([&] { return mutations; }); monotonic_counter eviction_ctr([&] { return global_cache_tracker().get_stats().partition_evictions; }); monotonic_counter pmerges_ctr([&] { return global_cache_tracker().get_stats().partition_merges; }); monotonic_counter miss_ctr([&] { return global_cache_tracker().get_stats().reads_with_misses; }); stats_printer.set_callback([&] { auto MB = 1024 * 1024; std::cout << sprint("rd/s: %d, wr/s: %d, ev/s: %d, pmerge/s: %d, miss/s: %d, cache: %d/%d [MB], LSA: %d/%d [MB], std free: %d [MB]", reads_ctr.change(), mutations_ctr.change(), eviction_ctr.change(), pmerges_ctr.change(), miss_ctr.change(), global_cache_tracker().region().occupancy().used_space() / MB, global_cache_tracker().region().occupancy().total_space() / MB, logalloc::shard_tracker().region_occupancy().used_space() / MB, logalloc::shard_tracker().region_occupancy().total_space() / MB, seastar::memory::stats().free_memory() / MB) << "\n\n"; auto print_percentiles = [] (const utils::estimated_histogram& hist) { return sprint("min: %-6d, 50%%: %-6d, 90%%: %-6d, 99%%: %-6d, 99.9%%: %-6d, max: %-6d [us]", hist.percentile(0), hist.percentile(0.5), hist.percentile(0.9), hist.percentile(0.99), hist.percentile(0.999), hist.percentile(1.0) ); }; std::cout << "reads : " << print_percentiles(reads_hist) << "\n"; std::cout << "writes: " << print_percentiles(writes_hist) << "\n"; std::cout << "\n"; reads_hist.clear(); writes_hist.clear(); }); stats_printer.arm_periodic(1s); auto pkey = ss.make_pkey("key1"); sstring value = sstring(sstring::initialized_later(), 1024); using clock = std::chrono::steady_clock; auto reader = seastar::async([&] { if (!reads_enabled) { return; } auto id = env.prepare("select * from ks.cf where pk = 'key1' limit 10;").get0(); while (!cancelled) { auto t0 = clock::now(); env.execute_prepared(id, {}).get(); reads_hist.add(std::chrono::duration_cast(clock::now() - t0).count()); ++reads; sleep(100ms).get(); // So that reads don't overwhelm the CPU } }); auto mutator = seastar::async([&] { uint32_t ckey_seq = 0; while (!cancelled) { mutation m(pkey, s); ss.add_row(m, ss.make_ckey(ckey_seq++), value); auto t0 = clock::now(); db.apply(s, freeze(m)).get(); writes_hist.add(std::chrono::duration_cast(clock::now() - t0).count()); ++mutations; } }); mutator.get(); reader.get(); stats_printer.cancel(); completion_timer.cancel(); }, cfg); }); }