mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-30 11:29:19 +08:00
mdbx: fix regression MDBX_KEYEXISTS
during mdbx_commit_ex()
and overwriting GC records.
Fix regression related to https://github.com/erthink/libmdbx/issues/123 and https://github.com/erthink/libmdbx/issues/128. Related to https://github.com/erthink/libmdbx/issues/131. В lifo-режиме при фиксации транзакции, записи в GC могли быть перезаписаны (с утечкой страниц БД), либо могла возникать ошибка MDBX_KEYEXISTS, по следующему сценарию: - В истории БД были две транзакции с огромным кол-вом retired pages, после которых в GC остались две соответствующие записи. - В ходе очередной транзакции первая из огромных GC-записей попадает в переработку и образует огромный reclaimed list. - При фиксации транзакции производится попытка разбить огромный reclaimed list на чанки размером в одну страницу. Для этого требуется много id для записей, которые в соответствии с lifo должны быть максимально близки к голове GC, т. е. получены путем переработки последних записей GC. - В ходе переработки последних записей очередь доходит до второй огромной записи, при этом переработка прерывается, ибо иначе reclaimed list переполнится. - Однако прерывание переработки внутри mdbx_update_gc() трактовалось как отсутствие записей в GC, поэтому список доступных просто добавлялись соответствующие id-шники. - Если в списке доступных id-шников для помещения в GC были переработанные, то записи с id по всему списку удалялись - тогда вторая большая запись (и возможно предыдущие) удалялись, а содержащиеся в них номера страниц выпадали из оборота. - Если же в списке доступных id-шников не было переработанных, то чистка не проводилась - тогда при последующая попытка помещения чанков reclaimed list в GC завершалась ошибкой MDBX_KEYEXISTS, которая и возвращалась из mdbx_commit_ex(). Change-Id: I3e5d40ef7950b7476da0513c6836fcba1de74879
This commit is contained in:
parent
ee22ffb878
commit
5f1b684719
51
src/core.c
51
src/core.c
@ -5597,15 +5597,24 @@ no_loose:
|
||||
goto fail;
|
||||
}
|
||||
const unsigned gc_len = MDBX_PNL_SIZE(gc_pnl);
|
||||
if (flags != MDBX_ALLOC_GC &&
|
||||
unlikely(gc_len + MDBX_PNL_SIZE(txn->tw.reclaimed_pglist) >
|
||||
if (unlikely(/* resulting list is tool long */ gc_len +
|
||||
MDBX_PNL_SIZE(txn->tw.reclaimed_pglist) >
|
||||
env->me_options.rp_augment_limit) &&
|
||||
(pgno_add(txn->mt_next_pgno, num) <= txn->mt_geo.upper ||
|
||||
(((/* not a slot-request from gc-update */
|
||||
mp || (flags & MDBX_LIFORECLAIM) == 0 ||
|
||||
(txn->tw.lifo_reclaimed &&
|
||||
MDBX_PNL_SIZE(txn->tw.lifo_reclaimed))) &&
|
||||
/* have enough unallocated space */ pgno_add(
|
||||
txn->mt_next_pgno, num) <= txn->mt_geo.upper) ||
|
||||
gc_len + MDBX_PNL_SIZE(txn->tw.reclaimed_pglist) >=
|
||||
MDBX_PGL_LIMIT / 16 * 15)) {
|
||||
/* Stop reclaiming to avoid overflow the page list.
|
||||
* This is a rare case while search for a continuously multi-page region
|
||||
* in a large database. https://github.com/erthink/libmdbx/issues/123 */
|
||||
mdbx_debug("stop reclaiming to avoid PNL overflow: %u (curent) + %u "
|
||||
"(chunk) -> %u",
|
||||
MDBX_PNL_SIZE(txn->tw.reclaimed_pglist), gc_len,
|
||||
gc_len + MDBX_PNL_SIZE(txn->tw.reclaimed_pglist));
|
||||
flags &= ~(MDBX_ALLOC_GC | MDBX_COALESCE);
|
||||
break;
|
||||
}
|
||||
@ -8105,7 +8114,39 @@ retry_noaccount:
|
||||
}
|
||||
|
||||
mdbx_tassert(txn, gc_rid >= MIN_TXNID && gc_rid <= MAX_TXNID);
|
||||
rc = mdbx_txl_append(&txn->tw.lifo_reclaimed, --gc_rid);
|
||||
--gc_rid;
|
||||
key.iov_base = &gc_rid;
|
||||
key.iov_len = sizeof(gc_rid);
|
||||
rc = mdbx_cursor_get(&couple.outer, &key, &data, MDBX_SET_KEY);
|
||||
if (unlikely(rc == MDBX_SUCCESS)) {
|
||||
mdbx_debug("%s: GC's id %" PRIaTXN
|
||||
" is used, continue bottom-up search",
|
||||
dbg_prefix_mode, gc_rid);
|
||||
++gc_rid;
|
||||
rc = mdbx_cursor_get(&couple.outer, &key, &data, MDBX_FIRST);
|
||||
if (rc == MDBX_NOTFOUND) {
|
||||
mdbx_debug("%s: GC is empty", dbg_prefix_mode);
|
||||
break;
|
||||
}
|
||||
if (unlikely(rc != MDBX_SUCCESS ||
|
||||
key.iov_len != sizeof(mdbx_tid_t))) {
|
||||
rc = MDBX_CORRUPTED;
|
||||
goto bailout;
|
||||
}
|
||||
txnid_t gc_first = unaligned_peek_u64(4, key.iov_base);
|
||||
if (unlikely(gc_first < MIN_TXNID || gc_first > MAX_TXNID)) {
|
||||
rc = MDBX_CORRUPTED;
|
||||
goto bailout;
|
||||
}
|
||||
if (gc_first < 2) {
|
||||
mdbx_debug("%s: no free GC's id(s) less than %" PRIaTXN,
|
||||
dbg_prefix_mode, gc_rid);
|
||||
break;
|
||||
}
|
||||
gc_rid = gc_first - 1;
|
||||
}
|
||||
|
||||
rc = mdbx_txl_append(&txn->tw.lifo_reclaimed, gc_rid);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
goto bailout;
|
||||
|
||||
@ -8216,7 +8257,7 @@ retry_noaccount:
|
||||
}
|
||||
mdbx_tassert(txn, chunk > 0);
|
||||
|
||||
mdbx_trace("%s: rc_rid %" PRIaTXN ", reused_gc_slot %u, reservation-id "
|
||||
mdbx_trace("%s: gc_rid %" PRIaTXN ", reused_gc_slot %u, reservation-id "
|
||||
"%" PRIaTXN,
|
||||
dbg_prefix_mode, gc_rid, reused_gc_slot, reservation_gc_id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user