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>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "test.h"
|
|
|
|
|
|
2019-10-06 17:07:14 +08:00
|
|
|
|
int testcase_hill::insert(const keygen::buffer &akey,
|
|
|
|
|
const keygen::buffer &adata, unsigned flags) {
|
|
|
|
|
int err = mdbx_put(txn_guard.get(), dbi, &akey->value, &adata->value, flags);
|
|
|
|
|
if (err == MDBX_SUCCESS) {
|
|
|
|
|
const auto S_key = S(akey);
|
|
|
|
|
const auto S_data = S(adata);
|
|
|
|
|
const bool inserted = mirror.emplace(S_key, S_data).second;
|
|
|
|
|
assert(inserted);
|
|
|
|
|
(void)inserted;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int testcase_hill::replace(const keygen::buffer &akey,
|
|
|
|
|
const keygen::buffer &new_data,
|
|
|
|
|
const keygen::buffer &old_data, unsigned flags) {
|
|
|
|
|
const auto S_key = S(akey);
|
|
|
|
|
const auto S_old = S(old_data);
|
|
|
|
|
const auto S_new = S(new_data);
|
|
|
|
|
const auto removed = mirror.erase(set::key_type(S_key, S_old));
|
|
|
|
|
assert(removed == 1);
|
|
|
|
|
(void)removed;
|
|
|
|
|
const bool inserted = mirror.emplace(S_key, S_new).second;
|
|
|
|
|
assert(inserted);
|
|
|
|
|
(void)inserted;
|
|
|
|
|
return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
|
|
|
|
|
&old_data->value, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int testcase_hill::remove(const keygen::buffer &akey,
|
|
|
|
|
const keygen::buffer &adata) {
|
|
|
|
|
const auto S_key = S(akey);
|
|
|
|
|
const auto S_data = S(adata);
|
|
|
|
|
const auto removed = mirror.erase(set::key_type(S_key, S_data));
|
|
|
|
|
assert(removed == 1);
|
|
|
|
|
(void)removed;
|
|
|
|
|
return mdbx_del(txn_guard.get(), dbi, &akey->value, &adata->value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool testcase_hill::verify() const {
|
|
|
|
|
char dump_key[128], dump_value[128];
|
|
|
|
|
char dump_mkey[128], dump_mvalue[128];
|
|
|
|
|
|
|
|
|
|
MDBX_cursor *cursor;
|
|
|
|
|
int err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
|
|
|
|
|
if (err != MDBX_SUCCESS)
|
|
|
|
|
failure_perror("mdbx_cursor_open()", err);
|
|
|
|
|
|
|
|
|
|
bool rc = true;
|
|
|
|
|
MDBX_val akey, avalue;
|
|
|
|
|
MDBX_val mkey, mvalue;
|
|
|
|
|
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_FIRST);
|
|
|
|
|
|
|
|
|
|
assert(std::is_sorted(mirror.cbegin(), mirror.cend(), ItemCompare(this)));
|
|
|
|
|
auto it = mirror.cbegin();
|
|
|
|
|
while (true) {
|
|
|
|
|
if (err != MDBX_SUCCESS) {
|
|
|
|
|
akey.iov_len = avalue.iov_len = 0;
|
|
|
|
|
akey.iov_base = avalue.iov_base = nullptr;
|
|
|
|
|
}
|
|
|
|
|
const auto S_key = S(akey);
|
|
|
|
|
const auto S_data = S(avalue);
|
|
|
|
|
if (it != mirror.cend()) {
|
|
|
|
|
mkey.iov_base = (void *)it->first.c_str();
|
|
|
|
|
mkey.iov_len = it->first.size();
|
|
|
|
|
mvalue.iov_base = (void *)it->second.c_str();
|
|
|
|
|
mvalue.iov_len = it->second.size();
|
|
|
|
|
}
|
|
|
|
|
if (err == MDBX_SUCCESS && it != mirror.cend() && S_key == it->first &&
|
|
|
|
|
S_data == it->second) {
|
|
|
|
|
++it;
|
|
|
|
|
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT);
|
|
|
|
|
} else if (err == MDBX_SUCCESS &&
|
|
|
|
|
(it == mirror.cend() || S_key < it->first ||
|
|
|
|
|
(S_key == it->first && S_data < it->second))) {
|
|
|
|
|
if (it != mirror.cend()) {
|
|
|
|
|
log_error("extra pair: db{%s, %s} < mi{%s, %s}",
|
|
|
|
|
mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
|
|
|
|
|
mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)),
|
|
|
|
|
mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
|
|
|
|
|
mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
|
|
|
|
|
} else {
|
|
|
|
|
log_error("extra pair: db{%s, %s} < mi.END",
|
|
|
|
|
mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
|
|
|
|
|
mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)));
|
|
|
|
|
}
|
|
|
|
|
err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT);
|
|
|
|
|
rc = false;
|
|
|
|
|
} else if (it != mirror.cend() &&
|
|
|
|
|
(err == MDBX_NOTFOUND || S_key > it->first ||
|
|
|
|
|
(S_key == it->first && S_data > it->second))) {
|
|
|
|
|
if (err == MDBX_NOTFOUND) {
|
|
|
|
|
log_error("lost pair: db.END > mi{%s, %s}",
|
|
|
|
|
mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
|
|
|
|
|
mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
|
|
|
|
|
} else {
|
|
|
|
|
log_error("lost pair: db{%s, %s} > mi{%s, %s}",
|
|
|
|
|
mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
|
|
|
|
|
mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)),
|
|
|
|
|
mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)),
|
|
|
|
|
mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue)));
|
|
|
|
|
}
|
|
|
|
|
++it;
|
|
|
|
|
rc = false;
|
|
|
|
|
} else if (err == MDBX_NOTFOUND && it == mirror.cend()) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (err != MDBX_SUCCESS) {
|
|
|
|
|
failure_perror("mdbx_cursor_get()", err);
|
|
|
|
|
} else {
|
|
|
|
|
assert(!"WTF?");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mdbx_cursor_close(cursor);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-30 23:54:57 +08:00
|
|
|
|
bool testcase_hill::run() {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
int err = db_open__begin__table_create_open_clean(dbi);
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("hill: bailout-prepare due '%s'", mdbx_strerror(err));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror.clear();
|
|
|
|
|
mirror_commited.clear();
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
/* LY: тест "холмиком":
|
|
|
|
|
* - сначала наполняем таблицу циклическими CRUD-манипуляциями,
|
|
|
|
|
* которые в каждом цикле делают несколько операций, включая удаление,
|
|
|
|
|
* но в результате добавляют записи.
|
|
|
|
|
* - затем очищаем таблицу также CRUD-манипуляциями, но уже с другой
|
|
|
|
|
* пропорцией удалений.
|
|
|
|
|
*
|
|
|
|
|
* При этом очень многое зависит от порядка перебора ключей:
|
|
|
|
|
* - (псевдо)случайное распределение требуется лишь для полноты картины,
|
|
|
|
|
* но в целом не покрывает важных кейсов.
|
|
|
|
|
* - кроме (псевдо)случайного перебора требуется последовательное
|
|
|
|
|
* итерирование ключей интервалами различной ширины, с тем чтобы
|
|
|
|
|
* проверить различные варианты как разделения, так и слияния страниц
|
|
|
|
|
* внутри движка.
|
2017-05-24 06:42:10 +08:00
|
|
|
|
* - при не-уникальных ключах (MDBX_DUPSORT с подвариантами), для каждого
|
2017-05-18 01:10:56 +08:00
|
|
|
|
* повтора внутри движка формируется вложенное btree-дерево,
|
|
|
|
|
* соответственно требуется соблюдение аналогичных принципов
|
|
|
|
|
* итерирования для значений.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* TODO: работа в несколько потоков */
|
2018-09-17 18:21:05 +08:00
|
|
|
|
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
keygen::buffer a_key = keygen::alloc(config.params.keylen_max);
|
|
|
|
|
keygen::buffer a_data_0 = keygen::alloc(config.params.datalen_max);
|
|
|
|
|
keygen::buffer a_data_1 = keygen::alloc(config.params.datalen_max);
|
|
|
|
|
keygen::buffer b_key = keygen::alloc(config.params.keylen_max);
|
|
|
|
|
keygen::buffer b_data = keygen::alloc(config.params.datalen_max);
|
|
|
|
|
|
2017-05-24 06:42:10 +08:00
|
|
|
|
const unsigned insert_flags = (config.params.table_flags & MDBX_DUPSORT)
|
|
|
|
|
? MDBX_NODUPDATA
|
|
|
|
|
: MDBX_NODUPDATA | MDBX_NOOVERWRITE;
|
|
|
|
|
const unsigned update_flags =
|
2018-08-31 09:06:15 +08:00
|
|
|
|
(config.params.table_flags & MDBX_DUPSORT)
|
|
|
|
|
? MDBX_CURRENT | MDBX_NODUPDATA | MDBX_NOOVERWRITE
|
|
|
|
|
: MDBX_NODUPDATA;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
uint64_t serial_count = 0;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
uint64_t commited_serial = serial_count;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
unsigned txn_nops = 0;
|
|
|
|
|
|
2019-10-06 17:07:14 +08:00
|
|
|
|
bool rc = false;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
while (should_continue()) {
|
|
|
|
|
const keygen::serial_t a_serial = serial_count;
|
2019-06-26 22:06:17 +08:00
|
|
|
|
if (unlikely(!keyvalue_maker.increment(serial_count, 1))) {
|
|
|
|
|
log_notice("uphill: unexpected key-space overflow");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
const keygen::serial_t b_serial = serial_count;
|
|
|
|
|
assert(b_serial > a_serial);
|
|
|
|
|
|
|
|
|
|
// создаем первую запись из пары
|
|
|
|
|
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
|
|
|
|
|
log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
|
|
|
|
|
a_serial);
|
|
|
|
|
generate_pair(a_serial, a_key, a_data_1, age_shift);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
|
|
|
|
|
err = insert(a_key, a_data_1, insert_flags);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("uphill: bailout at insert-a due '%s'", mdbx_strerror(err));
|
|
|
|
|
txn_restart(true, false);
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_put(insert-a.1)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after insert-a, before commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
commited_serial = a_serial;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after insert-a, after commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// создаем вторую запись из пары
|
|
|
|
|
log_trace("uphill: insert-b %" PRIu64, b_serial);
|
|
|
|
|
generate_pair(b_serial, b_key, b_data, 0);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = insert(b_key, b_data, insert_flags);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("uphill: bailout at insert-b due '%s'", mdbx_strerror(err));
|
|
|
|
|
txn_restart(true, false);
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_put(insert-b)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after insert-b, before commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
commited_serial = a_serial;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after insert-b, after commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// обновляем данные в первой записи
|
|
|
|
|
log_trace("uphill: update-a (age %" PRIu64 "->0) %" PRIu64, age_shift,
|
|
|
|
|
a_serial);
|
|
|
|
|
generate_pair(a_serial, a_key, a_data_0, 0);
|
2018-09-17 18:06:43 +08:00
|
|
|
|
checkdata("uphill: update-a", dbi, a_key->value, a_data_1->value);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = replace(a_key, a_data_0, a_data_1, update_flags);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("uphill: bailout at update-a due '%s'", mdbx_strerror(err));
|
|
|
|
|
txn_restart(true, false);
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_replace(update-a: 1->0)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after update-a, before commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
commited_serial = a_serial;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after update-a, after commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// удаляем вторую запись
|
|
|
|
|
log_trace("uphill: delete-b %" PRIu64, b_serial);
|
2018-09-17 18:06:43 +08:00
|
|
|
|
checkdata("uphill: delete-b", dbi, b_key->value, b_data->value);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = remove(b_key, b_data);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("uphill: bailout at delete-b due '%s'", mdbx_strerror(err));
|
|
|
|
|
txn_restart(true, false);
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_del(b)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after delete-b, before commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
|
|
|
|
|
serial_count = commited_serial;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
commited_serial = a_serial;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("uphill: bailout after delete-b, after commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report(1);
|
|
|
|
|
if (!keyvalue_maker.increment(serial_count, 1)) {
|
|
|
|
|
// дошли до границы пространства ключей
|
|
|
|
|
serial_count = a_serial;
|
|
|
|
|
goto overflow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-26 22:06:17 +08:00
|
|
|
|
while (serial_count > 1) {
|
2017-05-18 01:10:56 +08:00
|
|
|
|
if (unlikely(!keyvalue_maker.increment(serial_count, -2)))
|
|
|
|
|
failure("downhill: unexpected key-space underflow");
|
|
|
|
|
|
|
|
|
|
overflow:
|
|
|
|
|
const keygen::serial_t a_serial = serial_count;
|
|
|
|
|
const keygen::serial_t b_serial = a_serial + 1;
|
|
|
|
|
assert(b_serial > a_serial);
|
|
|
|
|
|
|
|
|
|
// обновляем первую запись из пары
|
|
|
|
|
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
|
|
|
|
|
log_trace("downhill: update-a (age 0->%" PRIu64 ") %" PRIu64, age_shift,
|
|
|
|
|
a_serial);
|
|
|
|
|
generate_pair(a_serial, a_key, a_data_0, 0);
|
|
|
|
|
generate_pair(a_serial, a_key, a_data_1, age_shift);
|
2018-09-17 18:06:43 +08:00
|
|
|
|
checkdata("downhill: update-a", dbi, a_key->value, a_data_0->value);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = replace(a_key, a_data_1, a_data_0, update_flags);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("downhill: bailout at update-a due '%s'",
|
|
|
|
|
mdbx_strerror(err));
|
|
|
|
|
txn_end(true);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_put(update-a: 0->1)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after update-a, before commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after update-a, after commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// создаем вторую запись из пары
|
|
|
|
|
log_trace("downhill: insert-b %" PRIu64, b_serial);
|
|
|
|
|
generate_pair(b_serial, b_key, b_data, 0);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = insert(b_key, b_data, insert_flags);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("downhill: bailout at insert-a due '%s'",
|
|
|
|
|
mdbx_strerror(err));
|
|
|
|
|
txn_end(true);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_put(insert-b)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after insert-b, before commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after insert-b, after commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// удаляем первую запись
|
|
|
|
|
log_trace("downhill: delete-a (age %" PRIu64 ") %" PRIu64, age_shift,
|
|
|
|
|
a_serial);
|
2018-09-17 18:06:43 +08:00
|
|
|
|
checkdata("downhill: delete-a", dbi, a_key->value, a_data_1->value);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = remove(a_key, a_data_1);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("downhill: bailout at delete-a due '%s'",
|
|
|
|
|
mdbx_strerror(err));
|
|
|
|
|
txn_end(true);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_del(a)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after delete-a, before commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after delete-a, after commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// удаляем вторую запись
|
|
|
|
|
log_trace("downhill: delete-b %" PRIu64, b_serial);
|
2018-09-17 18:06:43 +08:00
|
|
|
|
checkdata("downhill: delete-b", dbi, b_key->value, b_data->value);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
err = remove(b_key, b_data);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
|
|
|
|
|
log_notice("downhill: bailout at delete-b due '%s'",
|
|
|
|
|
mdbx_strerror(err));
|
|
|
|
|
txn_end(true);
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
failure_perror("mdbx_del(b)", err);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after delete-b, before commit");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (++txn_nops >= config.params.batch_write) {
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_restart();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror = mirror_commited;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
mirror_commited = mirror;
|
2017-05-18 01:10:56 +08:00
|
|
|
|
txn_nops = 0;
|
2019-10-06 17:07:14 +08:00
|
|
|
|
if (!verify()) {
|
|
|
|
|
log_notice("downhill: bailout after delete-b, after commit");
|
|
|
|
|
goto bailout;
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report(1);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-06 17:07:14 +08:00
|
|
|
|
rc = verify();
|
|
|
|
|
bailout:
|
2019-06-24 07:55:26 +08:00
|
|
|
|
if (txn_guard) {
|
2019-06-25 20:48:03 +08:00
|
|
|
|
err = breakable_commit();
|
2019-06-24 07:55:26 +08:00
|
|
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
|
|
|
log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
|
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
|
|
|
|
|
if (dbi) {
|
|
|
|
|
if (config.params.drop_table && !mode_readonly()) {
|
2017-10-27 02:14:29 +08:00
|
|
|
|
txn_begin(false);
|
2017-05-18 01:10:56 +08:00
|
|
|
|
db_table_drop(dbi);
|
2019-06-23 20:55:13 +08:00
|
|
|
|
err = breakable_commit();
|
|
|
|
|
if (unlikely(err != MDBX_SUCCESS)) {
|
|
|
|
|
log_notice("hill: bailout-clean due '%s'", mdbx_strerror(err));
|
2019-10-06 17:07:14 +08:00
|
|
|
|
return rc;
|
2019-06-23 20:55:13 +08:00
|
|
|
|
}
|
2017-05-18 01:10:56 +08:00
|
|
|
|
} else
|
|
|
|
|
db_table_close(dbi);
|
|
|
|
|
}
|
2019-10-06 17:07:14 +08:00
|
|
|
|
return rc;
|
2017-03-30 23:54:57 +08:00
|
|
|
|
}
|