2019-09-09 18:40:24 +08:00
|
|
|
|
/*
|
2019-02-03 17:28:01 +08:00
|
|
|
|
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
|
2017-03-30 23:54:57 +08:00
|
|
|
|
* 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
|
|
|
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "base.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
|
|
#define ACTOR_ID_MAX INT16_MAX
|
|
|
|
|
|
2017-10-27 01:51:46 +08:00
|
|
|
|
enum actor_testcase {
|
|
|
|
|
ac_none,
|
|
|
|
|
ac_hill,
|
|
|
|
|
ac_deadread,
|
|
|
|
|
ac_deadwrite,
|
|
|
|
|
ac_jitter,
|
2018-11-04 23:57:36 +08:00
|
|
|
|
ac_try,
|
2019-02-04 03:37:57 +08:00
|
|
|
|
ac_copy,
|
2019-06-22 03:42:45 +08:00
|
|
|
|
ac_append,
|
2019-10-10 04:38:44 +08:00
|
|
|
|
ac_ttl,
|
|
|
|
|
ac_nested
|
2017-10-27 01:51:46 +08:00
|
|
|
|
};
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
enum actor_status {
|
|
|
|
|
as_unknown,
|
|
|
|
|
as_debuging,
|
|
|
|
|
as_running,
|
|
|
|
|
as_successful,
|
|
|
|
|
as_killed,
|
2019-07-07 07:33:35 +08:00
|
|
|
|
as_failed,
|
|
|
|
|
as_coredump,
|
2017-03-30 23:54:57 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *testcase2str(const actor_testcase);
|
|
|
|
|
const char *status2str(actor_status status);
|
|
|
|
|
|
2017-05-18 01:10:56 +08:00
|
|
|
|
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);
|
|
|
|
|
|
2017-03-30 23:54:57 +08:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2018-08-20 18:23:54 +08:00
|
|
|
|
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
|
|
|
|
std::string &value, bool allow_empty,
|
|
|
|
|
const char *default_value);
|
|
|
|
|
|
2017-03-30 23:54:57 +08:00
|
|
|
|
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,
|
2017-05-24 23:50:24 +08:00
|
|
|
|
unsigned &mask, const option_verb *verbs);
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
|
|
|
|
uint64_t &value, const scale_mode scale,
|
2018-08-20 18:23:54 +08:00
|
|
|
|
const uint64_t minval = 0, const uint64_t maxval = INT64_MAX,
|
|
|
|
|
const uint64_t default_value = 0);
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
|
|
|
|
unsigned &value, const scale_mode scale,
|
2018-08-20 18:23:54 +08:00
|
|
|
|
const unsigned minval = 0, const unsigned maxval = INT32_MAX,
|
|
|
|
|
const unsigned default_value = 0);
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
2017-05-18 01:10:56 +08:00
|
|
|
|
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
|
|
|
|
uint8_t &value, const uint8_t minval = 0,
|
2018-08-20 18:23:54 +08:00
|
|
|
|
const uint8_t maxval = 255, const uint8_t default_value = 0);
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
2018-08-20 18:23:54 +08:00
|
|
|
|
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);
|
2019-08-13 07:12:13 +08:00
|
|
|
|
|
|
|
|
|
inline bool parse_option_intptr(int argc, char *const argv[], int &narg,
|
|
|
|
|
const char *option, intptr_t &value,
|
|
|
|
|
const intptr_t minval, const intptr_t maxval,
|
|
|
|
|
const intptr_t default_value = -1) {
|
|
|
|
|
static_assert(sizeof(intptr_t) == 4 || sizeof(intptr_t) == 8, "WTF?");
|
|
|
|
|
if (sizeof(intptr_t) == 8)
|
|
|
|
|
return parse_option(argc, argv, narg, option,
|
|
|
|
|
*reinterpret_cast<int64_t *>(&value), int64_t(minval),
|
|
|
|
|
int64_t(maxval), int64_t(default_value));
|
|
|
|
|
else
|
|
|
|
|
return parse_option(argc, argv, narg, option,
|
|
|
|
|
*reinterpret_cast<int32_t *>(&value), int32_t(minval),
|
|
|
|
|
int32_t(maxval), int32_t(default_value));
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-30 23:54:57 +08:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
|
|
2017-05-18 01:10:56 +08:00
|
|
|
|
struct keygen_params_pod {
|
|
|
|
|
/* Параметры генератора пар key-value.
|
|
|
|
|
*
|
|
|
|
|
* Ключи и значения генерируются по задаваемым параметрам на основе "плоской"
|
|
|
|
|
* исходной координаты. При этом, в общем случае, в процессе тестов исходная
|
|
|
|
|
* координата последовательно итерируется в заданном диапазоне, а необходимые
|
|
|
|
|
* паттерны/последовательности/узоры получаются за счет преобразования
|
|
|
|
|
* исходной координаты, согласно описанным ниже параметрам.
|
|
|
|
|
*
|
|
|
|
|
* Стоит отметить, что порядок описания параметров для удобства совпадает с
|
|
|
|
|
* порядком их использования, т.е. с порядком соответствующих преобразований.
|
|
|
|
|
*
|
|
|
|
|
* Второе важное замечание касается ограничений одновременной координированной
|
|
|
|
|
* генерации паттеров как для ключей, так и для значений. Суть в том, что
|
|
|
|
|
* такая возможность не нужна по следующим причинам:
|
|
|
|
|
* - libmdbx поддерживает два существенно различающихся вида таблиц,
|
|
|
|
|
* "уникальные" (без дубликатов и без multi-value), и так называемые
|
|
|
|
|
* "с дубликатами" (c multi-value).
|
|
|
|
|
* - Для таблиц "без дубликатов" только размер связанных к ключами значений
|
|
|
|
|
* (данных) оказывает влияния на работу движка, непосредственно содержимое
|
|
|
|
|
* данных не анализируется движком и не оказывает влияния на его работу.
|
|
|
|
|
* - Для таблиц "с дубликатами", при наличии более одного значения для
|
|
|
|
|
* некоторого ключа, формируется дочернее btree-поддерево. Это дерево
|
|
|
|
|
* формируется в отдельном "кусте" страниц и обслуживается независимо
|
|
|
|
|
* от окружения родительского ключа.
|
|
|
|
|
* - Таким образом, паттерн генерации значений имеет смысл только для
|
|
|
|
|
* таблиц "с дубликатами" и только в контексте одного значения ключа.
|
|
|
|
|
* Иначе говоря, нет смысла в со-координации генерации паттернов для
|
|
|
|
|
* ключей и значений. Более того, генерацию значений всегда необходимо
|
|
|
|
|
* рассматривать в контексте связки с одним значением ключа.
|
2018-08-31 23:50:35 +08:00
|
|
|
|
* - Тем не менее, во всех случаях достаточно важным является равномерная
|
|
|
|
|
* всех возможных сочетаний длин ключей и данных.
|
2017-05-18 01:10:56 +08:00
|
|
|
|
*
|
|
|
|
|
* width:
|
|
|
|
|
* Большинство тестов предполагают создание или итерирование некоторого
|
|
|
|
|
* количества записей. При этом требуется итерирование или генерация
|
|
|
|
|
* значений и ключей из некоторого ограниченного пространства вариантов.
|
|
|
|
|
*
|
|
|
|
|
* Параметр width задает такую ширину пространства вариантов в битах.
|
|
|
|
|
* Таким образом мощность пространства вариантов (пока) всегда равна
|
|
|
|
|
* степени двойки. Это ограничение можно снять, но ценой увеличения
|
|
|
|
|
* вычислительной сложности, включая потерю простоты и прозрачности.
|
|
|
|
|
*
|
|
|
|
|
* С другой стороны, не-битовый width может быть полезен:
|
|
|
|
|
* - Позволит генерировать ключи/значения в точно задаваемом диапазоне.
|
|
|
|
|
* Например, перебрать в псевдо-случайном порядке 10001 значение.
|
|
|
|
|
* - Позволит поровну разделять заданное пространство (диапазон)
|
|
|
|
|
* ключей/значений между количеством потоков некратным степени двойки.
|
|
|
|
|
*
|
|
|
|
|
* mesh и seed:
|
|
|
|
|
* Позволяют получить псевдо-случайные последовательности ключей/значений.
|
|
|
|
|
* Параметр mesh задает сколько младших бит исходной плоской координаты
|
|
|
|
|
* будет "перемешано" (инъективно отображено), а параметр seed позволяет
|
|
|
|
|
* выбрать конкретный вариант "перемешивания".
|
|
|
|
|
*
|
|
|
|
|
* Перемешивание выполняется при ненулевом значении mesh. Перемешивание
|
|
|
|
|
* реализуется посредством применения двух инъективных функций для
|
|
|
|
|
* заданного количества бит:
|
|
|
|
|
* - применяется первая инъективная функция;
|
|
|
|
|
* - к результату добавляется salt полученный из seed;
|
|
|
|
|
* - применяется вторая инъективная функция;
|
|
|
|
|
*
|
|
|
|
|
* Следует отметить, что mesh умышленно позволяет перемешать только младшую
|
|
|
|
|
* часть, что при ненулевом значении split (см далее) не позволяет получать
|
|
|
|
|
* псевдо-случайные значений ключей без псевдо-случайности в значениях.
|
|
|
|
|
*
|
|
|
|
|
* Такое ограничение соответствуют внутренней алгоритмике libmdbx. Проще
|
2018-08-31 23:50:35 +08:00
|
|
|
|
* говоря, мы можем проверить движок псевдо-случайной последовательностью
|
2017-05-18 01:10:56 +08:00
|
|
|
|
* ключей на таблицах без дубликатов (без 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;
|
2019-10-24 04:46:12 +08:00
|
|
|
|
keygen_case keycase;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
};
|
|
|
|
|
|
2017-03-30 23:54:57 +08:00
|
|
|
|
struct actor_params_pod {
|
2017-05-24 23:50:24 +08:00
|
|
|
|
unsigned mode_flags;
|
|
|
|
|
unsigned table_flags;
|
2018-08-22 12:22:01 +08:00
|
|
|
|
intptr_t size_lower;
|
|
|
|
|
intptr_t size_now;
|
|
|
|
|
intptr_t size_upper;
|
|
|
|
|
int shrink_threshold;
|
|
|
|
|
int growth_step;
|
|
|
|
|
int pagesize;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
unsigned test_duration;
|
2017-04-21 23:41:11 +08:00
|
|
|
|
unsigned test_nops;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
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;
|
2018-03-19 21:51:44 +08:00
|
|
|
|
unsigned inject_writefaultn;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
unsigned max_readers;
|
|
|
|
|
unsigned max_tables;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
keygen_params_pod keygen;
|
|
|
|
|
|
2019-10-24 04:46:12 +08:00
|
|
|
|
uint8_t loglevel;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
bool drop_table;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
bool ignore_dbfull;
|
2019-10-09 22:56:59 +08:00
|
|
|
|
bool speculum;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct actor_config_pod {
|
2017-04-21 23:41:11 +08:00
|
|
|
|
unsigned actor_id, space_id;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
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[];
|
2017-04-11 17:55:16 +08:00
|
|
|
|
void dump(const char *title = "config-dump: ");
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
} /* namespace config */
|
|
|
|
|
|
|
|
|
|
struct actor_params : public config::actor_params_pod {
|
|
|
|
|
std::string pathname_log;
|
|
|
|
|
std::string pathname_db;
|
2018-03-20 01:41:08 +08:00
|
|
|
|
void set_defaults(const std::string &tmpdir);
|
2018-08-31 22:05:00 +08:00
|
|
|
|
|
|
|
|
|
unsigned mdbx_keylen_min() const;
|
|
|
|
|
unsigned mdbx_keylen_max() const;
|
|
|
|
|
unsigned mdbx_datalen_min() const;
|
|
|
|
|
unsigned mdbx_datalen_max() const;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct actor_config : public config::actor_config_pod {
|
|
|
|
|
actor_params params;
|
|
|
|
|
|
|
|
|
|
bool wanna_event4signalling() const { return true /* TODO ? */; }
|
|
|
|
|
|
2017-04-21 23:41:11 +08:00
|
|
|
|
actor_config(actor_testcase testcase, const actor_params ¶ms,
|
|
|
|
|
unsigned space_id, unsigned wait4id);
|
2017-03-30 23:54:57 +08:00
|
|
|
|
|
|
|
|
|
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:
|
2017-04-21 23:41:11 +08:00
|
|
|
|
if (!params.test_nops || params.test_nops >= nops)
|
2017-03-30 23:54:57 +08:00
|
|
|
|
return true;
|
2017-10-28 15:35:01 +08:00
|
|
|
|
__fallthrough;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|