This commit is contained in:
Henry Filgueiras 2026-05-27 11:29:28 +03:00 committed by GitHub
commit 1e93c4cbce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 2 deletions

View file

@ -44,6 +44,18 @@
static dictResizeEnable dict_can_resize = DICT_RESIZE_ENABLE;
static unsigned int dict_force_resize_ratio = 4;
/* Accumulator of microseconds spent in incremental rehash steps performed
* inside dict lookup/insert/delete operations. Read and reset by higher
* layers (server.c call()) to attribute per-command rehash cost. */
uint64_t dictRehashStepElapsedUs = 0;
/* Non-zero when latency monitoring is active
* (server.latency_monitor_threshold != 0). Set by server.c at the outermost
* call() entry and cleared at the outermost call() exit. While zero, the
* rehash timing path is skipped entirely: no clock reads, no accumulator
* writes - just one predictable branch. */
int dictRehashStepTiming = 0;
/* -------------------------- types ----------------------------------------- */
struct dictEntry {
struct dictEntry *next; /* Must be first */
@ -466,7 +478,19 @@ int dictRehashMicroseconds(dict *d, uint64_t us) {
* dictionary so that the hash table automatically migrates from H1 to H2
* while it is actively used. */
static void _dictRehashStep(dict *d) {
if (d->pauserehash == 0) dictRehash(d,1);
if (d->pauserehash == 0) {
/* Skip the timing path when latency monitoring is off, and also
* when getMonotonicUs is still NULL during early startup (core
* module registration runs dictAdd before monotonicInit). */
if (!dictRehashStepTiming || getMonotonicUs == NULL) {
dictRehash(d,1);
return;
}
monotime t;
elapsedStart(&t);
dictRehash(d,1);
dictRehashStepElapsedUs += elapsedUs(t);
}
}
/* Performs rehashing on a single bucket. */
@ -1705,6 +1729,12 @@ static void _dictShrinkIfNeeded(dict *d)
static void _dictRehashStepIfNeeded(dict *d, uint64_t visitedIdx) {
if ((!dictIsRehashing(d)) || (d->pauserehash != 0))
return;
/* Skip the timing path when latency monitoring is off, and also when
* getMonotonicUs is still NULL during early startup (core module
* registration runs dictAdd before monotonicInit). */
const int time_it = dictRehashStepTiming && (getMonotonicUs != NULL);
monotime t;
if (time_it) elapsedStart(&t);
/* rehashing not in progress if rehashidx == -1 */
if ((long)visitedIdx >= d->rehashidx && d->ht_table[0][visitedIdx]) {
/* If we have a valid hash entry at `idx` in ht0, we perform
@ -1715,6 +1745,7 @@ static void _dictRehashStepIfNeeded(dict *d, uint64_t visitedIdx) {
* on the rehashidx (not CPU cache friendly). */
dictRehash(d,1);
}
if (time_it) dictRehashStepElapsedUs += elapsedUs(t);
}
/* Our hash table capability is a power of two */

View file

@ -292,6 +292,8 @@ void dictEmpty(dict *d, void(callback)(dict*));
void dictSetResizeEnabled(dictResizeEnable enable);
int dictRehash(dict *d, int n);
int dictRehashMicroseconds(dict *d, uint64_t us);
extern uint64_t dictRehashStepElapsedUs;
extern int dictRehashStepTiming;
void dictSetHashFunctionSeed(uint8_t *seed);
unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, void *privdata);
unsigned long dictScanDefrag(dict *d, unsigned long v, dictScanFunction *fn, dictDefragFunctions *defragfns, void *privdata);

View file

@ -3983,6 +3983,17 @@ void call(client *c, int flags) {
/* Pass current server.ustime to avoid ustime() call if monotonic clock is used
* and time will be updated before command execution based on monotonic clock. */
const long long call_timer = use_hw_clock ? server.ustime : ustime();
/* Arm dict-rehash attribution at the outermost call() only. The
* accumulator starts at zero so that only rehash work performed during
* this top-level command's proc is attributed to it, and the timing
* flag is set so that the dict-side path starts collecting samples
* while this command runs. When the feature is disabled
* (latency_monitor_threshold == 0), the flag stays 0 and the entire
* timing path is a single predictable branch. */
if (server.execution_nesting == 0) {
dictRehashStepElapsedUs = 0;
dictRehashStepTiming = (server.latency_monitor_threshold != 0);
}
enterExecutionUnit(1, call_timer);
/* setting the CLIENT_EXECUTING_COMMAND flag so we will avoid
@ -4039,10 +4050,20 @@ void call(client *c, int flags) {
char *latency_event = (real_cmd->flags & CMD_FAST) ?
"fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
if (server.execution_nesting == 0)
if (server.execution_nesting == 0) {
durationAddSample(EL_DURATION_TYPE_CMD, duration);
latencyAddSampleIfNeeded("dict-rehash-during-command",
(long long)(dictRehashStepElapsedUs/1000));
}
}
/* Disarm the dict-rehash timing path unconditionally at outermost call()
* exit, so cron work between commands does not pay the timing cost. This
* must run regardless of update_command_stats (e.g. during AOF loading)
* so the flag cannot stay armed across paths that skip the stats block. */
if (server.execution_nesting == 0)
dictRehashStepTiming = 0;
/* Log the command into the Slow log if needed.
* If the client is blocked we will handle slowlog when it is unblocked. */
if (update_command_stats && !(c->flags & CLIENT_BLOCKED))