From 5dfe3433a8f446d5c63a032bcccff011c6cd492a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru> Date: Thu, 20 Mar 2025 01:21:02 +0300 Subject: [PATCH] =?UTF-8?q?mdbx:=20=D1=83=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B3=D0=BE=D0=BD=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20`tbl=5Fsetup(MDBX=5FDUPFIXED=20|=20MDBX=5FINTEGERDUP)`?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5=20?= =?UTF-8?q?=D0=B2=20=D1=80=D0=B0=D0=B7=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=BA=D0=B0=D1=85=20(backport).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема была в том, что в случаях фиксированного размера значений clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались по актуальному размеру данных в БД. Поэтому при конкурентном вызове из разных потоков, один поток мог выполнять инициализацию, а второй прочитать временные/промежуточные значения lmin/lmax. В результате, при конкурентном старте транзакций в разных потоках при использовании только-что открытого dbi-хендла, проверка допустимости длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE. --- src/cogs.h | 4 ++++ src/cursor.c | 6 ++---- src/proto.h | 2 +- src/table.c | 40 ++++++++++++++++++++++++++-------------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/cogs.h b/src/cogs.h index 11d7fe1d..7e814e7b 100644 --- a/src/cogs.h +++ b/src/cogs.h @@ -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) { diff --git a/src/cursor.c b/src/cursor.c index ff2c67e8..01fc8a56 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -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); diff --git a/src/proto.h b/src/proto.h index f0a6657f..92bbce6d 100644 --- a/src/proto.h +++ b/src/proto.h @@ -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); diff --git a/src/table.c b/src/table.c index faa44da1..5b3e441a 100644 --- a/src/table.c +++ b/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 ((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; + 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); + + 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; }