mdbx: запрещение разного MDBX_WRITEMAP между процессами в режимах с отложенной/ленивой записью.

Ранее упущенный не очевидный момент: При работе БД в режимах
не-синхронной/отложенной фиксации на диске, все процессы-писатели должны
иметь одинаковый режим MDBX_WRITEMAP.

В противном случае, сброс на диск следует выполнять дважды: сначала
msync(), затем fdatasync(). При этом msync() не обязан отрабатывать в
процессах без MDBX_WRITEMAP, так как файл в память отображен только для
чтения. Поэтому, в общем случае, различия по MDBX_WRITEMAP не позволяют
выполнить фиксацию данных на диск, после их изменения в другом процессе.

В режиме MDBX_UTTERLY_NOSYNC позволять совместную работу с MDBX_WRITEMAP
также не следует, поскольку никакой процесс (в том числе последний) не
может гарантированно сбросить данные на диск, а следовательно не должен
помечать какую-либо транзакцию как steady.

В результате, требуется либо запретить совместную работу процессам с
разным MDBX_WRITEMAP в режиме отложенной записи, либо отслеживать такое
смешивание и блокировать steady-пометки - что контрпродуктивно.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-12-04 18:10:54 +03:00
parent db83bd34d2
commit 48a56d1d05

View File

