mirror of
				https://github.com/isar/libmdbx.git
				synced 2025-10-31 15:38:57 +08:00 
			
		
		
		
	mdbx-tools: rework mdbx_reader_list() & mdbx_stat.
Change-Id: I0524cad93ca439e74eba9486cbcbeacf4253dd84
This commit is contained in:
		
							
								
								
									
										40
									
								
								mdbx.h
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								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] 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. | ||||
|  * | ||||
|  * Dump the entries in the reader lock table. | ||||
| /* Enumarete the entries in the reader lock table. | ||||
|  * | ||||
|  * [in] env     An environment handle returned by mdbx_env_create() | ||||
|  * [in] func  A MDBX_msg_func function | ||||
|  * [in] ctx   Anything the message function needs | ||||
|  * [in] func    A MDBX_reader_list_func function | ||||
|  * [in] ctx     An arbitrary context pointer for the enumeration function. | ||||
|  * | ||||
|  * 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. | ||||
|   | ||||
| @@ -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; | ||||
|       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(buf, ctx); | ||||
|         if (rc < 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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|       rc = mdbx_reader_check(env, &dead); | ||||
|       if (rc == MDBX_RESULT_TRUE) { | ||||
|         printf("  %d stale readers cleared.\n", dead); | ||||
|       rc = mdbx_reader_list(env, (MDBX_msg_func *)fputs, stdout); | ||||
|         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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user