mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-20 08:30:35 +00:00
This applies to small partition workload where index pages have high partition count, and the index doesn't fit in cache. It was observed that the count can be in the order of hundreds. In such a workload pages undergo constant population, LSA compaction, and LSA eviction, which has severe impact on CPU utilization.
Refs https://scylladb.atlassian.net/browse/SCYLLADB-620
This PR reduces the impact by several changes:
- reducing memory footprint in the partition index. Assuming partition key size is 16 bytes, the cost dropped from 96 bytes to 36 bytes per partition.
- flattening the object graph and amortizing storage. Storing entries directly in the vector. Storing all key values in a single managed_bytes. Making index_entry a trivial struct.
- index entries and key storage are now trivially moveable, and batched inside vector storage
so LSA migration can use memcpy(), which amortizes the cost per key. This reduces the cost of LSA segment compaction.
- LSA eviction is now pretty much constant time for the whole page
regardless of the number of entries, because elements are trivial and batched inside vectors.
Page eviction cost dropped from 50 us to 1 us.
Performance evaluated with:
scylla perf-simple-query -c1 -m200M --partitions=1000000
Before:
```
7774.96 tps (166.0 allocs/op, 521.7 logallocs/op, 54.0 tasks/op, 802428 insns/op, 430457 cycles/op, 0 errors)
7511.08 tps (166.1 allocs/op, 527.2 logallocs/op, 54.0 tasks/op, 804185 insns/op, 430752 cycles/op, 0 errors)
7740.44 tps (166.3 allocs/op, 526.2 logallocs/op, 54.2 tasks/op, 805347 insns/op, 432117 cycles/op, 0 errors)
7818.72 tps (165.2 allocs/op, 517.6 logallocs/op, 53.7 tasks/op, 794965 insns/op, 427751 cycles/op, 0 errors)
7865.49 tps (165.1 allocs/op, 513.3 logallocs/op, 53.6 tasks/op, 788898 insns/op, 425171 cycles/op, 0 errors)
```
After (+318%):
```
32492.40 tps (130.7 allocs/op, 12.8 logallocs/op, 36.1 tasks/op, 109236 insns/op, 103203 cycles/op, 0 errors)
32591.99 tps (130.4 allocs/op, 12.8 logallocs/op, 36.0 tasks/op, 108947 insns/op, 102889 cycles/op, 0 errors)
32514.52 tps (130.6 allocs/op, 12.8 logallocs/op, 36.0 tasks/op, 109118 insns/op, 103219 cycles/op, 0 errors)
32491.14 tps (130.6 allocs/op, 12.8 logallocs/op, 36.0 tasks/op, 109349 insns/op, 103272 cycles/op, 0 errors)
32582.90 tps (130.5 allocs/op, 12.8 logallocs/op, 36.0 tasks/op, 109269 insns/op, 102872 cycles/op, 0 errors)
32479.43 tps (130.6 allocs/op, 12.8 logallocs/op, 36.0 tasks/op, 109313 insns/op, 103242 cycles/op, 0 errors)
32418.48 tps (130.7 allocs/op, 12.8 logallocs/op, 36.1 tasks/op, 109201 insns/op, 103301 cycles/op, 0 errors)
31394.14 tps (130.7 allocs/op, 12.8 logallocs/op, 36.1 tasks/op, 109267 insns/op, 103301 cycles/op, 0 errors)
32298.55 tps (130.7 allocs/op, 12.8 logallocs/op, 36.1 tasks/op, 109323 insns/op, 103551 cycles/op, 0 errors)
```
When the workload is miss-only, with both row cache and index cache disabled (no cache maintenance cost):
perf-simple-query -c1 -m200M --duration 6000 --partitions=100000 --enable-index-cache=0 --enable-cache=0
Before:
```
9124.57 tps (146.2 allocs/op, 789.0 logallocs/op, 45.3 tasks/op, 889320 insns/op, 357937 cycles/op, 0 errors)
9437.23 tps (146.1 allocs/op, 789.3 logallocs/op, 45.3 tasks/op, 889613 insns/op, 357782 cycles/op, 0 errors)
9455.65 tps (146.0 allocs/op, 787.4 logallocs/op, 45.2 tasks/op, 887606 insns/op, 357167 cycles/op, 0 errors)
9451.22 tps (146.0 allocs/op, 787.4 logallocs/op, 45.3 tasks/op, 887627 insns/op, 357357 cycles/op, 0 errors)
9429.50 tps (146.0 allocs/op, 787.4 logallocs/op, 45.3 tasks/op, 887761 insns/op, 358148 cycles/op, 0 errors)
9430.29 tps (146.1 allocs/op, 788.2 logallocs/op, 45.3 tasks/op, 888501 insns/op, 357679 cycles/op, 0 errors)
9454.08 tps (146.0 allocs/op, 787.3 logallocs/op, 45.3 tasks/op, 887545 insns/op, 357132 cycles/op, 0 errors)
```
After (+55%):
```
14484.84 tps (150.7 allocs/op, 6.5 logallocs/op, 44.7 tasks/op, 396164 insns/op, 229490 cycles/op, 0 errors)
14526.21 tps (150.8 allocs/op, 6.5 logallocs/op, 44.8 tasks/op, 396401 insns/op, 228824 cycles/op, 0 errors)
14567.53 tps (150.7 allocs/op, 6.5 logallocs/op, 44.7 tasks/op, 396319 insns/op, 228701 cycles/op, 0 errors)
14545.63 tps (150.6 allocs/op, 6.5 logallocs/op, 44.7 tasks/op, 395889 insns/op, 228493 cycles/op, 0 errors)
14626.06 tps (150.5 allocs/op, 6.5 logallocs/op, 44.7 tasks/op, 395254 insns/op, 227891 cycles/op, 0 errors)
14593.74 tps (150.5 allocs/op, 6.5 logallocs/op, 44.7 tasks/op, 395480 insns/op, 227993 cycles/op, 0 errors)
14538.10 tps (150.8 allocs/op, 6.5 logallocs/op, 44.8 tasks/op, 397035 insns/op, 228831 cycles/op, 0 errors)
14527.18 tps (150.8 allocs/op, 6.5 logallocs/op, 44.8 tasks/op, 396992 insns/op, 228839 cycles/op, 0 errors)
```
Same as above, but with summary ratio increased from 0.0005 to 0.005 (smaller pages):
Before:
```
33906.70 tps (146.1 allocs/op, 83.6 logallocs/op, 45.1 tasks/op, 170553 insns/op, 98104 cycles/op, 0 errors)
32696.16 tps (146.0 allocs/op, 83.5 logallocs/op, 45.1 tasks/op, 170369 insns/op, 98405 cycles/op, 0 errors)
33889.05 tps (146.1 allocs/op, 83.6 logallocs/op, 45.1 tasks/op, 170551 insns/op, 98135 cycles/op, 0 errors)
33893.24 tps (146.1 allocs/op, 83.5 logallocs/op, 45.1 tasks/op, 170488 insns/op, 98168 cycles/op, 0 errors)
33836.73 tps (146.1 allocs/op, 83.6 logallocs/op, 45.1 tasks/op, 170528 insns/op, 98226 cycles/op, 0 errors)
33897.61 tps (146.0 allocs/op, 83.5 logallocs/op, 45.1 tasks/op, 170428 insns/op, 98081 cycles/op, 0 errors)
33834.73 tps (146.1 allocs/op, 83.5 logallocs/op, 45.1 tasks/op, 170438 insns/op, 98178 cycles/op, 0 errors)
33776.31 tps (146.3 allocs/op, 83.9 logallocs/op, 45.2 tasks/op, 170958 insns/op, 98418 cycles/op, 0 errors)
33808.08 tps (146.3 allocs/op, 83.9 logallocs/op, 45.2 tasks/op, 170940 insns/op, 98388 cycles/op, 0 errors)
```
After (+18%):
```
40081.51 tps (148.2 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 121047 insns/op, 82231 cycles/op, 0 errors)
40005.85 tps (148.6 allocs/op, 4.4 logallocs/op, 45.2 tasks/op, 121327 insns/op, 82545 cycles/op, 0 errors)
39816.75 tps (148.3 allocs/op, 4.4 logallocs/op, 45.1 tasks/op, 121067 insns/op, 82419 cycles/op, 0 errors)
39953.11 tps (148.1 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 121027 insns/op, 82258 cycles/op, 0 errors)
40073.96 tps (148.2 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 121006 insns/op, 82313 cycles/op, 0 errors)
39882.25 tps (148.2 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 120925 insns/op, 82320 cycles/op, 0 errors)
39916.08 tps (148.3 allocs/op, 4.4 logallocs/op, 45.1 tasks/op, 121054 insns/op, 82393 cycles/op, 0 errors)
39786.30 tps (148.2 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 121027 insns/op, 82465 cycles/op, 0 errors)
38662.45 tps (148.3 allocs/op, 4.4 logallocs/op, 45.0 tasks/op, 121108 insns/op, 82312 cycles/op, 0 errors)
39849.42 tps (148.3 allocs/op, 4.4 logallocs/op, 45.1 tasks/op, 121098 insns/op, 82447 cycles/op, 0 errors)
```
Closes scylladb/scylladb#28603
* github.com:scylladb/scylladb:
sstables: mx: index_reader: Optimize parsing for no promoted index case
vint: Use std::countl_zero()
test: sstable_partition_index_cache_test: Validate scenario of pages with sparse promoted index placement
sstables: mx: index_reader: Amoritze partition key storage
managed_bytes: Hoist write_fragmented() to common header
utils: managed_vector: Use std::uninitialized_move() to move objects
sstables: mx: index_reader: Keep promoted_index info next to index_entry
sstables: mx: index_reader: Extract partition_index_page::clear_gently()
sstables: mx: index_reader: Shave-off 16 bytes from index_entry by using raw_token
sstables: mx: index_reader: Reduce allocation_section overhead during index page parsing by batching allocation
sstables: mx: index_reader: Keep index_entry directly in the vector
dht: Introduce raw_token
test: perf_simple_query: Add 'sstable-format' command-line option
test: perf_simple_query: Add 'sstable-summary-ratio' command-line option
test: perf-simple-query: Add option to disable index cache
test: cql_test_env: Respect enable-index-cache config
(cherry picked from commit 5e7fb08bf3)
Closes scylladb/scylladb#29136
Closes scylladb/scylladb#29140
431 lines
19 KiB
C++
431 lines
19 KiB
C++
/*
|
|
* Copyright (C) 2015-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#include "utils/assert.hh"
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include <json/json.h>
|
|
#include <fmt/ranges.h>
|
|
|
|
#include "test/lib/cql_test_env.hh"
|
|
#include "test/perf/perf.hh"
|
|
#include <seastar/core/app-template.hh>
|
|
#include <seastar/testing/test_runner.hh>
|
|
#include "test/lib/random_utils.hh"
|
|
#include "db/config.hh"
|
|
|
|
#include "db/config.hh"
|
|
#include "schema/schema_builder.hh"
|
|
#include "release.hh"
|
|
#include <fstream>
|
|
#include "service/storage_proxy.hh"
|
|
#include "cql3/query_processor.hh"
|
|
#include "db/config.hh"
|
|
#include "db/extensions.hh"
|
|
#include "db/tags/extension.hh"
|
|
#include "gms/gossiper.hh"
|
|
|
|
static const sstring table_name = "cf";
|
|
|
|
static bytes make_key(uint64_t sequence) {
|
|
bytes b(bytes::initialized_later(), sizeof(sequence));
|
|
auto i = b.begin();
|
|
write<uint64_t>(i, sequence);
|
|
return b;
|
|
};
|
|
|
|
static void execute_update_for_key(cql_test_env& env, const bytes& key) {
|
|
env.execute_cql(fmt::format("UPDATE cf SET "
|
|
"\"C0\" = 0x8f75da6b3dcec90c8a404fb9a5f6b0621e62d39c69ba5758e5f41b78311fbb26cc7a,"
|
|
"\"C1\" = 0xa8761a2127160003033a8f4f3d1069b7833ebe24ef56b3beee728c2b686ca516fa51,"
|
|
"\"C2\" = 0x583449ce81bfebc2e1a695eb59aad5fcc74d6d7311fc6197b10693e1a161ca2e1c64,"
|
|
"\"C3\" = 0x62bcb1dbc0ff953abc703bcb63ea954f437064c0c45366799658bd6b91d0f92908d7,"
|
|
"\"C4\" = 0x222fcbe31ffa1e689540e1499b87fa3f9c781065fccd10e4772b4c7039c2efd0fb27 "
|
|
"WHERE \"KEY\"= 0x{};", to_hex(key))).get();
|
|
};
|
|
|
|
static void execute_counter_update_for_key(cql_test_env& env, const bytes& key) {
|
|
env.execute_cql(fmt::format("UPDATE cf SET "
|
|
"\"C0\" = \"C0\" + 1,"
|
|
"\"C1\" = \"C1\" + 2,"
|
|
"\"C2\" = \"C2\" + 3,"
|
|
"\"C3\" = \"C3\" + 4,"
|
|
"\"C4\" = \"C4\" + 5 "
|
|
"WHERE \"KEY\"= 0x{};", to_hex(key))).get();
|
|
};
|
|
|
|
struct test_config {
|
|
enum class run_mode { read, write, del };
|
|
run_mode mode;
|
|
unsigned partitions;
|
|
unsigned concurrency;
|
|
bool query_single_key;
|
|
unsigned duration_in_seconds;
|
|
bool counters;
|
|
bool flush_memtables;
|
|
unsigned memtable_partitions = 0;
|
|
unsigned operations_per_shard = 0;
|
|
bool stop_on_error;
|
|
sstring timeout;
|
|
bool bypass_cache;
|
|
std::optional<unsigned> initial_tablets;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const test_config::run_mode& m) {
|
|
switch (m) {
|
|
case test_config::run_mode::write: return os << "write";
|
|
case test_config::run_mode::read: return os << "read";
|
|
case test_config::run_mode::del: return os << "delete";
|
|
}
|
|
abort();
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const test_config& cfg) {
|
|
return os << "{partitions=" << cfg.partitions
|
|
<< ", concurrency=" << cfg.concurrency
|
|
<< ", mode=" << cfg.mode
|
|
<< ", query_single_key=" << (cfg.query_single_key ? "yes" : "no")
|
|
<< ", counters=" << (cfg.counters ? "yes" : "no")
|
|
<< "}";
|
|
}
|
|
|
|
static void create_partitions(cql_test_env& env, test_config& cfg) {
|
|
std::cout << "Creating " << cfg.partitions << " partitions..." << std::endl;
|
|
unsigned next_flush = (cfg.memtable_partitions > 0 ? cfg.memtable_partitions : cfg.partitions);
|
|
for (unsigned sequence = 0; sequence < cfg.partitions; ++sequence) {
|
|
if (cfg.counters) {
|
|
execute_counter_update_for_key(env, make_key(sequence));
|
|
} else {
|
|
execute_update_for_key(env, make_key(sequence));
|
|
}
|
|
if (sequence + 1 >= next_flush) {
|
|
env.db().invoke_on_all(&replica::database::flush_all_memtables).get();
|
|
next_flush += cfg.memtable_partitions;
|
|
}
|
|
}
|
|
|
|
if (cfg.flush_memtables) {
|
|
std::cout << "Flushing partitions..." << std::endl;
|
|
env.db().invoke_on_all(&replica::database::flush_all_memtables).get();
|
|
}
|
|
}
|
|
|
|
static int64_t make_random_seq(test_config& cfg) {
|
|
return cfg.query_single_key ? 0 : tests::random::get_int<uint64_t>(cfg.partitions - 1);
|
|
}
|
|
|
|
static bytes make_random_key(test_config& cfg) {
|
|
return make_key(make_random_seq(cfg));
|
|
}
|
|
|
|
static std::vector<perf_result> test_read(cql_test_env& env, test_config& cfg) {
|
|
create_partitions(env, cfg);
|
|
sstring query = "select \"C0\", \"C1\", \"C2\", \"C3\", \"C4\" from cf where \"KEY\" = ?";
|
|
if (cfg.bypass_cache) {
|
|
query += " bypass cache";
|
|
}
|
|
if (!cfg.timeout.empty()) {
|
|
query += " using timeout " + cfg.timeout;
|
|
}
|
|
auto id = env.prepare(query).get();
|
|
return time_parallel([&env, &cfg, id] {
|
|
bytes key = make_random_key(cfg);
|
|
return env.execute_prepared(id, {{cql3::raw_value::make_value(std::move(key))}}).discard_result();
|
|
}, cfg.concurrency, cfg.duration_in_seconds, cfg.operations_per_shard, cfg.stop_on_error);
|
|
}
|
|
|
|
static std::vector<perf_result> test_write(cql_test_env& env, test_config& cfg) {
|
|
sstring usings;
|
|
if (!cfg.timeout.empty()) {
|
|
usings += "USING TIMEOUT " + cfg.timeout;
|
|
}
|
|
sstring query = format("UPDATE cf {}SET "
|
|
"\"C0\" = 0x8f75da6b3dcec90c8a404fb9a5f6b0621e62d39c69ba5758e5f41b78311fbb26cc7a,"
|
|
"\"C1\" = 0xa8761a2127160003033a8f4f3d1069b7833ebe24ef56b3beee728c2b686ca516fa51,"
|
|
"\"C2\" = 0x583449ce81bfebc2e1a695eb59aad5fcc74d6d7311fc6197b10693e1a161ca2e1c64,"
|
|
"\"C3\" = 0x62bcb1dbc0ff953abc703bcb63ea954f437064c0c45366799658bd6b91d0f92908d7,"
|
|
"\"C4\" = 0x222fcbe31ffa1e689540e1499b87fa3f9c781065fccd10e4772b4c7039c2efd0fb27 "
|
|
"WHERE \"KEY\" = ?", usings);
|
|
auto id = env.prepare(query).get();
|
|
return time_parallel([&env, &cfg, id] {
|
|
bytes key = make_random_key(cfg);
|
|
return env.execute_prepared(id, {{cql3::raw_value::make_value(std::move(key))}}).discard_result();
|
|
}, cfg.concurrency, cfg.duration_in_seconds, cfg.operations_per_shard, cfg.stop_on_error);
|
|
}
|
|
|
|
static std::vector<perf_result> test_delete(cql_test_env& env, test_config& cfg) {
|
|
create_partitions(env, cfg);
|
|
sstring usings;
|
|
if (!cfg.timeout.empty()) {
|
|
usings += "USING TIMEOUT " + cfg.timeout;
|
|
}
|
|
sstring query = format("DELETE \"C0\", \"C1\", \"C2\", \"C3\", \"C4\" FROM cf {}WHERE \"KEY\" = ?", usings);
|
|
auto id = env.prepare(query).get();
|
|
return time_parallel([&env, &cfg, id] {
|
|
bytes key = make_random_key(cfg);
|
|
return env.execute_prepared(id, {{cql3::raw_value::make_value(std::move(key))}}).discard_result();
|
|
}, cfg.concurrency, cfg.duration_in_seconds, cfg.operations_per_shard, cfg.stop_on_error);
|
|
}
|
|
|
|
static std::vector<perf_result> test_counter_update(cql_test_env& env, test_config& cfg) {
|
|
sstring usings;
|
|
if (!cfg.timeout.empty()) {
|
|
usings += "USING TIMEOUT " + cfg.timeout;
|
|
}
|
|
sstring query = format("UPDATE cf {}SET "
|
|
"\"C0\" = \"C0\" + 1,"
|
|
"\"C1\" = \"C1\" + 2,"
|
|
"\"C2\" = \"C2\" + 3,"
|
|
"\"C3\" = \"C3\" + 4,"
|
|
"\"C4\" = \"C4\" + 5 "
|
|
"WHERE \"KEY\" = ?", usings);
|
|
auto id = env.prepare(query).get();
|
|
return time_parallel([&env, &cfg, id] {
|
|
bytes key = make_random_key(cfg);
|
|
return env.execute_prepared(id, {{cql3::raw_value::make_value(std::move(key))}}).discard_result();
|
|
}, cfg.concurrency, cfg.duration_in_seconds, cfg.operations_per_shard, cfg.stop_on_error);
|
|
}
|
|
|
|
static schema_ptr make_counter_schema(std::string_view ks_name) {
|
|
return schema_builder(ks_name, "cf")
|
|
.with_column("KEY", bytes_type, column_kind::partition_key)
|
|
.with_column("C0", counter_type)
|
|
.with_column("C1", counter_type)
|
|
.with_column("C2", counter_type)
|
|
.with_column("C3", counter_type)
|
|
.with_column("C4", counter_type)
|
|
.build();
|
|
}
|
|
|
|
static std::vector<perf_result> do_cql_test(cql_test_env& env, test_config& cfg) {
|
|
std::cout << "Running test with config: " << cfg << std::endl;
|
|
env.create_table([&cfg] (auto ks_name) {
|
|
if (cfg.counters) {
|
|
return *make_counter_schema(ks_name);
|
|
}
|
|
return *schema_builder(ks_name, "cf")
|
|
.with_column("KEY", bytes_type, column_kind::partition_key)
|
|
.with_column("C0", bytes_type)
|
|
.with_column("C1", bytes_type)
|
|
.with_column("C2", bytes_type)
|
|
.with_column("C3", bytes_type)
|
|
.with_column("C4", bytes_type)
|
|
.build();
|
|
}).get();
|
|
|
|
std::cout << "Disabling auto compaction" << std::endl;
|
|
env.db().invoke_on_all([] (auto& db) {
|
|
auto& cf = db.find_column_family("ks", "cf");
|
|
return cf.disable_auto_compaction();
|
|
}).get();
|
|
|
|
switch (cfg.mode) {
|
|
case test_config::run_mode::read:
|
|
return test_read(env, cfg);
|
|
case test_config::run_mode::write:
|
|
if (cfg.counters) {
|
|
return test_counter_update(env, cfg);
|
|
} else {
|
|
return test_write(env, cfg);
|
|
}
|
|
case test_config::run_mode::del:
|
|
return test_delete(env, cfg);
|
|
};
|
|
abort();
|
|
}
|
|
|
|
void write_json_result(std::string result_file, const test_config& cfg, const aggregated_perf_results& agg) {
|
|
Json::Value results;
|
|
|
|
Json::Value params;
|
|
params["concurrency"] = cfg.concurrency;
|
|
params["partitions"] = cfg.partitions;
|
|
params["cpus"] = smp::count;
|
|
params["duration"] = cfg.duration_in_seconds;
|
|
params["concurrency,partitions,cpus,duration"] = fmt::format("{},{},{},{}", cfg.concurrency, cfg.partitions, smp::count, cfg.duration_in_seconds);
|
|
if (cfg.initial_tablets) {
|
|
params["initial_tablets"] = cfg.initial_tablets.value();
|
|
}
|
|
results["parameters"] = std::move(params);
|
|
|
|
Json::Value stats;
|
|
auto med = agg.median_by_throughput;
|
|
stats["median tps"] = med.throughput;
|
|
stats["allocs_per_op"] = med.mallocs_per_op;
|
|
stats["logallocs_per_op"] = med.logallocs_per_op;
|
|
stats["tasks_per_op"] = med.tasks_per_op;
|
|
stats["instructions_per_op"] = med.instructions_per_op;
|
|
stats["cpu_cycles_per_op"] = med.cpu_cycles_per_op;
|
|
const auto& tps = agg.stats.at("throughput");
|
|
stats["mad tps"] = tps.median_absolute_deviation;
|
|
stats["max tps"] = tps.max;
|
|
stats["min tps"] = tps.min;
|
|
results["stats"] = std::move(stats);
|
|
|
|
std::string test_type;
|
|
switch (cfg.mode) {
|
|
case test_config::run_mode::read: test_type = "read"; break;
|
|
case test_config::run_mode::write: test_type = "write"; break;
|
|
case test_config::run_mode::del: test_type = "delete"; break;
|
|
}
|
|
if (cfg.counters) {
|
|
test_type += "_counters";
|
|
}
|
|
results["test_properties"]["type"] = test_type;
|
|
|
|
// <version>-<release>
|
|
auto version_components = std::vector<std::string>{};
|
|
auto sver = scylla_version();
|
|
boost::algorithm::split(version_components, sver, boost::is_any_of("-"));
|
|
// <scylla-build>.<date>.<git-hash>
|
|
auto release_components = std::vector<std::string>{};
|
|
boost::algorithm::split(release_components, version_components[1], boost::is_any_of("."));
|
|
|
|
Json::Value version;
|
|
version["commit_id"] = release_components[2];
|
|
version["date"] = release_components[1];
|
|
version["version"] = version_components[0];
|
|
|
|
// It'd be nice to have std::chrono::format(), wouldn't it?
|
|
auto current_time = std::time(nullptr);
|
|
char time_str[100];
|
|
::tm time_buf;
|
|
std::strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", ::localtime_r(¤t_time, &time_buf));
|
|
version["run_date_time"] = time_str;
|
|
|
|
results["versions"]["scylla-server"] = std::move(version);
|
|
|
|
auto out = std::ofstream(result_file);
|
|
out << results;
|
|
}
|
|
|
|
/// If app configuration contains the named parameter, store its value into \p store.
|
|
static void set_from_cli(const char* name, app_template& app, utils::config_file::named_value<sstring>& store) {
|
|
const auto& cfg = app.configuration();
|
|
auto found = cfg.find(name);
|
|
if (found != cfg.end()) {
|
|
store(found->second.as<std::string>());
|
|
}
|
|
}
|
|
|
|
namespace perf {
|
|
|
|
int scylla_simple_query_main(int argc, char** argv) {
|
|
namespace bpo = boost::program_options;
|
|
app_template app;
|
|
app.add_options()
|
|
("random-seed", boost::program_options::value<unsigned>(), "Random number generator seed")
|
|
("partitions", bpo::value<unsigned>()->default_value(10000), "number of partitions")
|
|
("write", "test write path instead of read path")
|
|
("delete", "test delete path instead of read path")
|
|
("duration", bpo::value<unsigned>()->default_value(5), "test duration in seconds")
|
|
("query-single-key", "test reading with a single key instead of random keys")
|
|
("concurrency", bpo::value<unsigned>()->default_value(100), "workers per core")
|
|
("operations-per-shard", bpo::value<unsigned>(), "run this many operations per shard (overrides duration)")
|
|
("counters", "test counters")
|
|
("tablets", "use tablets")
|
|
("initial-tablets", bpo::value<unsigned>()->default_value(128), "initial number of tablets")
|
|
("sstable-summary-ratio", bpo::value<double>(), "Generate summary entry, so that summary file size / data file size ~= this ratio")
|
|
("sstable-format", bpo::value<std::string>(), "SSTable format name to use")
|
|
("flush", "flush memtables before test")
|
|
("memtable-partitions", bpo::value<unsigned>(), "apply this number of partitions to memtable, then flush")
|
|
("json-result", bpo::value<std::string>(), "name of the json result file")
|
|
("enable-cache", bpo::value<bool>()->default_value(true), "enable row cache")
|
|
("enable-index-cache", bpo::value<bool>()->default_value(true), "enable partition index cache")
|
|
("stop-on-error", bpo::value<bool>()->default_value(true), "stop after encountering the first error")
|
|
("timeout", bpo::value<std::string>()->default_value(""), "use timeout")
|
|
("bypass-cache", "use bypass cache when querying")
|
|
("audit", bpo::value<std::string>(), "value for audit config entry")
|
|
("audit-keyspaces", bpo::value<std::string>(), "value for audit_keyspaces config entry")
|
|
("audit-tables", bpo::value<std::string>(), "value for audit_tables config entry")
|
|
("audit-categories", bpo::value<std::string>(), "value for audit_categories config entry")
|
|
;
|
|
|
|
set_abort_on_internal_error(true);
|
|
|
|
return app.run(argc, argv, [&app] {
|
|
auto conf_seed = app.configuration()["random-seed"];
|
|
auto seed = conf_seed.empty() ? std::random_device()() : conf_seed.as<unsigned>();
|
|
std::cout << "random-seed=" << seed << '\n';
|
|
return smp::invoke_on_all([seed] {
|
|
seastar::testing::local_random_engine.seed(seed + this_shard_id());
|
|
}).then([&app] () -> future<> {
|
|
auto ext = std::make_shared<db::extensions>();
|
|
ext->add_schema_extension<db::tags_extension>(db::tags_extension::NAME);
|
|
auto db_cfg = ::make_shared<db::config>(ext);
|
|
|
|
const auto enable_cache = app.configuration()["enable-cache"].as<bool>();
|
|
const auto enable_index_cache = app.configuration()["enable-index-cache"].as<bool>();
|
|
std::cout << "enable-cache=" << enable_cache << '\n';
|
|
std::cout << "enable-index-cache=" << enable_index_cache << '\n';
|
|
db_cfg->enable_cache(enable_cache);
|
|
db_cfg->cache_index_pages(enable_index_cache);
|
|
if (app.configuration().contains("sstable-summary-ratio")) {
|
|
db_cfg->sstable_summary_ratio(app.configuration()["sstable-summary-ratio"].as<double>());
|
|
}
|
|
std::cout << "sstable-summary-ratio=" << db_cfg->sstable_summary_ratio() << '\n';
|
|
if (app.configuration().contains("sstable-format")) {
|
|
db_cfg->sstable_format(app.configuration()["sstable-format"].as<std::string>());
|
|
}
|
|
std::cout << "sstable-format=" << db_cfg->sstable_format() << '\n';
|
|
cql_test_config cfg(db_cfg);
|
|
if (app.configuration().contains("tablets")) {
|
|
cfg.db_config->tablets_mode_for_new_keyspaces.set(db::tablets_mode_t::mode::enabled);
|
|
cfg.initial_tablets = app.configuration()["initial-tablets"].as<unsigned>();
|
|
}
|
|
set_from_cli("audit", app, cfg.db_config->audit);
|
|
set_from_cli("audit-keyspaces", app, cfg.db_config->audit_keyspaces);
|
|
set_from_cli("audit-tables", app, cfg.db_config->audit_tables);
|
|
set_from_cli("audit-categories", app, cfg.db_config->audit_categories);
|
|
return do_with_cql_env_thread([&app] (auto&& env) {
|
|
auto cfg = test_config();
|
|
cfg.partitions = app.configuration()["partitions"].as<unsigned>();
|
|
cfg.duration_in_seconds = app.configuration()["duration"].as<unsigned>();
|
|
cfg.concurrency = app.configuration()["concurrency"].as<unsigned>();
|
|
cfg.query_single_key = app.configuration().contains("query-single-key");
|
|
cfg.counters = app.configuration().contains("counters");
|
|
cfg.flush_memtables = app.configuration().contains("flush");
|
|
if (app.configuration().contains("tablets")) {
|
|
cfg.initial_tablets = app.configuration()["initial-tablets"].as<unsigned>();
|
|
}
|
|
if (app.configuration().contains("write")) {
|
|
cfg.mode = test_config::run_mode::write;
|
|
} else if (app.configuration().contains("delete")) {
|
|
cfg.mode = test_config::run_mode::del;
|
|
} else {
|
|
cfg.mode = test_config::run_mode::read;
|
|
};
|
|
if (app.configuration().contains("operations-per-shard")) {
|
|
cfg.operations_per_shard = app.configuration()["operations-per-shard"].as<unsigned>();
|
|
}
|
|
if (app.configuration().contains("memtable-partitions")) {
|
|
cfg.memtable_partitions = app.configuration()["memtable-partitions"].as<unsigned>();
|
|
}
|
|
cfg.stop_on_error = app.configuration()["stop-on-error"].as<bool>();
|
|
cfg.timeout = app.configuration()["timeout"].as<std::string>();
|
|
cfg.bypass_cache = app.configuration().contains("bypass-cache");
|
|
audit::audit::create_audit(env.local_db().get_config(), env.get_shared_token_metadata()).handle_exception([&] (auto&& e) {
|
|
fmt::print("audit creation failed: {}", e);
|
|
}).get();
|
|
audit::audit::start_audit(env.local_db().get_config(), env.qp(), env.migration_manager()).get();
|
|
auto audit_stop = defer([] {
|
|
audit::audit::stop_audit().get();
|
|
});
|
|
auto results = do_cql_test(env, cfg);
|
|
aggregated_perf_results agg(results);
|
|
std::cout << agg << std::endl;
|
|
if (app.configuration().contains("json-result")) {
|
|
write_json_result(app.configuration()["json-result"].as<std::string>(), cfg, agg);
|
|
}
|
|
}, std::move(cfg));
|
|
});
|
|
});
|
|
}
|
|
|
|
} // namespace perf
|