mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-02 00:24:13 +08:00
mdbx: fix/rework mdbx_env_stat_ex()
to return stat of the whole env.
Resolves https://github.com/erthink/libmdbx/issues/190 Included fix for the first version of this change. Change-Id: I8357de24fda2ab4c93510574055b52d6e16b0c15
This commit is contained in:
parent
a56c72d190
commit
732d3cd2d4
158
src/core.c
158
src/core.c
@ -18866,66 +18866,130 @@ __cold int mdbx_env_get_fd(const MDBX_env *env, mdbx_filehandle_t *arg) {
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
/* Common code for mdbx_dbi_stat() and mdbx_env_stat().
|
||||
* [in] env the environment to operate in.
|
||||
* [in] db the MDBX_db record containing the stats to return.
|
||||
* [out] arg the address of an MDBX_stat structure to receive the stats.
|
||||
* Returns 0, this function always succeeds. */
|
||||
static void mdbx_stat0(const MDBX_env *env, const MDBX_db *db, MDBX_stat *dest,
|
||||
size_t bytes) {
|
||||
dest->ms_psize = env->me_psize;
|
||||
dest->ms_depth = db->md_depth;
|
||||
dest->ms_branch_pages = db->md_branch_pages;
|
||||
dest->ms_leaf_pages = db->md_leaf_pages;
|
||||
dest->ms_overflow_pages = db->md_overflow_pages;
|
||||
dest->ms_entries = db->md_entries;
|
||||
if (likely(bytes >=
|
||||
offsetof(MDBX_stat, ms_mod_txnid) + sizeof(dest->ms_mod_txnid)))
|
||||
dest->ms_mod_txnid = db->md_mod_txnid;
|
||||
}
|
||||
|
||||
#ifndef LIBMDBX_NO_EXPORTS_LEGACY_API
|
||||
__cold int mdbx_env_stat(const MDBX_env *env, MDBX_stat *stat, size_t bytes) {
|
||||
return __inline_mdbx_env_stat(env, stat, bytes);
|
||||
}
|
||||
#endif /* LIBMDBX_NO_EXPORTS_LEGACY_API */
|
||||
|
||||
static void stat_get(const MDBX_db *db, MDBX_stat *st, size_t bytes) {
|
||||
st->ms_depth = db->md_depth;
|
||||
st->ms_branch_pages = db->md_branch_pages;
|
||||
st->ms_leaf_pages = db->md_leaf_pages;
|
||||
st->ms_overflow_pages = db->md_overflow_pages;
|
||||
st->ms_entries = db->md_entries;
|
||||
if (likely(bytes >=
|
||||
offsetof(MDBX_stat, ms_mod_txnid) + sizeof(st->ms_mod_txnid)))
|
||||
st->ms_mod_txnid = db->md_mod_txnid;
|
||||
}
|
||||
|
||||
static void stat_add(const MDBX_db *db, MDBX_stat *const st,
|
||||
const size_t bytes) {
|
||||
st->ms_depth += db->md_depth;
|
||||
st->ms_branch_pages += db->md_branch_pages;
|
||||
st->ms_leaf_pages += db->md_leaf_pages;
|
||||
st->ms_overflow_pages += db->md_overflow_pages;
|
||||
st->ms_entries += db->md_entries;
|
||||
if (likely(bytes >=
|
||||
offsetof(MDBX_stat, ms_mod_txnid) + sizeof(st->ms_mod_txnid)))
|
||||
st->ms_mod_txnid = (st->ms_mod_txnid > db->md_mod_txnid) ? st->ms_mod_txnid
|
||||
: db->md_mod_txnid;
|
||||
}
|
||||
|
||||
static __cold int stat_acc(const MDBX_txn *txn, MDBX_stat *st, size_t bytes) {
|
||||
int err = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
st->ms_psize = txn->mt_env->me_psize;
|
||||
#if 1
|
||||
/* assuming GC is internal and not subject for accounting */
|
||||
stat_get(&txn->mt_dbs[MAIN_DBI], st, bytes);
|
||||
#else
|
||||
stat_get(&txn->mt_dbs[FREE_DBI], st, bytes);
|
||||
stat_add(&txn->mt_dbs[MAIN_DBI], st, bytes);
|
||||
#endif
|
||||
|
||||
/* account opened named subDBs */
|
||||
for (MDBX_dbi dbi = CORE_DBS; dbi < txn->mt_numdbs; dbi++)
|
||||
if ((txn->mt_dbistate[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID)
|
||||
stat_add(txn->mt_dbs + dbi, st, bytes);
|
||||
|
||||
if (!(txn->mt_dbs[MAIN_DBI].md_flags & (MDBX_DUPSORT | MDBX_INTEGERKEY)) &&
|
||||
txn->mt_dbs[MAIN_DBI].md_entries /* TODO: use `md_subs` field */) {
|
||||
MDBX_cursor_couple cx;
|
||||
err = mdbx_cursor_init(&cx.outer, (MDBX_txn *)txn, MAIN_DBI);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
/* scan and account not opened named subDBs */
|
||||
err = mdbx_page_search(&cx.outer, NULL, MDBX_PS_FIRST);
|
||||
while (err == MDBX_SUCCESS) {
|
||||
const MDBX_page *mp = cx.outer.mc_pg[cx.outer.mc_top];
|
||||
for (unsigned i = 0; i < page_numkeys(mp); i++) {
|
||||
const MDBX_node *node = page_node(mp, i);
|
||||
if (node_flags(node) != F_SUBDATA)
|
||||
continue;
|
||||
if (unlikely(node_ds(node) != sizeof(MDBX_db)))
|
||||
return MDBX_CORRUPTED;
|
||||
|
||||
/* skip opened and already accounted */
|
||||
for (MDBX_dbi dbi = CORE_DBS; dbi < txn->mt_numdbs; dbi++)
|
||||
if ((txn->mt_dbistate[dbi] & (DBI_VALID | DBI_STALE)) == DBI_VALID &&
|
||||
node_ks(node) == txn->mt_dbxs[dbi].md_name.iov_len &&
|
||||
memcmp(node_key(node), txn->mt_dbxs[dbi].md_name.iov_base,
|
||||
node_ks(node)) == 0) {
|
||||
node = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
MDBX_db db;
|
||||
memcpy(&db, node_data(node), sizeof(db));
|
||||
stat_add(&db, st, bytes);
|
||||
}
|
||||
}
|
||||
err = mdbx_cursor_sibling(&cx.outer, SIBLING_RIGHT);
|
||||
}
|
||||
if (unlikely(err != MDBX_NOTFOUND))
|
||||
return err;
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
__cold int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
|
||||
MDBX_stat *dest, size_t bytes) {
|
||||
if (unlikely((env == NULL && txn == NULL) || dest == NULL))
|
||||
if (unlikely(!dest))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (txn) {
|
||||
int err = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
}
|
||||
if (env) {
|
||||
int err = check_env(env, true);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
if (txn && unlikely(txn->mt_env != env))
|
||||
return MDBX_EINVAL;
|
||||
}
|
||||
|
||||
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
|
||||
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (txn) {
|
||||
mdbx_stat0(txn->mt_env, &txn->mt_dbs[MAIN_DBI], dest, bytes);
|
||||
return MDBX_SUCCESS;
|
||||
if (likely(txn)) {
|
||||
if (env && unlikely(txn->mt_env != env))
|
||||
return MDBX_EINVAL;
|
||||
return stat_acc(txn, dest, bytes);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
const MDBX_meta *const recent_meta = mdbx_meta_head(env);
|
||||
const txnid_t txnid = mdbx_meta_txnid_fluid(env, recent_meta);
|
||||
mdbx_stat0(env, &recent_meta->mm_dbs[MAIN_DBI], dest, bytes);
|
||||
mdbx_compiler_barrier();
|
||||
if (likely(txnid == mdbx_meta_txnid_fluid(env, recent_meta) &&
|
||||
recent_meta == mdbx_meta_head(env)))
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
int err = check_env(env, true);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
if (env->me_txn0 && env->me_txn0->mt_owner == mdbx_thread_self())
|
||||
/* inside write-txn */
|
||||
return stat_acc(env->me_txn, dest, bytes);
|
||||
|
||||
MDBX_txn *tmp_txn;
|
||||
err = mdbx_txn_begin((MDBX_env *)env, NULL, MDBX_TXN_RDONLY, &tmp_txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
|
||||
const int rc = stat_acc(tmp_txn, dest, bytes);
|
||||
err = mdbx_txn_abort(tmp_txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
return err;
|
||||
return rc;
|
||||
}
|
||||
|
||||
__cold int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
|
||||
@ -19479,7 +19543,9 @@ __cold int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest,
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
}
|
||||
mdbx_stat0(txn->mt_env, &txn->mt_dbs[dbi], dest, bytes);
|
||||
|
||||
dest->ms_psize = txn->mt_env->me_psize;
|
||||
stat_get(&txn->mt_dbs[dbi], dest, bytes);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user