/* * Copyright 2017-2019 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ #pragma once #include "base.h" #include "log.h" #include "utils.h" #define ACTOR_ID_MAX INT16_MAX enum actor_testcase { ac_none, ac_hill, ac_deadread, ac_deadwrite, ac_jitter, ac_try, ac_copy, ac_append, ac_ttl }; enum actor_status { as_unknown, as_debuging, as_running, as_successful, as_killed, as_failed, as_coredump, }; const char *testcase2str(const actor_testcase); const char *status2str(actor_status status); enum keygen_case { kc_random, /* [ 6.. 2.. 7.. 4.. 0.. 1.. 5.. 3.. ] */ kc_dashes, /* [ 0123.. 4567.. ] */ kc_custom, /* TODO: more cases */ }; const char *keygencase2str(const keygen_case); //----------------------------------------------------------------------------- namespace config { enum scale_mode { no_scale, decimal, binary, duration }; bool parse_option(int argc, char *const argv[], int &narg, const char *option, const char **value, const char *default_value = nullptr); bool parse_option(int argc, char *const argv[], int &narg, const char *option, std::string &value, bool allow_empty = false); bool parse_option(int argc, char *const argv[], int &narg, const char *option, std::string &value, bool allow_empty, const char *default_value); bool parse_option(int argc, char *const argv[], int &narg, const char *option, bool &value); struct option_verb { const char *const verb; unsigned mask; }; bool parse_option(int argc, char *const argv[], int &narg, const char *option, unsigned &mask, const option_verb *verbs); bool parse_option(int argc, char *const argv[], int &narg, const char *option, uint64_t &value, const scale_mode scale, const uint64_t minval = 0, const uint64_t maxval = INT64_MAX, const uint64_t default_value = 0); bool parse_option(int argc, char *const argv[], int &narg, const char *option, unsigned &value, const scale_mode scale, const unsigned minval = 0, const unsigned maxval = INT32_MAX, const unsigned default_value = 0); bool parse_option(int argc, char *const argv[], int &narg, const char *option, uint8_t &value, const uint8_t minval = 0, const uint8_t maxval = 255, const uint8_t default_value = 0); bool parse_option(int argc, char *const argv[], int &narg, const char *option, int64_t &value, const int64_t minval, const int64_t maxval, const int64_t default_value = -1); bool parse_option(int argc, char *const argv[], int &narg, const char *option, int32_t &value, const int32_t minval, const int32_t maxval, const int32_t default_value = -1); //----------------------------------------------------------------------------- #pragma pack(push, 1) struct keygen_params_pod { keygen_case keycase; /* Параметры генератора пар key-value. * * Ключи и значения генерируются по задаваемым параметрам на основе "плоской" * исходной координаты. При этом, в общем случае, в процессе тестов исходная * координата последовательно итерируется в заданном диапазоне, а необходимые * паттерны/последовательности/узоры получаются за счет преобразования * исходной координаты, согласно описанным ниже параметрам. * * Стоит отметить, что порядок описания параметров для удобства совпадает с * порядком их использования, т.е. с порядком соответствующих преобразований. * * Второе важное замечание касается ограничений одновременной координированной * генерации паттеров как для ключей, так и для значений. Суть в том, что * такая возможность не нужна по следующим причинам: * - libmdbx поддерживает два существенно различающихся вида таблиц, * "уникальные" (без дубликатов и без multi-value), и так называемые * "с дубликатами" (c multi-value). * - Для таблиц "без дубликатов" только размер связанных к ключами значений * (данных) оказывает влияния на работу движка, непосредственно содержимое * данных не анализируется движком и не оказывает влияния на его работу. * - Для таблиц "с дубликатами", при наличии более одного значения для * некоторого ключа, формируется дочернее btree-поддерево. Это дерево * формируется в отдельном "кусте" страниц и обслуживается независимо * от окружения родительского ключа. * - Таким образом, паттерн генерации значений имеет смысл только для * таблиц "с дубликатами" и только в контексте одного значения ключа. * Иначе говоря, нет смысла в со-координации генерации паттернов для * ключей и значений. Более того, генерацию значений всегда необходимо * рассматривать в контексте связки с одним значением ключа. * - Тем не менее, во всех случаях достаточно важным является равномерная * всех возможных сочетаний длин ключей и данных. * * width: * Большинство тестов предполагают создание или итерирование некоторого * количества записей. При этом требуется итерирование или генерация * значений и ключей из некоторого ограниченного пространства вариантов. * * Параметр width задает такую ширину пространства вариантов в битах. * Таким образом мощность пространства вариантов (пока) всегда равна * степени двойки. Это ограничение можно снять, но ценой увеличения * вычислительной сложности, включая потерю простоты и прозрачности. * * С другой стороны, не-битовый width может быть полезен: * - Позволит генерировать ключи/значения в точно задаваемом диапазоне. * Например, перебрать в псевдо-случайном порядке 10001 значение. * - Позволит поровну разделять заданное пространство (диапазон) * ключей/значений между количеством потоков некратным степени двойки. * * mesh и seed: * Позволяют получить псевдо-случайные последовательности ключей/значений. * Параметр mesh задает сколько младших бит исходной плоской координаты * будет "перемешано" (инъективно отображено), а параметр seed позволяет * выбрать конкретный вариант "перемешивания". * * Перемешивание выполняется при ненулевом значении mesh. Перемешивание * реализуется посредством применения двух инъективных функций для * заданного количества бит: * - применяется первая инъективная функция; * - к результату добавляется salt полученный из seed; * - применяется вторая инъективная функция; * * Следует отметить, что mesh умышленно позволяет перемешать только младшую * часть, что при ненулевом значении split (см далее) не позволяет получать * псевдо-случайные значений ключей без псевдо-случайности в значениях. * * Такое ограничение соответствуют внутренней алгоритмике libmdbx. Проще * говоря, мы можем проверить движок псевдо-случайной последовательностью * ключей на таблицах без дубликатов (без multi-value), а затем проверить * корректность работу псевдо-случайной последовательностью значений на * таблицах с дубликатами (с multi-value), опционально добавляя * псевдо-случайности к последовательности ключей. Однако, нет смысла * генерировать псевдо-случайные ключи, одновременно с формированием * какого-либо паттерна в значениях, так как содержимое в данных либо * не будет иметь значения (для таблиц без дубликатов), либо будет * обрабатываться в отдельных btree-поддеревьях. * * rotate и offset: * Для проверки слияния и разделения страниц внутри движка требуются * генерация ключей/значений в виде не-смежных последовательностей, как-бы * в виде "пунктира", который постепенно заполняет весь заданных диапазон. * * Параметры позволяют генерировать такой "пунктир". Соответственно rotate * задает циклический сдвиг вправо, а offset задает смещение, точнее говоря * сложение по модулю внутри диапазона заданного посредством width. * * Например, при rotate равном 1 (циклический сдвиг вправо на 1 бит), * четные и нечетные исходные значения сложатся в две линейные * последовательности, которые постепенно закроют старшую и младшую * половины диапазона. * * split: * Для таблиц без дубликатов (без multi-value ключей) фактически требуется * генерация только ключей, а данные могут быть постоянным. Но для таблиц с * дубликатами (с multi-value ключами) также требуется генерация значений. * * Ненулевое значение параметра split фактически включает генерацию значений, * при этом значение split определяет сколько бит исходного абстрактного * номера будет отрезано для генерации значения. */ uint8_t width; uint8_t mesh; uint8_t rotate; uint8_t split; uint32_t seed; uint64_t offset; }; struct actor_params_pod { unsigned loglevel; unsigned mode_flags; unsigned table_flags; intptr_t size_lower; intptr_t size_now; intptr_t size_upper; int shrink_threshold; int growth_step; int pagesize; unsigned test_duration; unsigned test_nops; unsigned nrepeat; unsigned nthreads; unsigned keylen_min, keylen_max; unsigned datalen_min, datalen_max; unsigned batch_read; unsigned batch_write; unsigned delaystart; unsigned waitfor_nops; unsigned inject_writefaultn; unsigned max_readers; unsigned max_tables; keygen_params_pod keygen; bool drop_table; bool ignore_dbfull; }; struct actor_config_pod { unsigned actor_id, space_id; actor_testcase testcase; unsigned wait4id; unsigned signal_nops; }; #pragma pack(pop) extern const struct option_verb mode_bits[]; extern const struct option_verb table_bits[]; void dump(const char *title = "config-dump: "); } /* namespace config */ struct actor_params : public config::actor_params_pod { std::string pathname_log; std::string pathname_db; void set_defaults(const std::string &tmpdir); unsigned mdbx_keylen_min() const; unsigned mdbx_keylen_max() const; unsigned mdbx_datalen_min() const; unsigned mdbx_datalen_max() const; }; struct actor_config : public config::actor_config_pod { actor_params params; bool wanna_event4signalling() const { return true /* TODO ? */; } actor_config(actor_testcase testcase, const actor_params ¶ms, unsigned space_id, unsigned wait4id); actor_config(const char *str) { if (!deserialize(str, *this)) failure("Invalid internal parameter '%s'\n", str); } const std::string osal_serialize(simple_checksum &) const; bool osal_deserialize(const char *str, const char *end, simple_checksum &); const std::string serialize(const char *prefix) const; static bool deserialize(const char *str, actor_config &config); bool is_waitable(size_t nops) const { switch (testcase) { case ac_hill: if (!params.test_nops || params.test_nops >= nops) return true; __fallthrough; default: return false; } } };