@ -14328,12 +14328,6 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
} }
osal_fseek(env->me_lfd, safe_parking_lot_offset); osal_fseek(env->me_lfd, safe_parking_lot_offset);
const MDBX_env_flags_t rigorous_flags =
MDBX_SAFE_NOSYNC | MDBX_DEPRECATED_MAPASYNC;
const MDBX_env_flags_t mode_flags = rigorous_flags | MDBX_NOMETASYNC |
MDBX_LIFORECLAIM |
MDBX_DEPRECATED_COALESCE | MDBX_NORDAHEAD;
eASSERT(env, env->me_dsync_fd == INVALID_HANDLE_VALUE); eASSERT(env, env->me_dsync_fd == INVALID_HANDLE_VALUE);
if (!(flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | MDBX_DEPRECATED_MAPASYNC))) { if (!(flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | MDBX_DEPRECATED_MAPASYNC))) {
rc = osal_openfile(MDBX_OPEN_DXB_DSYNC, env, env_pathname.dxb, rc = osal_openfile(MDBX_OPEN_DXB_DSYNC, env, env_pathname.dxb,
@ -14345,11 +14339,19 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
} }
} }
const MDBX_env_flags_t lazy_flags =
MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC | MDBX_NOMETASYNC;
const MDBX_env_flags_t mode_flags = lazy_flags | MDBX_LIFORECLAIM |
MDBX_NORDAHEAD | MDBX_RDONLY |
MDBX_WRITEMAP;
MDBX_lockinfo *const lck = env->me_lck_mmap.lck; MDBX_lockinfo *const lck = env->me_lck_mmap.lck;
if (lck && lck_rc != MDBX_RESULT_TRUE && (env->me_flags & MDBX_RDONLY) == 0) { if (lck && lck_rc != MDBX_RESULT_TRUE && (env->me_flags & MDBX_RDONLY) == 0) {
while (atomic_load32(&lck->mti_envmode, mo_AcquireRelease) == MDBX_RDONLY) { MDBX_env_flags_t snap_flags;
while ((snap_flags = atomic_load32(&lck->mti_envmode, mo_AcquireRelease)) ==
MDBX_RDONLY) {
if (atomic_cas32(&lck->mti_envmode, MDBX_RDONLY, if (atomic_cas32(&lck->mti_envmode, MDBX_RDONLY,
env->me_flags & mode_flags)) { (snap_flags = (env->me_flags & mode_flags)))) {
/* The case: /* The case:
* - let's assume that for some reason the DB file is smaller * - let's assume that for some reason the DB file is smaller
* than it should be according to the geometry, * than it should be according to the geometry,
@ -14368,15 +14370,44 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
if (env->me_flags & MDBX_ACCEDE) { if (env->me_flags & MDBX_ACCEDE) {
/* Pickup current mode-flags (MDBX_LIFORECLAIM, MDBX_NORDAHEAD, etc). */ /* Pickup current mode-flags (MDBX_LIFORECLAIM, MDBX_NORDAHEAD, etc). */
const unsigned diff = const MDBX_env_flags_t diff =
(lck->mti_envmode.weak ^ env->me_flags) & mode_flags; (snap_flags ^ env->me_flags) &
NOTICE("accede mode-flags: 0x%X, 0x%X -> 0x%X", diff, env->me_flags, ((snap_flags & lazy_flags) ? mode_flags
env->me_flags ^ diff); : mode_flags & ~MDBX_WRITEMAP);
env->me_flags ^= diff; env->me_flags ^= diff;
NOTICE("accede mode-flags: 0x%X, 0x%X -> 0x%X", diff,
env->me_flags ^ diff, env->me_flags);
} }
if ((lck->mti_envmode.weak ^ env->me_flags) & rigorous_flags) { /* Ранее упущенный не очевидный момент: При работе БД в режимах
ERROR("%s", "current mode/flags incompatible with requested"); * не-синхронной/отложенной фиксации на диске, все процессы-писатели должны
* иметь одинаковый режим MDBX_WRITEMAP.
*
* В противном случае, сброс на диск следует выполнять дважды: сначала
* msync(), затем fdatasync(). При этом msync() не обязан отрабатывать
* в процессах без MDBX_WRITEMAP, так как файл в память отображен только
* для чтения. Поэтому, в общем случае, различия по MDBX_WRITEMAP не
* позволяют выполнить фиксацию данных на диск, после их изменения в другом
* процессе.
*
* В режиме MDBX_UTTERLY_NOSYNC позволять совместную работу с MDBX_WRITEMAP
* также не следует, поскольку никакой процесс (в том числе последний) не
* может гарантированно сбросить данные на диск, а следовательно не должен
* помечать какую-либо транзакцию как steady.
*
* В результате, требуется либо запретить совместную работу процессам с
* разным MDBX_WRITEMAP в режиме отложенной записи, либо отслеживать такое
* смешивание и блокировать steady-пометки - что контрпродуктивно. */
const MDBX_env_flags_t rigorous_flags =
(snap_flags & lazy_flags)
? MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC | MDBX_WRITEMAP
: MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC;
const MDBX_env_flags_t rigorous_diff =
(snap_flags ^ env->me_flags) & rigorous_flags;
if (rigorous_diff) {
ERROR("current mode/flags 0x%X incompatible with requested 0x%X, "
"rigorous diff 0x%X",
env->me_flags, snap_flags, rigorous_diff);
rc = MDBX_INCOMPATIBLE; rc = MDBX_INCOMPATIBLE;
goto bailout; goto bailout;
} }
@ -14397,11 +14428,14 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
} }
DEBUG("opened dbenv %p", (void *)env); DEBUG("opened dbenv %p", (void *)env);
if (!lck || lck_rc == MDBX_RESULT_TRUE) {
env->me_lck->mti_envmode.weak = env->me_flags & mode_flags;
env->me_lck->mti_meta_sync_txnid.weak =
(uint32_t)recent_committed_txnid(env);
env->me_lck->mti_reader_check_timestamp.weak = osal_monotime();
}
if (lck) { if (lck) {
if (lck_rc == MDBX_RESULT_TRUE) { if (lck_rc == MDBX_RESULT_TRUE) {
lck->mti_envmode.weak = env->me_flags & (mode_flags | MDBX_RDONLY);
lck->mti_meta_sync_txnid.weak = (uint32_t)recent_committed_txnid(env);
lck->mti_reader_check_timestamp.weak = osal_monotime();
rc = osal_lck_downgrade(env); rc = osal_lck_downgrade(env);
DEBUG("lck-downgrade-%s: rc %i", DEBUG("lck-downgrade-%s: rc %i",
(env->me_flags & MDBX_EXCLUSIVE) ? "partial" : "full", rc); (env->me_flags & MDBX_EXCLUSIVE) ? "partial" : "full", rc);
@ -14420,11 +14454,6 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
goto bailout; goto bailout;
env->me_flags |= MDBX_ENV_TXKEY; env->me_flags |= MDBX_ENV_TXKEY;
} }
} else {
env->me_lck->mti_envmode.weak = env->me_flags & (mode_flags | MDBX_RDONLY);
env->me_lck->mti_meta_sync_txnid.weak =
(uint32_t)recent_committed_txnid(env);
env->me_lck->mti_reader_check_timestamp.weak = osal_monotime();
} }
if ((flags & MDBX_RDONLY) == 0) { if ((flags & MDBX_RDONLY) == 0) {