mirror of
https://github.com/isar/libmdbx.git
synced 2025-04-01 02:32:57 +08:00
mdbx: устранение гонки в tbl_setup(MDBX_DUPFIXED | MDBX_INTEGERDUP)
при работе в разных потоках (backport).
Проблема была в том, что в случаях фиксированного размера значений clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались по актуальному размеру данных в БД. Поэтому при конкурентном вызове из разных потоков, один поток мог выполнять инициализацию, а второй прочитать временные/промежуточные значения lmin/lmax. В результате, при конкурентном старте транзакций в разных потоках при использовании только-что открытого dbi-хендла, проверка допустимости длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE.
This commit is contained in:
parent
1720762080
commit
5dfe3433a8
@ -200,6 +200,10 @@ static inline bool check_table_flags(unsigned flags) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline int tbl_setup_ifneed(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db) {
|
||||
return likely(kvx->clc.v.lmax) ? MDBX_SUCCESS : tbl_setup(env, kvx, db);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
MDBX_NOTHROW_PURE_FUNCTION static inline size_t pgno2bytes(const MDBX_env *env, size_t pgno) {
|
||||
|
@ -298,10 +298,7 @@ static __always_inline int couple_init(cursor_couple_t *couple, const MDBX_txn *
|
||||
if (unlikely(*dbi_state & DBI_STALE))
|
||||
return tbl_fetch(couple->outer.txn, cursor_dbi(&couple->outer));
|
||||
|
||||
if (unlikely(kvx->clc.k.lmax == 0))
|
||||
return tbl_setup(txn->env, kvx, tree);
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
return tbl_setup_ifneed(txn->env, kvx, tree);
|
||||
}
|
||||
|
||||
__cold int cursor_init4walk(cursor_couple_t *couple, const MDBX_txn *const txn, tree_t *const tree, kvx_t *const kvx) {
|
||||
@ -387,6 +384,7 @@ int cursor_dupsort_setup(MDBX_cursor *mc, const node_t *node, const page_t *mp)
|
||||
}
|
||||
mc->tree->dupfix_size = mx->nested_tree.dupfix_size;
|
||||
mc->clc->v.lmin = mc->clc->v.lmax = mx->nested_tree.dupfix_size;
|
||||
cASSERT(mc, mc->clc->v.lmax >= mc->clc->v.lmin);
|
||||
}
|
||||
|
||||
DEBUG("Sub-db dbi -%zu root page %" PRIaPGNO, cursor_dbi(&mx->cursor), mx->nested_tree.root);
|
||||
|
@ -95,7 +95,7 @@ MDBX_INTERNAL void recalculate_subpage_thresholds(MDBX_env *env);
|
||||
|
||||
/* table.c */
|
||||
MDBX_INTERNAL int __must_check_result tbl_fetch(MDBX_txn *txn, size_t dbi);
|
||||
MDBX_INTERNAL int __must_check_result tbl_setup(const MDBX_env *env, kvx_t *const kvx, const tree_t *const db);
|
||||
MDBX_INTERNAL int __must_check_result tbl_setup(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db);
|
||||
|
||||
/* coherency.c */
|
||||
MDBX_INTERNAL bool coherency_check_meta(const MDBX_env *env, const volatile meta_t *meta, bool report);
|
||||
|
40
src/table.c
40
src/table.c
@ -3,28 +3,37 @@
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
int tbl_setup(const MDBX_env *env, kvx_t *const kvx, const tree_t *const db) {
|
||||
int tbl_setup(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db) {
|
||||
osal_memory_fence(mo_AcquireRelease, false);
|
||||
|
||||
if (unlikely(!check_table_flags(db->flags))) {
|
||||
ERROR("incompatible or invalid db.flags (0x%x) ", db->flags);
|
||||
return MDBX_INCOMPATIBLE;
|
||||
}
|
||||
if (unlikely(!kvx->clc.k.cmp)) {
|
||||
kvx->clc.k.cmp = builtin_keycmp(db->flags);
|
||||
kvx->clc.v.cmp = builtin_datacmp(db->flags);
|
||||
|
||||
size_t v_lmin = valsize_min(db->flags);
|
||||
size_t v_lmax = env_valsize_max(env, db->flags);
|
||||
if ((db->flags & (MDBX_DUPFIXED | MDBX_INTEGERDUP)) != 0 && db->dupfix_size) {
|
||||
if (!MDBX_DISABLE_VALIDATION && unlikely(db->dupfix_size < v_lmin || db->dupfix_size > v_lmax)) {
|
||||
ERROR("db.dupfix_size (%u) <> min/max value-length (%zu/%zu)", db->dupfix_size, v_lmin, v_lmax);
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
v_lmin = v_lmax = db->dupfix_size;
|
||||
}
|
||||
|
||||
kvx->clc.k.lmin = keysize_min(db->flags);
|
||||
kvx->clc.k.lmax = env_keysize_max(env, db->flags);
|
||||
kvx->clc.v.lmin = valsize_min(db->flags);
|
||||
kvx->clc.v.lmax = env_valsize_max(env, db->flags);
|
||||
if (unlikely(!kvx->clc.k.cmp)) {
|
||||
kvx->clc.v.cmp = builtin_datacmp(db->flags);
|
||||
kvx->clc.k.cmp = builtin_keycmp(db->flags);
|
||||
}
|
||||
kvx->clc.v.lmin = v_lmin;
|
||||
osal_memory_fence(mo_Relaxed, true);
|
||||
kvx->clc.v.lmax = v_lmax;
|
||||
osal_memory_fence(mo_AcquireRelease, true);
|
||||
|
||||
if ((db->flags & (MDBX_DUPFIXED | MDBX_INTEGERDUP)) != 0 && db->dupfix_size) {
|
||||
if (!MDBX_DISABLE_VALIDATION && unlikely(db->dupfix_size < kvx->clc.v.lmin || db->dupfix_size > kvx->clc.v.lmax)) {
|
||||
ERROR("db.dupfix_size (%u) <> min/max value-length (%zu/%zu)", db->dupfix_size, kvx->clc.v.lmin, kvx->clc.v.lmax);
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
kvx->clc.v.lmin = kvx->clc.v.lmax = db->dupfix_size;
|
||||
}
|
||||
eASSERT(env, kvx->clc.k.lmax >= kvx->clc.k.lmin);
|
||||
eASSERT(env, kvx->clc.v.lmax >= kvx->clc.v.lmin);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
@ -86,10 +95,13 @@ int tbl_fetch(MDBX_txn *txn, size_t dbi) {
|
||||
return MDBX_CORRUPTED;
|
||||
}
|
||||
#endif /* !MDBX_DISABLE_VALIDATION */
|
||||
rc = tbl_setup(txn->env, kvx, db);
|
||||
rc = tbl_setup_ifneed(txn->env, kvx, db);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
|
||||
if (unlikely(dbi_changed(txn, dbi)))
|
||||
return MDBX_BAD_DBI;
|
||||
|
||||
txn->dbi_state[dbi] &= ~DBI_STALE;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user