mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-19 22:28:21 +08:00
mdbx: рефакторинг mdbx_txn_commit_ex()
5/5 (вычленение txn_basal_end()
).
This commit is contained in:
parent
6d92a778a5
commit
10e7e5c899
@ -271,8 +271,8 @@ int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void latency_gcprof(MDBX_commit_latency *latency, const MDBX_txn *txn) {
|
static void latency_gcprof(MDBX_commit_latency *latency, const MDBX_txn *txn) {
|
||||||
if (latency && MDBX_ENABLE_PROFGC) {
|
MDBX_env *const env = txn->env;
|
||||||
MDBX_env *const env = txn->env;
|
if (latency && likely(env->lck) && MDBX_ENABLE_PROFGC) {
|
||||||
pgop_stat_t *const ptr = &env->lck->pgops;
|
pgop_stat_t *const ptr = &env->lck->pgops;
|
||||||
latency->gc_prof.work_counter = ptr->gc_prof.work.spe_counter;
|
latency->gc_prof.work_counter = ptr->gc_prof.work.spe_counter;
|
||||||
latency->gc_prof.work_rtime_monotonic = osal_monotime_to_16dot16(ptr->gc_prof.work.rtime_monotonic);
|
latency->gc_prof.work_rtime_monotonic = osal_monotime_to_16dot16(ptr->gc_prof.work.rtime_monotonic);
|
||||||
@ -336,12 +336,9 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
|
|||||||
struct commit_timestamp ts;
|
struct commit_timestamp ts;
|
||||||
latency_init(latency, &ts);
|
latency_init(latency, &ts);
|
||||||
|
|
||||||
/* txn_end() mode for a commit which writes nothing */
|
|
||||||
unsigned end_mode = TXN_END_PURE_COMMIT | TXN_END_UPDATE | TXN_END_SLOT | TXN_END_FREE;
|
|
||||||
|
|
||||||
int rc = check_txn(txn, MDBX_TXN_FINISHED);
|
int rc = check_txn(txn, MDBX_TXN_FINISHED);
|
||||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||||
if (rc == MDBX_BAD_TXN && (txn->flags & MDBX_TXN_RDONLY)) {
|
if (rc == MDBX_BAD_TXN && F_ISSET(txn->flags, MDBX_TXN_FINISHED | MDBX_TXN_RDONLY)) {
|
||||||
rc = MDBX_RESULT_TRUE;
|
rc = MDBX_RESULT_TRUE;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -355,19 +352,18 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
|
|||||||
return LOG_IFERR(rc);
|
return LOG_IFERR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(txn->flags & MDBX_TXN_RDONLY)) {
|
if (txn->flags & MDBX_TXN_RDONLY) {
|
||||||
if (unlikely(txn->parent || (txn->flags & MDBX_TXN_HAS_CHILD) || txn == env->txn || txn == env->basal_txn)) {
|
if (unlikely(txn->parent || (txn->flags & MDBX_TXN_HAS_CHILD) || txn == env->txn || txn == env->basal_txn)) {
|
||||||
ERROR("attempt to commit %s txn %p", "strange read-only", (void *)txn);
|
ERROR("attempt to commit %s txn %p", "strange read-only", (void *)txn);
|
||||||
return MDBX_PROBLEM;
|
return MDBX_PROBLEM;
|
||||||
}
|
}
|
||||||
if (txn->flags & MDBX_TXN_ERROR) {
|
latency_gcprof(latency, txn);
|
||||||
rc = MDBX_RESULT_TRUE;
|
rc = (txn->flags & MDBX_TXN_ERROR) ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
|
||||||
goto fail;
|
txn_end(txn, TXN_END_PURE_COMMIT | TXN_END_UPDATE | TXN_END_SLOT | TXN_END_FREE);
|
||||||
}
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!txn->parent && (txn->flags & MDBX_NOSTICKYTHREADS) && unlikely(txn->owner != osal_thread_self())) {
|
if ((txn->flags & MDBX_NOSTICKYTHREADS) && txn == env->basal_txn && unlikely(txn->owner != osal_thread_self())) {
|
||||||
txn->flags |= MDBX_TXN_ERROR;
|
txn->flags |= MDBX_TXN_ERROR;
|
||||||
rc = MDBX_THREAD_MISMATCH;
|
rc = MDBX_THREAD_MISMATCH;
|
||||||
return LOG_IFERR(rc);
|
return LOG_IFERR(rc);
|
||||||
@ -375,7 +371,12 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
|
|||||||
|
|
||||||
if (unlikely(txn->flags & MDBX_TXN_ERROR)) {
|
if (unlikely(txn->flags & MDBX_TXN_ERROR)) {
|
||||||
rc = MDBX_RESULT_TRUE;
|
rc = MDBX_RESULT_TRUE;
|
||||||
goto fail;
|
fail:
|
||||||
|
latency_gcprof(latency, txn);
|
||||||
|
int err = txn_abort(txn);
|
||||||
|
if (unlikely(err != MDBX_SUCCESS))
|
||||||
|
rc = err;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txn->nested) {
|
if (txn->nested) {
|
||||||
@ -395,46 +396,27 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
|
|||||||
ERROR("attempt to commit %s txn %p", "strange nested", (void *)txn);
|
ERROR("attempt to commit %s txn %p", "strange nested", (void *)txn);
|
||||||
return MDBX_PROBLEM;
|
return MDBX_PROBLEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
latency_gcprof(latency, txn);
|
||||||
rc = txn_nested_join(txn, latency ? &ts : nullptr);
|
rc = txn_nested_join(txn, latency ? &ts : nullptr);
|
||||||
if (likely(rc == MDBX_SUCCESS))
|
goto done;
|
||||||
goto provide_latency;
|
|
||||||
if (rc == MDBX_RESULT_TRUE) {
|
|
||||||
/* fast completion of pure nested transaction */
|
|
||||||
end_mode = TXN_END_PURE_COMMIT | TXN_END_SLOT | TXN_END_FREE;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = txn_basal_commit(txn, latency ? &ts : nullptr);
|
rc = txn_basal_commit(txn, latency ? &ts : nullptr);
|
||||||
end_mode = TXN_END_COMMITTED | TXN_END_UPDATE;
|
latency_gcprof(latency, txn);
|
||||||
if (likely(rc == MDBX_SUCCESS))
|
int end = TXN_END_COMMITTED | TXN_END_UPDATE;
|
||||||
goto done;
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||||
if (rc == MDBX_RESULT_TRUE) {
|
end = TXN_END_ABORT;
|
||||||
#if MDBX_NOSUCCESS_PURE_COMMIT
|
if (rc == MDBX_RESULT_TRUE) {
|
||||||
rc = txn_end(txn, end_mode);
|
end = TXN_END_PURE_COMMIT | TXN_END_UPDATE;
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
rc = MDBX_NOSUCCESS_PURE_COMMIT ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
|
||||||
goto fail;
|
}
|
||||||
rc = MDBX_RESULT_TRUE;
|
|
||||||
goto provide_latency;
|
|
||||||
#else
|
|
||||||
goto done;
|
|
||||||
#endif /* MDBX_NOSUCCESS_PURE_COMMIT */
|
|
||||||
}
|
}
|
||||||
|
int err = txn_end(txn, end);
|
||||||
fail:
|
if (unlikely(err != MDBX_SUCCESS))
|
||||||
txn->flags |= MDBX_TXN_ERROR;
|
rc = err;
|
||||||
if (latency)
|
|
||||||
latency_gcprof(latency, txn);
|
|
||||||
txn_abort(txn);
|
|
||||||
goto provide_latency;
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (latency)
|
|
||||||
latency_gcprof(latency, txn);
|
|
||||||
rc = txn_end(txn, end_mode);
|
|
||||||
|
|
||||||
provide_latency:
|
|
||||||
latency_done(latency, &ts);
|
latency_done(latency, &ts);
|
||||||
return LOG_IFERR(rc);
|
return LOG_IFERR(rc);
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,8 @@ struct commit_timestamp {
|
|||||||
};
|
};
|
||||||
MDBX_INTERNAL int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts);
|
MDBX_INTERNAL int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts);
|
||||||
MDBX_INTERNAL int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts);
|
MDBX_INTERNAL int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts);
|
||||||
|
MDBX_INTERNAL int txn_basal_end(MDBX_txn *txn, unsigned mode);
|
||||||
|
MDBX_INTERNAL int txn_rdo_end(MDBX_txn *txn, unsigned mode);
|
||||||
|
|
||||||
/* env.c */
|
/* env.c */
|
||||||
MDBX_INTERNAL int env_open(MDBX_env *env, mdbx_mode_t mode);
|
MDBX_INTERNAL int env_open(MDBX_env *env, mdbx_mode_t mode);
|
||||||
|
167
src/txn.c
167
src/txn.c
@ -439,6 +439,7 @@ int txn_abort(MDBX_txn *txn) {
|
|||||||
txn_abort(txn->nested);
|
txn_abort(txn->nested);
|
||||||
|
|
||||||
tASSERT(txn, (txn->flags & MDBX_TXN_ERROR) || dpl_check(txn));
|
tASSERT(txn, (txn->flags & MDBX_TXN_ERROR) || dpl_check(txn));
|
||||||
|
txn->flags |= /* avoid merge cursors' state */ MDBX_TXN_ERROR;
|
||||||
return txn_end(txn, TXN_END_ABORT | TXN_END_SLOT | TXN_END_FREE);
|
return txn_end(txn, TXN_END_ABORT | TXN_END_SLOT | TXN_END_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -839,8 +840,9 @@ bailout:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int txn_ro_end(MDBX_txn *txn, unsigned mode) {
|
int txn_ro_end(MDBX_txn *txn, unsigned mode) {
|
||||||
MDBX_env *const env = txn->env;
|
MDBX_env *const env = txn->env;
|
||||||
|
tASSERT(txn, (txn->flags & txn_may_have_cursors) == 0);
|
||||||
txn->n_dbi = 0; /* prevent further DBI activity */
|
txn->n_dbi = 0; /* prevent further DBI activity */
|
||||||
if (txn->to.reader) {
|
if (txn->to.reader) {
|
||||||
reader_slot_t *slot = txn->to.reader;
|
reader_slot_t *slot = txn->to.reader;
|
||||||
@ -899,43 +901,14 @@ int txn_end(MDBX_txn *txn, unsigned mode) {
|
|||||||
txn->dbs[MAIN_DBI].root, txn->dbs[FREE_DBI].root);
|
txn->dbs[MAIN_DBI].root, txn->dbs[FREE_DBI].root);
|
||||||
|
|
||||||
tASSERT(txn, txn->signature == txn_signature && !txn->nested && !(txn->flags & MDBX_TXN_HAS_CHILD));
|
tASSERT(txn, txn->signature == txn_signature && !txn->nested && !(txn->flags & MDBX_TXN_HAS_CHILD));
|
||||||
if (txn->flags & txn_may_have_cursors) {
|
if (txn->flags & txn_may_have_cursors)
|
||||||
txn->flags |= /* avoid merge cursors' state */ MDBX_TXN_ERROR;
|
|
||||||
txn_done_cursors(txn);
|
txn_done_cursors(txn);
|
||||||
}
|
|
||||||
|
|
||||||
MDBX_env *const env = txn->env;
|
MDBX_env *const env = txn->env;
|
||||||
MDBX_txn *const parent = txn->parent;
|
MDBX_txn *const parent = txn->parent;
|
||||||
if (txn == env->basal_txn) {
|
if (txn == env->basal_txn) {
|
||||||
tASSERT(txn, !parent && !(txn->flags & MDBX_TXN_RDONLY));
|
tASSERT(txn, !parent && !(txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED)) && txn->owner);
|
||||||
tASSERT(txn, (txn->flags & MDBX_TXN_FINISHED) == 0 && txn->owner);
|
return txn_basal_end(txn, mode);
|
||||||
if (unlikely(txn->flags & MDBX_TXN_FINISHED))
|
|
||||||
return MDBX_SUCCESS;
|
|
||||||
|
|
||||||
ENSURE(env, txn->txnid >= /* paranoia is appropriate here */ env->lck->cached_oldest.weak);
|
|
||||||
dxb_sanitize_tail(env, nullptr);
|
|
||||||
|
|
||||||
txn->flags = MDBX_TXN_FINISHED;
|
|
||||||
env->txn = nullptr;
|
|
||||||
pnl_free(txn->tw.spilled.list);
|
|
||||||
txn->tw.spilled.list = nullptr;
|
|
||||||
|
|
||||||
eASSERT(env, txn->parent == nullptr);
|
|
||||||
pnl_shrink(&txn->tw.retired_pages);
|
|
||||||
pnl_shrink(&txn->tw.repnl);
|
|
||||||
if (!(env->flags & MDBX_WRITEMAP))
|
|
||||||
dpl_release_shadows(txn);
|
|
||||||
|
|
||||||
/* Export or close DBI handles created in this txn */
|
|
||||||
int err = dbi_update(txn, (mode & TXN_END_UPDATE) != 0);
|
|
||||||
if (unlikely(err != MDBX_SUCCESS)) {
|
|
||||||
ERROR("unexpected error %d during export the state of dbi-handles to env", err);
|
|
||||||
err = MDBX_PROBLEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The writer mutex was locked in mdbx_txn_begin. */
|
|
||||||
lck_txn_unlock(env);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txn->flags & MDBX_TXN_RDONLY) {
|
if (txn->flags & MDBX_TXN_RDONLY) {
|
||||||
@ -1072,7 +1045,7 @@ int txn_unpark(MDBX_txn *txn) {
|
|||||||
return err ? err : MDBX_OUSTED;
|
return err ? err : MDBX_OUSTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBX_txn *txn_basal_create(const size_t max_dbi) {
|
__cold MDBX_txn *txn_basal_create(const size_t max_dbi) {
|
||||||
MDBX_txn *txn = nullptr;
|
MDBX_txn *txn = nullptr;
|
||||||
const intptr_t bitmap_bytes =
|
const intptr_t bitmap_bytes =
|
||||||
#if MDBX_ENABLE_DBI_SPARSE
|
#if MDBX_ENABLE_DBI_SPARSE
|
||||||
@ -1107,7 +1080,7 @@ MDBX_txn *txn_basal_create(const size_t max_dbi) {
|
|||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void txn_basal_destroy(MDBX_txn *txn) {
|
__cold void txn_basal_destroy(MDBX_txn *txn) {
|
||||||
dpl_free(txn);
|
dpl_free(txn);
|
||||||
txl_free(txn->tw.gc.retxl);
|
txl_free(txn->tw.gc.retxl);
|
||||||
pnl_free(txn->tw.retired_pages);
|
pnl_free(txn->tw.retired_pages);
|
||||||
@ -1266,6 +1239,7 @@ int txn_nested_create(MDBX_txn *parent, const MDBX_txn_flags_t flags) {
|
|||||||
|
|
||||||
void txn_nested_abort(MDBX_txn *nested) {
|
void txn_nested_abort(MDBX_txn *nested) {
|
||||||
MDBX_txn *const parent = nested->parent;
|
MDBX_txn *const parent = nested->parent;
|
||||||
|
tASSERT(nested, !(nested->flags & txn_may_have_cursors));
|
||||||
nested->signature = 0;
|
nested->signature = 0;
|
||||||
nested->owner = 0;
|
nested->owner = 0;
|
||||||
|
|
||||||
@ -1314,7 +1288,7 @@ int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts) {
|
|||||||
tASSERT(txn, txn->tw.loose_count == 0);
|
tASSERT(txn, txn->tw.loose_count == 0);
|
||||||
|
|
||||||
VERBOSE("fast-complete pure nested txn %" PRIaTXN, txn->txnid);
|
VERBOSE("fast-complete pure nested txn %" PRIaTXN, txn->txnid);
|
||||||
return MDBX_RESULT_TRUE;
|
return txn_end(txn, TXN_END_PURE_COMMIT | TXN_END_SLOT | TXN_END_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Preserve space for spill list to avoid parent's state corruption
|
/* Preserve space for spill list to avoid parent's state corruption
|
||||||
@ -1396,6 +1370,7 @@ int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts) {
|
|||||||
txn_merge(parent, txn, parent_retired_len);
|
txn_merge(parent, txn, parent_retired_len);
|
||||||
env->txn = parent;
|
env->txn = parent;
|
||||||
parent->nested = nullptr;
|
parent->nested = nullptr;
|
||||||
|
parent->flags &= ~MDBX_TXN_HAS_CHILD;
|
||||||
tASSERT(parent, dpl_check(parent));
|
tASSERT(parent, dpl_check(parent));
|
||||||
|
|
||||||
#if MDBX_ENABLE_REFUND
|
#if MDBX_ENABLE_REFUND
|
||||||
@ -1419,6 +1394,35 @@ int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts) {
|
|||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int txn_basal_end(MDBX_txn *txn, unsigned mode) {
|
||||||
|
MDBX_env *const env = txn->env;
|
||||||
|
tASSERT(txn, (txn->flags & (MDBX_TXN_FINISHED | txn_may_have_cursors)) == 0 && txn->owner);
|
||||||
|
ENSURE(env, txn->txnid >= /* paranoia is appropriate here */ env->lck->cached_oldest.weak);
|
||||||
|
dxb_sanitize_tail(env, nullptr);
|
||||||
|
|
||||||
|
txn->flags = MDBX_TXN_FINISHED;
|
||||||
|
env->txn = nullptr;
|
||||||
|
pnl_free(txn->tw.spilled.list);
|
||||||
|
txn->tw.spilled.list = nullptr;
|
||||||
|
|
||||||
|
eASSERT(env, txn->parent == nullptr);
|
||||||
|
pnl_shrink(&txn->tw.retired_pages);
|
||||||
|
pnl_shrink(&txn->tw.repnl);
|
||||||
|
if (!(env->flags & MDBX_WRITEMAP))
|
||||||
|
dpl_release_shadows(txn);
|
||||||
|
|
||||||
|
/* Export or close DBI handles created in this txn */
|
||||||
|
int err = dbi_update(txn, (mode & TXN_END_UPDATE) != 0);
|
||||||
|
if (unlikely(err != MDBX_SUCCESS)) {
|
||||||
|
ERROR("unexpected error %d during export the state of dbi-handles to env", err);
|
||||||
|
err = MDBX_PROBLEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The writer mutex was locked in mdbx_txn_begin. */
|
||||||
|
lck_txn_unlock(env);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts) {
|
int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts) {
|
||||||
MDBX_env *const env = txn->env;
|
MDBX_env *const env = txn->env;
|
||||||
tASSERT(txn, txn == env->basal_txn && !txn->parent && !txn->nested);
|
tASSERT(txn, txn == env->basal_txn && !txn->parent && !txn->nested);
|
||||||
@ -1432,8 +1436,53 @@ int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts) {
|
|||||||
if (txn->flags & txn_may_have_cursors)
|
if (txn->flags & txn_may_have_cursors)
|
||||||
txn_done_cursors(txn);
|
txn_done_cursors(txn);
|
||||||
|
|
||||||
|
bool need_flush_for_nometasync = false;
|
||||||
|
const meta_ptr_t head = meta_recent(env, &txn->tw.troika);
|
||||||
|
const uint32_t meta_sync_txnid = atomic_load32(&env->lck->meta_sync_txnid, mo_Relaxed);
|
||||||
|
/* sync prev meta */
|
||||||
|
if (head.is_steady && meta_sync_txnid != (uint32_t)head.txnid) {
|
||||||
|
/* Исправление унаследованного от LMDB недочета:
|
||||||
|
*
|
||||||
|
* Всё хорошо, если все процессы работающие с БД не используют WRITEMAP.
|
||||||
|
* Тогда мета-страница (обновленная, но не сброшенная на диск) будет
|
||||||
|
* сохранена в результате fdatasync() при записи данных этой транзакции.
|
||||||
|
*
|
||||||
|
* Всё хорошо, если все процессы работающие с БД используют WRITEMAP
|
||||||
|
* без MDBX_AVOID_MSYNC.
|
||||||
|
* Тогда мета-страница (обновленная, но не сброшенная на диск) будет
|
||||||
|
* сохранена в результате msync() при записи данных этой транзакции.
|
||||||
|
*
|
||||||
|
* Если же в процессах работающих с БД используется оба метода, как sync()
|
||||||
|
* в режиме MDBX_WRITEMAP, так и записи через файловый дескриптор, то
|
||||||
|
* становится невозможным обеспечить фиксацию на диске мета-страницы
|
||||||
|
* предыдущей транзакции и данных текущей транзакции, за счет одной
|
||||||
|
* sync-операцией выполняемой после записи данных текущей транзакции.
|
||||||
|
* Соответственно, требуется явно обновлять мета-страницу, что полностью
|
||||||
|
* уничтожает выгоду от NOMETASYNC. */
|
||||||
|
const uint32_t txnid_dist = ((txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC) ? MDBX_NOMETASYNC_LAZY_FD
|
||||||
|
: MDBX_NOMETASYNC_LAZY_WRITEMAP;
|
||||||
|
/* Смысл "магии" в том, чтобы избежать отдельного вызова fdatasync()
|
||||||
|
* или msync() для гарантированной фиксации на диске мета-страницы,
|
||||||
|
* которая была "лениво" отправлена на запись в предыдущей транзакции,
|
||||||
|
* но не сброшена на диск из-за активного режима MDBX_NOMETASYNC. */
|
||||||
|
if (
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
!env->ioring.overlapped_fd &&
|
||||||
|
#endif
|
||||||
|
meta_sync_txnid == (uint32_t)head.txnid - txnid_dist)
|
||||||
|
need_flush_for_nometasync = true;
|
||||||
|
else {
|
||||||
|
int err = meta_sync(env, head);
|
||||||
|
if (unlikely(err != MDBX_SUCCESS)) {
|
||||||
|
ERROR("txn-%s: error %d", "presync-meta", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((!txn->tw.dirtylist || txn->tw.dirtylist->length == 0) &&
|
if ((!txn->tw.dirtylist || txn->tw.dirtylist->length == 0) &&
|
||||||
(txn->flags & (MDBX_TXN_DIRTY | MDBX_TXN_SPILLS)) == 0) {
|
(txn->flags & (MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | MDBX_TXN_NOSYNC | MDBX_TXN_NOMETASYNC)) == 0 &&
|
||||||
|
!need_flush_for_nometasync && !head.is_steady && !AUDIT_ENABLED()) {
|
||||||
TXN_FOREACH_DBI_ALL(txn, i) { tASSERT(txn, !(txn->dbi_state[i] & DBI_DIRTY)); }
|
TXN_FOREACH_DBI_ALL(txn, i) { tASSERT(txn, !(txn->dbi_state[i] & DBI_DIRTY)); }
|
||||||
/* fast completion of pure transaction */
|
/* fast completion of pure transaction */
|
||||||
return MDBX_NOSUCCESS_PURE_COMMIT ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
|
return MDBX_NOSUCCESS_PURE_COMMIT ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
|
||||||
@ -1497,50 +1546,6 @@ int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool need_flush_for_nometasync = false;
|
|
||||||
const meta_ptr_t head = meta_recent(env, &txn->tw.troika);
|
|
||||||
const uint32_t meta_sync_txnid = atomic_load32(&env->lck->meta_sync_txnid, mo_Relaxed);
|
|
||||||
/* sync prev meta */
|
|
||||||
if (head.is_steady && meta_sync_txnid != (uint32_t)head.txnid) {
|
|
||||||
/* Исправление унаследованного от LMDB недочета:
|
|
||||||
*
|
|
||||||
* Всё хорошо, если все процессы работающие с БД не используют WRITEMAP.
|
|
||||||
* Тогда мета-страница (обновленная, но не сброшенная на диск) будет
|
|
||||||
* сохранена в результате fdatasync() при записи данных этой транзакции.
|
|
||||||
*
|
|
||||||
* Всё хорошо, если все процессы работающие с БД используют WRITEMAP
|
|
||||||
* без MDBX_AVOID_MSYNC.
|
|
||||||
* Тогда мета-страница (обновленная, но не сброшенная на диск) будет
|
|
||||||
* сохранена в результате msync() при записи данных этой транзакции.
|
|
||||||
*
|
|
||||||
* Если же в процессах работающих с БД используется оба метода, как sync()
|
|
||||||
* в режиме MDBX_WRITEMAP, так и записи через файловый дескриптор, то
|
|
||||||
* становится невозможным обеспечить фиксацию на диске мета-страницы
|
|
||||||
* предыдущей транзакции и данных текущей транзакции, за счет одной
|
|
||||||
* sync-операцией выполняемой после записи данных текущей транзакции.
|
|
||||||
* Соответственно, требуется явно обновлять мета-страницу, что полностью
|
|
||||||
* уничтожает выгоду от NOMETASYNC. */
|
|
||||||
const uint32_t txnid_dist = ((txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC) ? MDBX_NOMETASYNC_LAZY_FD
|
|
||||||
: MDBX_NOMETASYNC_LAZY_WRITEMAP;
|
|
||||||
/* Смысл "магии" в том, чтобы избежать отдельного вызова fdatasync()
|
|
||||||
* или msync() для гарантированной фиксации на диске мета-страницы,
|
|
||||||
* которая была "лениво" отправлена на запись в предыдущей транзакции,
|
|
||||||
* но не сброшена на диск из-за активного режима MDBX_NOMETASYNC. */
|
|
||||||
if (
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
!env->ioring.overlapped_fd &&
|
|
||||||
#endif
|
|
||||||
meta_sync_txnid == (uint32_t)head.txnid - txnid_dist)
|
|
||||||
need_flush_for_nometasync = true;
|
|
||||||
else {
|
|
||||||
rc = meta_sync(env, head);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
|
||||||
ERROR("txn-%s: error %d", "presync-meta", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txn->tw.dirtylist) {
|
if (txn->tw.dirtylist) {
|
||||||
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
|
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
|
||||||
tASSERT(txn, txn->tw.loose_count == 0);
|
tASSERT(txn, txn->tw.loose_count == 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user