From 7c39c16829706f9b73af840fa2d568cba4799877 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Thu, 19 Sep 2019 00:54:03 +0300 Subject: [PATCH] mdbx-tools: rework mdbx_reader_list() & mdbx_stat. Change-Id: I0524cad93ca439e74eba9486cbcbeacf4253dd84 --- mdbx.h | 44 +++++++++++++++++------- src/elements/core.c | 80 +++++++++++++++++++++++++++---------------- src/tools/mdbx_stat.c | 44 ++++++++++++++++++++---- 3 files changed, 119 insertions(+), 49 deletions(-) diff --git a/mdbx.h b/mdbx.h index 9197021d..6172af72 100644 --- a/mdbx.h +++ b/mdbx.h @@ -2716,31 +2716,49 @@ LIBMDBX_API int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, LIBMDBX_API int mdbx_dcmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b); -/* A callback function used to print a message from the library. +/* A callback function used to enumerate the reader lock table. * - * [in] msg The string to be printed. - * [in] ctx An arbitrary context pointer for the callback. + * [in] ctx An arbitrary context pointer for the callback. + * [in] num The serial number during enumeration, starting from 1. + * [in] slot The reader lock table slot number. + * [in] txnid The ID of the transaction being read, + * i.e. the MVCC-snaphot number. + * [in] lag The lag from a recent MVCC-snapshot, i.e. the number of + * committed transaction since read transaction started. + * [in] pid The reader process ID. + * [in] thread The reader thread ID. + * [in] bytes_used The number of last used page in the MVCC-snapshot which + * being read, i.e. database file can't shrinked beyond this. + * [in] bytes_retired The total size of the database pages that were retired by + * committed write transactions after the reader's + * MVCC-snapshot, i.e. the space which would be freed after + * the Reader releases the MVCC-snapshot for reuse by + * completion read transaction. * * Returns < 0 on failure, >= 0 on success. */ -typedef int(MDBX_msg_func)(const char *msg, void *ctx); +typedef int(MDBX_reader_list_func)(void *ctx, int num, int slot, mdbx_pid_t pid, + mdbx_tid_t thread, uint64_t txnid, + uint64_t lag, size_t bytes_used, + size_t bytes_retired); -/* FIXME: Rework this function. +/* Enumarete the entries in the reader lock table. * - * Dump the entries in the reader lock table. + * [in] env An environment handle returned by mdbx_env_create() + * [in] func A MDBX_reader_list_func function + * [in] ctx An arbitrary context pointer for the enumeration function. * - * [in] env An environment handle returned by mdbx_env_create() - * [in] func A MDBX_msg_func function - * [in] ctx Anything the message function needs - * - * Returns < 0 on failure, >= 0 on success. */ -LIBMDBX_API int mdbx_reader_list(MDBX_env *env, MDBX_msg_func *func, void *ctx); + * Returns A non-zero error value on failure and 0 on success, + * or MDBX_RESULT_TRUE (-1) if the reader lock table is empty. */ +LIBMDBX_API int mdbx_reader_list(MDBX_env *env, MDBX_reader_list_func *func, + void *ctx); /* Check for stale entries in the reader lock table. * * [in] env An environment handle returned by mdbx_env_create() * [out] dead Number of stale slots that were cleared * - * Returns 0 on success, non-zero on failure. */ + * Returns A non-zero error value on failure and 0 on success, + * or MDBX_RESULT_TRUE (-1) if a dead reader(s) found or mutex was recovered. */ LIBMDBX_API int mdbx_reader_check(MDBX_env *env, int *dead); /* Returns a lag of the reading for the given transaction. diff --git a/src/elements/core.c b/src/elements/core.c index 7cdf6bd1..36c83fba 100644 --- a/src/elements/core.c +++ b/src/elements/core.c @@ -13195,46 +13195,66 @@ int mdbx_set_dupsort(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) { return MDBX_SUCCESS; } -int __cold mdbx_reader_list(MDBX_env *env, MDBX_msg_func *func, void *ctx) { - char buf[64]; - int rc = 0, first = 1; - +int __cold mdbx_reader_list(MDBX_env *env, MDBX_reader_list_func *func, + void *ctx) { if (unlikely(!env || !func)) - return (MDBX_EINVAL > 0) ? -MDBX_EINVAL : MDBX_EINVAL; + return MDBX_EINVAL; if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - const MDBX_lockinfo *const lck = env->me_lck; - if (likely(lck)) { - const unsigned snap_nreaders = lck->mti_numreaders; + int rc = MDBX_RESULT_TRUE; + int serial = 0; + if (likely(env->me_lck)) { + const unsigned snap_nreaders = env->me_lck->mti_numreaders; for (unsigned i = 0; i < snap_nreaders; i++) { - if (lck->mti_readers[i].mr_pid) { - const txnid_t txnid = lck->mti_readers[i].mr_txnid; - if (txnid == ~(txnid_t)0) - snprintf(buf, sizeof(buf), "%10" PRIuPTR " %" PRIxPTR " -\n", - (uintptr_t)lck->mti_readers[i].mr_pid, - (uintptr_t)lck->mti_readers[i].mr_tid); - else - snprintf(buf, sizeof(buf), - "%10" PRIuPTR " %" PRIxPTR " %" PRIaTXN "\n", - (uintptr_t)lck->mti_readers[i].mr_pid, - (uintptr_t)lck->mti_readers[i].mr_tid, txnid); + const MDBX_reader *r = env->me_lck->mti_readers + i; + retry_reader:; + const mdbx_pid_t pid = r->mr_pid; + if (!pid) + continue; + txnid_t txnid = r->mr_txnid; + const mdbx_tid_t tid = r->mr_tid; + const pgno_t pages_used = r->mr_snapshot_pages_used; + const uint64_t reader_pages_retired = r->mr_snapshot_pages_retired; + mdbx_compiler_barrier(); + if (unlikely(pid != r->mr_pid || txnid != r->mr_txnid || + tid != r->mr_tid || + pages_used != r->mr_snapshot_pages_used || + reader_pages_retired != r->mr_snapshot_pages_retired)) + goto retry_reader; - if (first) { - first = 0; - rc = func(" pid thread txnid\n", ctx); - if (rc < 0) - break; - } - rc = func(buf, ctx); - if (rc < 0) - break; + mdbx_assert(env, txnid > 0); + if (txnid == ~(txnid_t)0) + txnid = 0; + + size_t bytes_used = 0; + size_t bytes_retained = 0; + uint64_t lag = 0; + if (txnid) { + retry_header:; + const MDBX_meta *const recent_meta = mdbx_meta_head(env); + const uint64_t head_pages_retired = recent_meta->mm_pages_retired; + const txnid_t head_txnid = mdbx_meta_txnid_fluid(env, recent_meta); + mdbx_compiler_barrier(); + if (unlikely(recent_meta != mdbx_meta_head(env) || + head_pages_retired != recent_meta->mm_pages_retired) || + head_txnid != mdbx_meta_txnid_fluid(env, recent_meta)) + goto retry_header; + + lag = head_txnid - txnid; + bytes_used = pgno2bytes(env, pages_used); + bytes_retained = (head_pages_retired > reader_pages_retired) + ? pgno2bytes(env, (pgno_t)(head_pages_retired - + reader_pages_retired)) + : 0; } + rc = func(ctx, ++serial, i, pid, tid, txnid, lag, bytes_used, + bytes_retained); + if (unlikely(rc != MDBX_SUCCESS)) + break; } } - if (first) - rc = func("(no active readers)\n", ctx); return rc; } diff --git a/src/tools/mdbx_stat.c b/src/tools/mdbx_stat.c index edcbaa62..cc5d5360 100644 --- a/src/tools/mdbx_stat.c +++ b/src/tools/mdbx_stat.c @@ -59,6 +59,27 @@ static void usage(char *prog) { exit(EXIT_FAILURE); } +static int reader_list_func(void *ctx, int num, int slot, mdbx_pid_t pid, + mdbx_tid_t thread, uint64_t txnid, uint64_t lag, + size_t bytes_used, size_t bytes_retired) { + (void)ctx; + if (num == 1) + printf("Reader Table Status\n" + " #\tslot\t%6s %*s %20s %10s %13s %13s\n", + "pid", (int)sizeof(size_t) * 2, "thread", "txnid", "lag", "used", + "retained"); + + printf(" %3d)\t[%d]\t%6" PRIdSIZE " %*" PRIxSIZE, num, slot, (size_t)pid, + (int)sizeof(size_t) * 2, (size_t)thread); + if (txnid) + printf(" %20" PRIu64 " %10" PRIu64 " %12.1fM %12.1fM\n", txnid, lag, + bytes_used / 1048576.0, bytes_retired / 1048576.0); + else + printf(" %20s %10s %13s %13s\n", "-", "0", "0", "0"); + + return user_break ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE; +} + int main(int argc, char *argv[]) { int o, rc; MDBX_env *env; @@ -206,13 +227,24 @@ int main(int argc, char *argv[]) { } if (rdrinfo) { - printf("Reader Table Status\n"); - rc = mdbx_reader_list(env, (MDBX_msg_func *)fputs, stdout); - if (rdrinfo > 1) { + rc = mdbx_reader_list(env, reader_list_func, nullptr); + if (rc == MDBX_RESULT_TRUE) + printf("Reader Table is empty\n"); + else if (rc == MDBX_SUCCESS && rdrinfo > 1) { int dead; - mdbx_reader_check(env, &dead); - printf(" %d stale readers cleared.\n", dead); - rc = mdbx_reader_list(env, (MDBX_msg_func *)fputs, stdout); + rc = mdbx_reader_check(env, &dead); + if (rc == MDBX_RESULT_TRUE) { + printf(" %d stale readers cleared.\n", dead); + rc = mdbx_reader_list(env, reader_list_func, nullptr); + if (rc == MDBX_RESULT_TRUE) + printf(" Now Reader Table is empty\n"); + } else + printf(" No stale readers.\n"); + } + if (MDBX_IS_ERROR(rc)) { + fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc, + mdbx_strerror(rc)); + goto env_close; } if (!(subname || alldbs || freinfo)) goto env_close;