src/server.c C 8,294 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 8,294.
1/*2 * Copyright (c) 2009-Present, Redis Ltd.3 * All rights reserved.4 *5 * Copyright (c) 2024-present, Valkey contributors.6 * All rights reserved.7 *8 * Licensed under your choice of (a) the Redis Source Available License 2.09 * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the10 * GNU Affero General Public License v3 (AGPLv3).11 *12 * Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.13 */1415#include "server.h"16#include "monotonic.h"17#include "cluster.h"18#include "cluster_slot_stats.h"19#include "slowlog.h"20#include "bio.h"21#include "latency.h"22#include "atomicvar.h"23#include "mt19937-64.h"24#include "functions.h"25#include "hdr_histogram.h"26#include "syscheck.h"27#include "threads_mngr.h"28#include "fmtargs.h"29#include "mstr.h"30#include "ebuckets.h"31#include "cluster_asm.h"32#include "fwtree.h"33#include "estore.h"34#include "chk.h"35#include "fast_float_strtod.h"3637#include <time.h>38#include <signal.h>39#include <sys/wait.h>40#include <errno.h>41#include <ctype.h>42#include <stdarg.h>43#include <arpa/inet.h>44#include <sys/stat.h>45#include <fcntl.h>46#include <sys/file.h>47#include <sys/time.h>48#include <sys/resource.h>49#include <sys/uio.h>50#include <sys/un.h>51#include <limits.h>52#include <float.h>53#include <math.h>54#include <sys/utsname.h>55#include <locale.h>56#include <sys/socket.h>5758#ifdef __linux__59#include <sys/mman.h>60#endif6162#if defined(HAVE_SYSCTL_KIPC_SOMAXCONN) || defined(HAVE_SYSCTL_KERN_SOMAXCONN)63#include <sys/sysctl.h>64#endif6566#ifdef __GNUC__67#define GNUC_VERSION_STR STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) "." STRINGIFY(__GNUC_PATCHLEVEL__)68#else69#define GNUC_VERSION_STR "0.0.0"70#endif7172/* Our shared "common" objects */7374struct sharedObjectsStruct shared;7576/* Global vars that are actually used as constants. The following double77 * values are used for double on-disk serialization, and are initialized78 * at runtime to avoid strange compiler optimizations. */7980double R_Zero, R_PosInf, R_NegInf, R_Nan;8182/*================================= Globals ================================= */8384/* Global vars */85struct redisServer server; /* Server global state */8687/* Snapshot of server.stat_total_client_process_input_buff_events used in88 * beforeSleep() to detect event loop cycles where client input buffers89 * were processed. */90long long stat_prev_total_client_process_input_buff_events = 0;9192/*============================ Internal prototypes ========================== */9394static inline int isShutdownInitiated(void);95static inline int isCommandReusable(struct redisCommand *cmd, robj *commandArg);96int isReadyToShutdown(void);97int finishShutdown(void);98const char *replstateToString(int replstate);99100/*============================ Utility functions ============================ */101102/* Check if a given command can be reused without performing a lookup.103 * A command is reusable if:104 * - It is not NULL.105 * - It does not have subcommands (subcommands_dict == NULL).106 *   This preserves simplicity on the check and accounts for the majority of the use cases.107 * - Its full name matches the provided command argument. */108static inline int isCommandReusable(struct redisCommand *cmd, robj *commandArg) {109    return cmd != NULL &&110           cmd->subcommands_dict == NULL &&111           strcasecmp(cmd->fullname, commandArg->ptr) == 0;112}113114/* This macro tells if we are in the context of loading an AOF. */115#define isAOFLoadingContext() \116    ((server.current_client && server.current_client->id == CLIENT_ID_AOF) ? 1 : 0)117118/* We use a private localtime implementation which is fork-safe. The logging119 * function of Redis may be called from other threads. */120void nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst);121122static inline int shouldShutdownAsap(void) {123    int shutdown_asap;124    atomicGet(server.shutdown_asap, shutdown_asap);125    return shutdown_asap;126}127128/* Low level logging. To use only for very big messages, otherwise129 * serverLog() is to prefer. */130void serverLogRaw(int level, const char *msg) {131    const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };132    const char *c = ".-*#";133    FILE *fp;134    char buf[64];135    int rawmode = (level & LL_RAW);136    int log_to_stdout = server.logfile[0] == '\0';137138    level &= 0xff; /* clear flags */139    if (level < server.verbosity) return;140141    fp = log_to_stdout ? stdout : fopen(server.logfile,"a");142    if (!fp) return;143144    if (rawmode) {145        fprintf(fp,"%s",msg);146    } else {147        int off;148        struct timeval tv;149        int role_char;150        int daylight_active = 0;151        pid_t pid = getpid();152153        gettimeofday(&tv,NULL);154        struct tm tm;155        atomicGet(server.daylight_active, daylight_active);156        nolocks_localtime(&tm,tv.tv_sec,server.timezone,daylight_active);157        off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm);158        snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);159        if (server.sentinel_mode) {160            role_char = 'X'; /* Sentinel. */161        } else if (pid != server.pid) {162            role_char = 'C'; /* RDB / AOF writing child. */163        } else {164            role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */165        }166        fprintf(fp,"%d:%c %s %c %s\n",167            (int)getpid(),role_char, buf,c[level],msg);168    }169    fflush(fp);170171    if (!log_to_stdout) fclose(fp);172    if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);173}174175/* Like serverLogRaw() but with printf-alike support. This is the function that176 * is used across the code. The raw version is only used in order to dump177 * the INFO output on crash. */178void _serverLog(int level, const char *fmt, ...) {179    va_list ap;180    char msg[LOG_MAX_LEN];181182    va_start(ap, fmt);183    vsnprintf(msg, sizeof(msg), fmt, ap);184    va_end(ap);185186    serverLogRaw(level,msg);187}188189/* Low level logging from signal handler. Should be used with pre-formatted strings. 190   See serverLogFromHandler. */191void serverLogRawFromHandler(int level, const char *msg) {192    int fd;193    int log_to_stdout = server.logfile[0] == '\0';194    char buf[64];195196    if ((level&0xff) < server.verbosity || (log_to_stdout && server.daemonize))197        return;198    fd = log_to_stdout ? STDOUT_FILENO :199                         open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);200    if (fd == -1) return;201    if (level & LL_RAW) {202        if (write(fd,msg,strlen(msg)) == -1) goto err;203    }204    else {205        ll2string(buf,sizeof(buf),getpid());206        if (write(fd,buf,strlen(buf)) == -1) goto err;207        if (write(fd,":signal-handler (",17) == -1) goto err;208        ll2string(buf,sizeof(buf),time(NULL));209        if (write(fd,buf,strlen(buf)) == -1) goto err;210        if (write(fd,") ",2) == -1) goto err;211        if (write(fd,msg,strlen(msg)) == -1) goto err;212        if (write(fd,"\n",1) == -1) goto err;213    }214err:215    if (!log_to_stdout) close(fd);216}217218/* An async-signal-safe version of serverLog. if LL_RAW is not included in level flags,219 * The message format is: <pid>:signal-handler (<time>) <msg> \n220 * with LL_RAW flag only the msg is printed (with no new line at the end)221 *222 * We actually use this only for signals that are not fatal from the point223 * of view of Redis. Signals that are going to kill the server anyway and224 * where we need printf-alike features are served by serverLog(). */225void serverLogFromHandler(int level, const char *fmt, ...) {226    va_list ap;227    char msg[LOG_MAX_LEN];228229    va_start(ap, fmt);230    vsnprintf_async_signal_safe(msg, sizeof(msg), fmt, ap);231    va_end(ap);232233    serverLogRawFromHandler(level, msg);234}235236/* Return the UNIX time in microseconds */237long long ustime(void) {238    struct timeval tv;239    long long ust;240241    gettimeofday(&tv, NULL);242    ust = ((long long)tv.tv_sec)*1000000;243    ust += tv.tv_usec;244    return ust;245}246247/* Return the UNIX time in milliseconds */248mstime_t mstime(void) {249    return ustime()/1000;250}251252/* Return the command time snapshot in milliseconds.253 * The time the command started is the logical time it runs,254 * and all the time readings during the execution time should255 * reflect the same time.256 * More details can be found in the comments below. */257mstime_t commandTimeSnapshot(void) {258    /* When we are in the middle of a command execution, we want to use a259     * reference time that does not change: in that case we just use the260     * cached time, that we update before each call in the call() function.261     * This way we avoid that commands such as RPOPLPUSH or similar, that262     * may re-open the same key multiple times, can invalidate an already263     * open object in a next call, if the next call will see the key expired,264     * while the first did not.265     * This is specifically important in the context of scripts, where we266     * pretend that time freezes. This way a key can expire only the first time267     * it is accessed and not in the middle of the script execution, making268     * propagation to slaves / AOF consistent. See issue #1525 for more info.269     * Note that we cannot use the cached server.mstime because it can change270     * in processEventsWhileBlocked etc. */271    return server.cmd_time_snapshot;272}273274/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of275 * exit(), because the latter may interact with the same file objects used by276 * the parent process. However if we are testing the coverage normal exit() is277 * used in order to obtain the right coverage information. 278 * There is a caveat for when we exit due to a signal.279 * In this case we want the function to be async signal safe, so we can't use exit()280 */281void exitFromChild(int retcode, int from_signal) {282#ifdef COVERAGE_TEST283    if (!from_signal) {284        exit(retcode);285    } else {286        _exit(retcode);287    }288#else289    UNUSED(from_signal);290    _exit(retcode);291#endif292}293294/*====================== Hash table type implementation  ==================== */295296/* This is a hash table type that uses the SDS dynamic strings library as297 * keys and redis objects as values (objects can hold SDS strings,298 * lists, sets). */299300void dictVanillaFree(dict *d, void *val)301{302    UNUSED(d);303    zfree(val);304}305306void dictListDestructor(dict *d, void *val)307{308    UNUSED(d);309    listRelease((list*)val);310}311312void dictDictDestructor(dict *d, void *val)313{314    UNUSED(d);315    dictRelease((dict*)val);316}317318size_t dictSdsKeyLen(dict *d, const void *key) {319    UNUSED(d);320    return sdslen((sds)key);321}322323static const void *kvGetKey(const void *kv) {324    sds sdsKey = kvobjGetKey((kvobj *) kv);325    return sdsKey;326}327328int dictSdsCompareKV(dictCmpCache *cache, const void *sdsKey1, const void *sdsKey2)329{330    /* is first cmp call of a new lookup */331    if (cache->useCache == 0) {332        cache->useCache = 1;333        cache->data[0].sz = sdslen((sds) sdsKey1);334    }335336    size_t l1 = cache->data[0].sz;337    size_t l2 = sdslen((sds)sdsKey2);338    if (l1 != l2) return 0;339    return memcmp(sdsKey1, sdsKey2, l1) == 0;340}341342static void dictDestructorKV(dict *d, void *key) {343    kvobj *kv = (kvobj *)key;344    if (kv == NULL) return;345    if (server.memory_tracking_enabled) {346        kvstore *kvs = d->type->userdata;347        kvstoreMetadata *kvstoreMeta = kvstoreGetMetadata(kvs);348        kvstoreDictMetadata *meta = (kvstoreDictMetadata *)dictMetadata(d);349        size_t alloc_size = kvobjAllocSize(kv);350        debugServerAssert(alloc_size <= meta->alloc_size);351        meta->alloc_size -= alloc_size;352        /* kvstoreMeta may be NULL when freeing kvstore created with kvstoreBaseType353         * (e.g. in lazy free context). */354        if (kvstoreMeta && kv->type < OBJ_TYPE_BASIC_MAX) {355            /* we don't call kvsUpdateHistogram() because it contains debugServerAssert356             * that may fail in bg thread as kvstore might not being fully initialized */357            int old_bin = (alloc_size == 0) ? 0 : log2ceil(alloc_size) + 1;358            debugServerAssert(old_bin < MAX_KEYSIZES_BINS);359            kvstoreMeta->allocsizes_hist[kv->type][old_bin]--;360        }361    }362    decrRefCount(kv);363}364365int dictSdsKeyCompare(dictCmpCache *cache, const void *key1,366        const void *key2)367{368    int l1,l2;369    UNUSED(cache);370371    l1 = sdslen((sds)key1);372    l2 = sdslen((sds)key2);373    if (l1 != l2) return 0;374    return memcmp(key1, key2, l1) == 0;375}376377/* A case insensitive version used for the command lookup table and other378 * places where case insensitive non binary-safe comparison is needed. */379int dictSdsKeyCaseCompare(dictCmpCache *cache, const void *key1,380        const void *key2)381{382    UNUSED(cache);383    return strcasecmp(key1, key2) == 0;384}385386void dictObjectDestructor(dict *d, void *val)387{388    UNUSED(d);389    if (val == NULL) return; /* Lazy freeing will set value to NULL. */390    decrRefCount(val);391}392393void dictSdsDestructor(dict *d, void *val)394{395    UNUSED(d);396    sdsfree(val);397}398399void setSdsDestructor(dict *d, void *val) {400    *htGetMetadataSize(d) -= sdsAllocSize(val);401    sdsfree(val);402}403404size_t setDictMetadataBytes(dict *d) {405    UNUSED(d);406    return sizeof(size_t);407}408409void *dictSdsDup(dict *d, const void *key) {410    UNUSED(d);411    return sdsdup((const sds) key);412}413414int dictObjKeyCompare(dictCmpCache *cache, const void *key1,415        const void *key2)416{417    const robj *o1 = key1, *o2 = key2;418    return dictSdsKeyCompare(cache, o1->ptr,o2->ptr);419}420421uint64_t dictObjHash(const void *key) {422    const robj *o = key;423    return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));424}425426uint64_t dictPtrHash(const void *key) {427    return dictGenHashFunction((unsigned char*)&key,sizeof(key));428}429430uint64_t dictSdsHash(const void *key) {431    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));432}433434uint64_t dictSdsCaseHash(const void *key) {435    return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key));436}437438/* Dict hash function for null terminated string */439uint64_t dictCStrHash(const void *key) {440    return dictGenHashFunction((unsigned char*)key, strlen((char*)key));441}442443/* Dict hash function for null terminated string */444uint64_t dictCStrCaseHash(const void *key) {445    return dictGenCaseHashFunction((unsigned char*)key, strlen((char*)key));446}447448/* Dict hash function for client */449uint64_t dictClientHash(const void *key) {450    return ((client *)key)->id;451}452453/* Dict compare function for client */454int dictClientKeyCompare(dictCmpCache *cache, const void *key1, const void *key2) {455    UNUSED(cache);456    return ((client *)key1)->id == ((client *)key2)->id;457}458459/* Dict compare function for null terminated string */460int dictCStrKeyCompare(dictCmpCache *cache, const void *key1, const void *key2) {461    int l1,l2;462    UNUSED(cache);463464    l1 = strlen((char*)key1);465    l2 = strlen((char*)key2);466    if (l1 != l2) return 0;467    return memcmp(key1, key2, l1) == 0;468}469470/* Dict case insensitive compare function for null terminated string */471int dictCStrKeyCaseCompare(dictCmpCache *cache, const void *key1, const void *key2) {472    UNUSED(cache);473    return strcasecmp(key1, key2) == 0;474}475476int dictEncObjKeyCompare(dictCmpCache *cache, const void *key1, const void *key2)477{478    robj *o1 = (robj*) key1, *o2 = (robj*) key2;479    int cmp;480481    if (o1->encoding == OBJ_ENCODING_INT &&482        o2->encoding == OBJ_ENCODING_INT)483            return o1->ptr == o2->ptr;484485    /* Due to OBJ_STATIC_REFCOUNT, we avoid calling getDecodedObject() without486     * good reasons, because it would incrRefCount() the object, which487     * is invalid. So we check to make sure dictFind() works with static488     * objects as well. */489    if (o1->refcount != OBJ_STATIC_REFCOUNT) o1 = getDecodedObject(o1);490    if (o2->refcount != OBJ_STATIC_REFCOUNT) o2 = getDecodedObject(o2);491    cmp = dictSdsKeyCompare(cache,o1->ptr,o2->ptr);492    if (o1->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o1);493    if (o2->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o2);494    return cmp;495}496497uint64_t dictEncObjHash(const void *key) {498    robj *o = (robj*) key;499500    if (sdsEncodedObject(o)) {501        return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));502    } else if (o->encoding == OBJ_ENCODING_INT) {503        char buf[32];504        int len;505506        len = ll2string(buf,32,(long)o->ptr);507        return dictGenHashFunction((unsigned char*)buf, len);508    } else {509        serverPanic("Unknown string encoding");510    }511}512513static size_t kvstoreMetadataBytes(kvstore *kvs) {514    UNUSED(kvs);515    return sizeof(kvstoreMetadata);516}517518static size_t kvstoreDictMetaBytes(dict *d) {519    UNUSED(d);520    return sizeof(kvstoreDictMetadata);521}522523static int kvstoreCanFreeDict(kvstore *kvs, int didx) {524    kvstoreDictMetadata *meta = kvstoreGetDictMeta(kvs, didx, 0);525    debugServerAssert(meta->alloc_size == 0);526    /* Free if not in cluster */527    if (!server.cluster_enabled) return 1;528529    /* Don't free if we have stats for this slot and the relevant tracking is enabled. */530    int has_cpu_stats = (server.cluster_slot_stats_enabled & CLUSTER_SLOT_STATS_CPU) && meta->cpu_usec;531    int has_net_stats = (server.cluster_slot_stats_enabled & CLUSTER_SLOT_STATS_NET) &&532                        (meta->network_bytes_in || meta->network_bytes_out);533    if ((has_cpu_stats || has_net_stats) && clusterIsMySlot(didx)) {534        return 0;535    }536537    /* Otherwise, we can free */538    return 1;539}540541static void kvstoreOnEmpty(kvstore *kvs) {542    kvstoreMetadata *meta = kvstoreGetMetadata(kvs);543    memset(&meta->keysizes_hist, 0, sizeof(meta->keysizes_hist));544    memset(&meta->allocsizes_hist, 0, sizeof(meta->allocsizes_hist));545}546547static void kvstoreOnDictEmpty(kvstore *kvs, int didx) {548    kvstoreDictMetadata *meta = kvstoreGetDictMeta(kvs, didx, 0);549    UNUSED(meta);550#ifdef DEBUG_ASSERTIONS551    dictEmpty(kvstoreGetDict(kvs, didx), NULL);552#endif553    debugServerAssert(meta->alloc_size == 0);554}555556/* Return 1 if currently we allow dict to expand. Dict may allocate huge557 * memory to contain hash buckets when dict expands, that may lead redis558 * rejects user's requests or evicts some keys, we can stop dict to expand559 * provisionally if used memory will be over maxmemory after dict expands,560 * but to guarantee the performance of redis, we still allow dict to expand561 * if dict load factor exceeds HASHTABLE_MAX_LOAD_FACTOR. */562int dictResizeAllowed(size_t moreMem, double usedRatio) {563    /* for debug purposes: dict is not allowed to be resized. */564    if (!server.dict_resizing) return 0;565566    if (usedRatio <= HASHTABLE_MAX_LOAD_FACTOR) {567        return !overMaxmemoryAfterAlloc(moreMem);568    } else {569        return 1;570    }571}572573/* dbDictType prefetch callbacks.574 * The main keyspace stores a kvobj as the entry's "stored key" (no_value=1).575 * The state machine in memory_prefetch.c calls these hooks to:576 *  - Bring the kvobj head into L1 before keyCompare runs (only useful when577 *    the entry holds an out-of-line pointer; embedded kvobjs are already578 *    in cache from the entry prefetch).579 *  - Bring kv->ptr into L1 for RAW strings, since addReplyBulk reads it580 *    immediately after the lookup. */581static void *dbDictPrefetchEntryKey(const dictEntry *de) {582    return dictEntryIsKey(de) ? NULL : dictGetKey(de);583}584585static void *dbDictPrefetchEntryValue(const dictEntry *de) {586    kvobj *kv = dictGetKey(de);587    return (kv->type == OBJ_STRING && kv->encoding == OBJ_ENCODING_RAW) ? kv->ptr : NULL;588}589590/* Generic hash table type where keys are Redis Objects, Values591 * dummy pointers. */592dictType objectKeyPointerValueDictType = {593    dictEncObjHash,            /* hash function */594    NULL,                      /* key dup */595    NULL,                      /* val dup */596    dictEncObjKeyCompare,      /* key compare */597    dictObjectDestructor,      /* key destructor */598    NULL,                      /* val destructor */599    NULL                       /* allow to expand */600};601602/* Dict type with robj pointer keys and no values. */603dictType objectKeyNoValueDictType = {604    dictEncObjHash,            /* hash function */605    NULL,                      /* key dup */606    NULL,                      /* val dup */607    dictEncObjKeyCompare,      /* key compare */608    dictObjectDestructor,      /* key destructor */609    NULL,                      /* val destructor */610    NULL,                      /* allow to expand */611    .no_value = 1,             /* no values in this dict */612    .keys_are_odd = 0,         /* robj pointers are not odd */613};614615/* Like objectKeyPointerValueDictType(), but values can be destroyed, if616 * not NULL, calling zfree(). */617dictType objectKeyHeapPointerValueDictType = {618    dictEncObjHash,            /* hash function */619    NULL,                      /* key dup */620    NULL,                      /* val dup */621    dictEncObjKeyCompare,      /* key compare */622    dictObjectDestructor,      /* key destructor */623    dictVanillaFree,           /* val destructor */624    NULL                       /* allow to expand */625};626627/* Set dictionary type. Keys are SDS strings, values are not used. */628dictType setDictType = {629    dictSdsHash,               /* hash function */630    NULL,                      /* key dup */631    NULL,                      /* val dup */632    dictSdsKeyCompare,         /* key compare */633    setSdsDestructor,          /* key destructor */634    NULL,                      /* val destructor */635    NULL,                      /* allow to expand */636    .no_value = 1,             /* no values in this dict */637    .keys_are_odd = 1,         /* an SDS string is always an odd pointer */638    .dictMetadataBytes = setDictMetadataBytes,639};640641/* Db->dict, keys are of type kvobj, unification of key and value */642dictType dbDictType = {643    dictSdsHash,            /* hash function */644    NULL,                   /* key dup */645    NULL,                   /* val dup */646    dictSdsCompareKV,       /* lookup key compare */647    dictDestructorKV,       /* key destructor */648    NULL,                   /* val destructor */649    dictResizeAllowed,      /* allow to resize */650    .no_value = 1,          /* keys and values are unified (kvobj) */651    .keys_are_odd = 0,      /* simple kvobj (robj) struct */652    .keyFromStoredKey = kvGetKey,    /* get key from stored-key */653    .prefetchEntryKey = dbDictPrefetchEntryKey,654    .prefetchEntryValue = dbDictPrefetchEntryValue,655};656657/* Db->expires */658dictType dbExpiresDictType = {659    dictSdsHash,                /* hash function */660    NULL,                       /* key dup */661    NULL,                       /* val dup */662    dictSdsCompareKV,           /* key compare */663    NULL,                       /* key destructor */664    NULL,                       /* val destructor */665    dictResizeAllowed,          /* allow to resize */666    .no_value = 1,              /* keys and values are unified (kvobj) */667    .keys_are_odd = 0,          /* simple kvobj (robj) struct */668    .keyFromStoredKey = kvGetKey,   /* get key from stored-key */669};670671/* Command table. sds string -> command struct pointer. */672dictType commandTableDictType = {673    dictSdsCaseHash,            /* hash function */674    NULL,                       /* key dup */675    NULL,                       /* val dup */676    dictSdsKeyCaseCompare,      /* key compare */677    dictSdsDestructor,          /* key destructor */678    NULL,                       /* val destructor */679    NULL,                       /* allow to expand */680    .force_full_rehash = 1,     /* force full rehashing */681};682683/* Hash type hash table (note that small hashes are represented with listpacks) */684dictType hashDictType = {685    dictSdsHash,                /* hash function */686    NULL,                       /* key dup */687    NULL,                       /* val dup */688    dictSdsKeyCompare,          /* key compare */689    dictSdsDestructor,          /* key destructor */690    dictSdsDestructor,          /* val destructor */691    NULL,                       /* allow to expand */692};693694/* Dict type without destructor */695dictType sdsReplyDictType = {696    dictSdsHash,                /* hash function */697    NULL,                       /* key dup */698    NULL,                       /* val dup */699    dictSdsKeyCompare,          /* key compare */700    NULL,                       /* key destructor */701    NULL,                       /* val destructor */702    NULL                        /* allow to expand */703};704705/* Keylist hash table type has unencoded redis objects as keys and706 * lists as values. It's used for blocking operations (BLPOP) and to707 * map swapped keys to a list of clients waiting for this keys to be loaded. */708dictType keylistDictType = {709    dictObjHash,                /* hash function */710    NULL,                       /* key dup */711    NULL,                       /* val dup */712    dictObjKeyCompare,          /* key compare */713    dictObjectDestructor,       /* key destructor */714    dictListDestructor,         /* val destructor */715    NULL                        /* allow to expand */716};717718/* KeyDict hash table type has unencoded redis objects as keys and719 * dicts as values. It's used for PUBSUB command to track clients subscribing the channels. */720dictType objToDictDictType = {721    dictObjHash,                /* hash function */722    NULL,                       /* key dup */723    NULL,                       /* val dup */724    dictObjKeyCompare,          /* key compare */725    dictObjectDestructor,       /* key destructor */726    dictDictDestructor,         /* val destructor */727    NULL                        /* allow to expand */728};729730/* Modules system dictionary type. Keys are module name,731 * values are pointer to RedisModule struct. */732dictType modulesDictType = {733    dictSdsCaseHash,            /* hash function */734    NULL,                       /* key dup */735    NULL,                       /* val dup */736    dictSdsKeyCaseCompare,      /* key compare */737    dictSdsDestructor,          /* key destructor */738    NULL,                       /* val destructor */739    NULL                        /* allow to expand */740};741742/* Migrate cache dict type. */743dictType migrateCacheDictType = {744    dictSdsHash,                /* hash function */745    NULL,                       /* key dup */746    NULL,                       /* val dup */747    dictSdsKeyCompare,          /* key compare */748    dictSdsDestructor,          /* key destructor */749    NULL,                       /* val destructor */750    NULL                        /* allow to expand */751};752753/* Dict for for case-insensitive search using null terminated C strings.754 * The keys stored in dict are sds though. */755dictType stringSetDictType = {756    dictCStrCaseHash,           /* hash function */757    NULL,                       /* key dup */758    NULL,                       /* val dup */759    dictCStrKeyCaseCompare,     /* key compare */760    dictSdsDestructor,          /* key destructor */761    NULL,                       /* val destructor */762    NULL                        /* allow to expand */763};764765/* Dict for for case-insensitive search using null terminated C strings.766 * The key and value do not have a destructor. */767dictType externalStringType = {768    dictCStrCaseHash,           /* hash function */769    NULL,                       /* key dup */770    NULL,                       /* val dup */771    dictCStrKeyCaseCompare,     /* key compare */772    NULL,                       /* key destructor */773    NULL,                       /* val destructor */774    NULL                        /* allow to expand */775};776777/* Dict for case-insensitive search using sds objects with a zmalloc778 * allocated object as the value. */779dictType sdsHashDictType = {780    dictSdsCaseHash,            /* hash function */781    NULL,                       /* key dup */782    NULL,                       /* val dup */783    dictSdsKeyCaseCompare,      /* key compare */784    dictSdsDestructor,          /* key destructor */785    dictVanillaFree,            /* val destructor */786    NULL                        /* allow to expand */787};788789/* Client Set dictionary type. Keys are client, values are not used. */790dictType clientDictType = {791    dictClientHash,             /* hash function */792    NULL,                       /* key dup */793    NULL,                       /* val dup */794    dictClientKeyCompare,       /* key compare */795    .no_value = 1,              /* no values in this dict */796    .keys_are_odd = 0           /* a client pointer is not an odd pointer */            797};798799kvstoreType kvstoreBaseType = {800    NULL, /* kvstore metadata size */801    NULL, /* dict metadata size */802    NULL, /* can free dict */803    NULL, /* on kvstore empty */804    NULL, /* on dict empty */805};806807kvstoreType kvstoreExType = {808    kvstoreMetadataBytes, /* kvstore metadata size */809    kvstoreDictMetaBytes, /* dict metadata size */810    kvstoreCanFreeDict,   /* can free dict */811    kvstoreOnEmpty,       /* on kvstore empty */812    kvstoreOnDictEmpty,   /* on dict empty */813};814815/* This function is called once a background process of some kind terminates,816 * as we want to avoid resizing the hash tables when there is a child in order817 * to play well with copy-on-write (otherwise when a resize happens lots of818 * memory pages are copied). The goal of this function is to update the ability819 * for dict.c to resize or rehash the tables accordingly to the fact we have an820 * active fork child running. */821void updateDictResizePolicy(void) {822    if (server.in_fork_child != CHILD_TYPE_NONE)823        dictSetResizeEnabled(DICT_RESIZE_FORBID);824    else if (hasActiveChildProcess())825        dictSetResizeEnabled(DICT_RESIZE_AVOID);826    else827        dictSetResizeEnabled(DICT_RESIZE_ENABLE);828}829830const char *strChildType(int type) {831    switch(type) {832        case CHILD_TYPE_RDB: return "RDB";833        case CHILD_TYPE_AOF: return "AOF";834        case CHILD_TYPE_LDB: return "LDB";835        case CHILD_TYPE_MODULE: return "MODULE";836        default: return "Unknown";837    }838}839840/* Return true if there are active children processes doing RDB saving,841 * AOF rewriting, or some side process spawned by a loaded module. */842int hasActiveChildProcess(void) {843    return server.child_pid != -1;844}845846void resetChildState(void) {847    server.child_type = CHILD_TYPE_NONE;848    server.child_pid = -1;849    server.stat_current_cow_peak = 0;850    server.stat_current_cow_bytes = 0;851    server.stat_current_cow_updated = 0;852    server.stat_current_save_keys_processed = 0;853    server.stat_module_progress = 0;854    server.stat_current_save_keys_total = 0;855    updateDictResizePolicy();856    closeChildInfoPipe();857    moduleFireServerEvent(REDISMODULE_EVENT_FORK_CHILD,858                          REDISMODULE_SUBEVENT_FORK_CHILD_DIED,859                          NULL);860}861862/* Return if child type is mutually exclusive with other fork children */863int isMutuallyExclusiveChildType(int type) {864    return type == CHILD_TYPE_RDB || type == CHILD_TYPE_AOF || type == CHILD_TYPE_MODULE;865}866867/* Returns true when we're inside a long command that yielded to the event loop. */868int isInsideYieldingLongCommand(void) {869    return scriptIsTimedout() || server.busy_module_yield_flags;870}871872/* Return true if this instance has persistence completely turned off:873 * both RDB and AOF are disabled. */874int allPersistenceDisabled(void) {875    return server.saveparamslen == 0 && server.aof_state == AOF_OFF;876}877878/* ======================= Cron: called every 100 ms ======================== */879880/* Add a sample to the instantaneous metric. This function computes the quotient881 * of the increment of value and base, which is useful to record operation count882 * per second, or the average time consumption of an operation.883 *884 * current_value - The dividend885 * current_base - The divisor886 * */887void trackInstantaneousMetric(int metric, long long current_value, long long current_base, long long factor) {888    if (server.inst_metric[metric].last_sample_base > 0) {889        long long base = current_base - server.inst_metric[metric].last_sample_base;890        long long value = current_value - server.inst_metric[metric].last_sample_value;891        long long avg = base > 0 ? (value * factor / base) : 0;892        server.inst_metric[metric].samples[server.inst_metric[metric].idx] = avg;893        server.inst_metric[metric].idx++;894        server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;895    }896    server.inst_metric[metric].last_sample_base = current_base;897    server.inst_metric[metric].last_sample_value = current_value;898}899900/* Return the mean of all the samples. */901long long getInstantaneousMetric(int metric) {902    int j;903    long long sum = 0;904905    for (j = 0; j < STATS_METRIC_SAMPLES; j++)906        sum += server.inst_metric[metric].samples[j];907    return sum / STATS_METRIC_SAMPLES;908}909910/* The client query buffer is an sds.c string that can end with a lot of911 * free space not used, this function reclaims space if needed.912 *913 * The function always returns 0 as it never terminates the client. */914int clientsCronResizeQueryBuffer(client *c) {915    /* If the client query buffer is NULL, it is using the reusable query buffer and there is nothing to do. */916    if (c->querybuf == NULL) return 0;917    size_t querybuf_size = sdsalloc(c->querybuf);918    time_t idletime = server.unixtime - c->lastinteraction;919920    /* Only resize the query buffer if the buffer is actually wasting at least a921     * few kbytes */922    if (sdsavail(c->querybuf) > 1024*4) {923        /* There are two conditions to resize the query buffer: */924        if (idletime > 2) {925            /* 1) Query is idle for a long time. */926            size_t remaining = sdslen(c->querybuf) - c->qb_pos;927            if (!(c->flags & CLIENT_MASTER) && !remaining) {928                /* If the client is not a master and no data is pending,929                 * The client can safely use the reusable query buffer in the next read - free the client's querybuf. */930                sdsfree(c->querybuf);931                /* By setting the querybuf to NULL, the client will use the reusable query buffer in the next read.932                 * We don't move the client to the reusable query buffer immediately, because if we allocated a private933                 * query buffer for the client, it's likely that the client will use it again soon. */934                c->querybuf = NULL;935            } else {936                c->querybuf = sdsRemoveFreeSpace(c->querybuf, 1);937            }938        } else if (querybuf_size > PROTO_RESIZE_THRESHOLD && querybuf_size/2 > c->querybuf_peak) {939            /* 2) Query buffer is too big for latest peak and is larger than940             *    resize threshold. Trim excess space but only up to a limit,941             *    not below the recent peak and current c->querybuf (which will942             *    be soon get used). If we're in the middle of a bulk then make943             *    sure not to resize to less than the bulk length. */944            size_t resize = sdslen(c->querybuf);945            if (resize < c->querybuf_peak) resize = c->querybuf_peak;946            if (c->bulklen != -1 && resize < (size_t)c->bulklen + 2) resize = c->bulklen + 2;947            c->querybuf = sdsResize(c->querybuf, resize, 1);948        }949    }950951    /* Reset the peak again to capture the peak memory usage in the next952     * cycle. */953    c->querybuf_peak = c->querybuf ? sdslen(c->querybuf) : 0;954    /* We reset to either the current used, or currently processed bulk size,955     * which ever is bigger. */956    if (c->bulklen != -1 && (size_t)c->bulklen + 2 > c->querybuf_peak) c->querybuf_peak = c->bulklen + 2;957    return 0;958}959960/* The client output buffer can be adjusted to better fit the memory requirements.961 *962 * the logic is:963 * in case the last observed peak size of the buffer equals the buffer size - we double the size964 * in case the last observed peak size of the buffer is less than half the buffer size - we shrink by half.965 * The buffer peak will be reset back to the buffer position every server.reply_buffer_peak_reset_time milliseconds966 * The function always returns 0 as it never terminates the client. */967int clientsCronResizeOutputBuffer(client *c, mstime_t now_ms) {968969    size_t new_buffer_size = 0;970    char *oldbuf = NULL;971    const size_t buffer_target_shrink_size = c->buf_usable_size/2;972    const size_t buffer_target_expand_size = c->buf_usable_size*2;973974    /* in case the resizing is disabled return immediately */975    if(!server.reply_buffer_resizing_enabled)976        return 0;977978    /* Don't resize encoded buffers. When buf is encoded, we track the last979     * partially written payloadHeader pointer, so we can't980     * reallocate the buffer as it would invalidate this pointer. */981    if (c->buf_encoded) return 0;982983    if (buffer_target_shrink_size >= PROTO_REPLY_MIN_BYTES &&984        c->buf_peak < buffer_target_shrink_size )985    {986        new_buffer_size = max(PROTO_REPLY_MIN_BYTES,c->buf_peak+1);987        server.stat_reply_buffer_shrinks++;988    } else if (buffer_target_expand_size < PROTO_REPLY_CHUNK_BYTES*2 &&989        c->buf_peak == c->buf_usable_size)990    {991        new_buffer_size = min(PROTO_REPLY_CHUNK_BYTES,buffer_target_expand_size);992        server.stat_reply_buffer_expands++;993    }994995    serverAssertWithInfo(c, NULL, (!new_buffer_size) || (new_buffer_size >= (size_t)c->bufpos));996997    /* reset the peak value each server.reply_buffer_peak_reset_time seconds. in case the client will be idle998     * it will start to shrink.999     */1000    if (server.reply_buffer_peak_reset_time >=0 &&1001        now_ms - c->buf_peak_last_reset_time >= server.reply_buffer_peak_reset_time)1002    {1003        c->buf_peak = c->bufpos;1004        c->buf_peak_last_reset_time = now_ms;1005    }10061007    if (new_buffer_size) {1008        oldbuf = c->buf;1009        c->buf = zmalloc_usable(new_buffer_size, &c->buf_usable_size);1010        memcpy(c->buf,oldbuf,c->bufpos);1011        zfree(oldbuf);1012    }1013    return 0;1014}10151016/* This function is used in order to track clients using the biggest amount1017 * of memory in the latest few seconds. This way we can provide such information1018 * in the INFO output (clients section), without having to do an O(N) scan for1019 * all the clients.1020 *1021 * This is how it works. We have an array of CLIENTS_PEAK_MEM_USAGE_SLOTS slots1022 * where we track, for each, the biggest client output and input buffers we1023 * saw in that slot. Every slot corresponds to one of the latest seconds, since1024 * the array is indexed by doing UNIXTIME % CLIENTS_PEAK_MEM_USAGE_SLOTS.1025 *1026 * When we want to know what was recently the peak memory usage, we just scan1027 * such few slots searching for the maximum value. */1028#define CLIENTS_PEAK_MEM_USAGE_SLOTS 81029size_t ClientsPeakMemInput[CLIENTS_PEAK_MEM_USAGE_SLOTS] = {0};1030size_t ClientsPeakMemOutput[CLIENTS_PEAK_MEM_USAGE_SLOTS] = {0};1031int CurrentPeakMemUsageSlot = 0;10321033int clientsCronTrackExpansiveClients(client *c) {1034    size_t qb_size = c->querybuf ? sdsZmallocSize(c->querybuf) : 0;1035    size_t argv_size = c->argv ? zmalloc_size(c->argv) : 0;1036    size_t in_usage = qb_size + c->all_argv_len_sum + argv_size;1037    size_t out_usage = getClientOutputBufferMemoryUsage(c);10381039    /* Track the biggest values observed so far in this slot. */1040    if (in_usage > ClientsPeakMemInput[CurrentPeakMemUsageSlot])1041        ClientsPeakMemInput[CurrentPeakMemUsageSlot] = in_usage;1042    if (out_usage > ClientsPeakMemOutput[CurrentPeakMemUsageSlot])1043        ClientsPeakMemOutput[CurrentPeakMemUsageSlot] = out_usage;10441045    return 0; /* This function never terminates the client. */1046}10471048/* All normal clients are placed in one of the "mem usage buckets" according1049 * to how much memory they currently use. We use this function to find the1050 * appropriate bucket based on a given memory usage value. The algorithm simply1051 * does a log2(mem) to ge the bucket. This means, for examples, that if a1052 * client's memory usage doubles it's moved up to the next bucket, if it's1053 * halved we move it down a bucket.1054 * For more details see CLIENT_MEM_USAGE_BUCKETS documentation in server.h. */1055static inline clientMemUsageBucket *getMemUsageBucket(size_t mem) {1056    int size_in_bits = 8*(int)sizeof(mem);1057    int clz = mem > 0 ? __builtin_clzl(mem) : size_in_bits;1058    int bucket_idx = size_in_bits - clz;1059    if (bucket_idx > CLIENT_MEM_USAGE_BUCKET_MAX_LOG)1060        bucket_idx = CLIENT_MEM_USAGE_BUCKET_MAX_LOG;1061    else if (bucket_idx < CLIENT_MEM_USAGE_BUCKET_MIN_LOG)1062        bucket_idx = CLIENT_MEM_USAGE_BUCKET_MIN_LOG;1063    bucket_idx -= CLIENT_MEM_USAGE_BUCKET_MIN_LOG;1064    return &server.client_mem_usage_buckets[bucket_idx];1065}10661067/*1068 * This method updates the client memory usage and update the1069 * server stats for client type.1070 *1071 * This method is called from the clientsCron to have updated1072 * stats for non CLIENT_TYPE_NORMAL/PUBSUB clients to accurately1073 * provide information around clients memory usage.1074 *1075 * It is also used in updateClientMemUsageAndBucket to have latest1076 * client memory usage information to place it into appropriate client memory1077 * usage bucket.1078 */1079void updateClientMemoryUsage(client *c) {1080    serverAssert(c->conn);1081    size_t mem = getClientMemoryUsage(c);10821083    int type = getClientType(c);1084    /* Now that we have the memory used by the client, remove the old1085     * value from the old category, and add it back. */1086    server.stat_clients_type_memory[c->last_memory_type] -= c->last_memory_usage;1087    server.stat_clients_type_memory[type] += mem;1088    /* Remember what we added and where, to remove it next time. */1089    c->last_memory_type = type;1090    c->last_memory_usage = mem;1091}10921093int clientEvictionAllowed(client *c) {1094    if (server.maxmemory_clients == 0 || c->flags & CLIENT_NO_EVICT || !c->conn) {1095        return 0;1096    }1097    int type = getClientType(c);1098    return (type == CLIENT_TYPE_NORMAL || type == CLIENT_TYPE_PUBSUB);1099}110011011102/* This function is used to cleanup the client's previously tracked memory usage.1103 * This is called during incremental client memory usage tracking as well as1104 * used to reset when client to bucket allocation is not required when1105 * client eviction is disabled.  */1106void removeClientFromMemUsageBucket(client *c, int allow_eviction) {1107    if (c->mem_usage_bucket) {1108        c->mem_usage_bucket->mem_usage_sum -= c->last_memory_usage;1109        /* If this client can't be evicted then remove it from the mem usage1110         * buckets */1111        if (!allow_eviction) {1112            listDelNode(c->mem_usage_bucket->clients, c->mem_usage_bucket_node);1113            c->mem_usage_bucket = NULL;1114            c->mem_usage_bucket_node = NULL;1115        }1116    }1117}11181119/* This is called only if explicit clients when something changed their buffers,1120 * so we can track clients' memory and enforce clients' maxmemory in real time.1121 *1122 * This also adds the client to the correct memory usage bucket. Each bucket contains1123 * all clients with roughly the same amount of memory. This way we group1124 * together clients consuming about the same amount of memory and can quickly1125 * free them in case we reach maxmemory-clients (client eviction).1126 *1127 * Note: This function filters clients of type no-evict, master or replica regardless1128 * of whether the eviction is enabled or not, so the memory usage we get from these1129 * types of clients via the INFO command may be out of date.1130 *1131 * returns 1 if client eviction for this client is allowed, 0 otherwise.1132 */1133int updateClientMemUsageAndBucket(client *c) {1134    /* The unlikely case this function was called from a thread different1135     * than the main one is a module call from a spawned thread. This is safe1136     * since this call must have been made after calling1137     * RedisModule_ThreadSafeContextLock i.e the module is holding the GIL. In1138     * that special case we assert that at least the updated client's1139     * running_tid is the main thread. The true main thread is allowed to call1140     * this function on clients handled by IO-threads as it makes sure the1141     * IO-threads are paused, f.e see cleintsCron() and evictClients(). */1142    serverAssert((pthread_equal(pthread_self(), server.main_thread_id) ||1143                  c->running_tid == IOTHREAD_MAIN_THREAD_ID) && c->conn);1144    int allow_eviction = clientEvictionAllowed(c);1145    removeClientFromMemUsageBucket(c, allow_eviction);11461147    if (!allow_eviction) {1148        return 0;1149    }11501151    /* Include unshared reply bytes in the client's memory usage for eviction.1152     * Walking the reply buffer is costly, so skip the scan when its outcome1153     * cannot affect bucket placement: since 0 <= unshared <= shared, if both1154     * endpoints map to the same bucket the cached value is reused. */1155    if (c->reply_bytes_shared > 0) {1156        size_t lower_bound = getClientMemoryUsage(c) - c->reply_bytes_unshared;1157        size_t upper_bound = lower_bound + c->reply_bytes_shared;1158        if (getMemUsageBucket(lower_bound) != getMemUsageBucket(upper_bound))1159            updateClientUnsharedReplyBytes(c);1160    } else {1161        /* No shared bytes: clear any stale cached unshared. */1162        c->reply_bytes_unshared = 0;1163    }11641165    /* Update client memory usage. */1166    updateClientMemoryUsage(c);11671168    /* Update the client in the mem usage buckets */1169    clientMemUsageBucket *bucket = getMemUsageBucket(c->last_memory_usage);1170    bucket->mem_usage_sum += c->last_memory_usage;1171    if (bucket != c->mem_usage_bucket) {1172        if (c->mem_usage_bucket)1173            listDelNode(c->mem_usage_bucket->clients,1174                        c->mem_usage_bucket_node);1175        c->mem_usage_bucket = bucket;1176        listAddNodeTail(bucket->clients, c);1177        c->mem_usage_bucket_node = listLast(bucket->clients);1178    }1179    return 1;1180}11811182/* Return the max samples in the memory usage of clients tracked by1183 * the function clientsCronTrackExpansiveClients(). */1184void getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {1185    size_t i = 0, o = 0;1186    for (int j = 0; j < CLIENTS_PEAK_MEM_USAGE_SLOTS; j++) {1187        if (ClientsPeakMemInput[j] > i) i = ClientsPeakMemInput[j];1188        if (ClientsPeakMemOutput[j] > o) o = ClientsPeakMemOutput[j];1189    }1190    *in_usage = i;1191    *out_usage = o;1192}11931194/* Run cron tasks for a single client. Return 1 if the client should1195 * be terminated, 0 otherwise. */1196int clientsCronRunClient(client *c) {1197    mstime_t now = server.mstime;1198    /* The following functions do different service checks on the client.1199     * The protocol is that they return non-zero if the client was1200     * terminated. */1201    if (clientsCronHandleTimeout(c,now)) return 1;1202    if (clientsCronResizeQueryBuffer(c)) return 1;1203    if (clientsCronResizeOutputBuffer(c,now)) return 1;12041205    if (clientsCronTrackExpansiveClients(c)) return 1;12061207    /* Iterating all the clients in getMemoryOverheadData() is too slow and1208     * in turn would make the INFO command too slow. So we perform this1209     * computation incrementally and track the (not instantaneous but updated1210     * to the second) total memory used by clients using clientsCron() in1211     * a more incremental way (depending on server.hz).1212     * If client eviction is enabled, update the bucket as well. */1213    if (!updateClientMemUsageAndBucket(c))1214        updateClientMemoryUsage(c);12151216    if (closeClientOnOutputBufferLimitReached(c, 0)) return 1;1217    return 0;1218}12191220/* Periodic maintenance for the pending command pool.1221 * This function should be called from serverCron to manage pool size based on utilization patterns. */1222void pendingCommandPoolCron(void) {1223    /* Only shrink pool when IO threads are not active */1224    if (server.io_threads_active) return;12251226    /* Calculate utilization rate based on minimum pool size reached */1227    if (server.cmd_pool.capacity > PENDING_COMMAND_POOL_SIZE) {1228        /* If utilization is below threshold, shrink the pool */1229        double utilization_ratio = 1.0 - (double)server.cmd_pool.min_size / server.cmd_pool.capacity;1230        if (utilization_ratio < 0.5)1231            shrinkPendingCommandPool();1232    }12331234    /* Reset tracking for next interval */1235    server.cmd_pool.min_size = server.cmd_pool.size; /* Reset to current size */1236}12371238/* This function is called by serverCron() and is used in order to perform1239 * operations on clients that are important to perform constantly. For instance1240 * we use this function in order to disconnect clients after a timeout, including1241 * clients blocked in some blocking command with a non-zero timeout.1242 *1243 * The function makes some effort to process all the clients every second, even1244 * if this cannot be strictly guaranteed, since serverCron() may be called with1245 * an actual frequency lower than server.hz in case of latency events like slow1246 * commands.1247 *1248 * It is very important for this function, and the functions it calls, to be1249 * very fast: sometimes Redis has tens of hundreds of connected clients, and the1250 * default server.hz value is 10, so sometimes here we need to process thousands1251 * of clients per second, turning this function into a source of latency.1252 */1253void clientsCron(void) {1254    /* Try to process at least numclients/server.hz of clients1255     * per call. Since normally (if there are no big latency events) this1256     * function is called server.hz times per second, in the average case we1257     * process all the clients in 1 second. */1258    int numclients = listLength(server.clients);1259    int iterations = numclients/server.hz;12601261    /* Process at least a few clients while we are at it, even if we need1262     * to process less than CLIENTS_CRON_MIN_ITERATIONS to meet our contract1263     * of processing each client once per second. */1264    if (iterations < CLIENTS_CRON_MIN_ITERATIONS)1265        iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ?1266                     numclients : CLIENTS_CRON_MIN_ITERATIONS;126712681269    CurrentPeakMemUsageSlot = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS;1270    /* Always zero the next sample, so that when we switch to that second, we'll1271     * only register samples that are greater in that second without considering1272     * the history of such slot.1273     *1274     * Note: our index may jump to any random position if serverCron() is not1275     * called for some reason with the normal frequency, for instance because1276     * some slow command is called taking multiple seconds to execute. In that1277     * case our array may end containing data which is potentially older1278     * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem1279     * since here we want just to track if "recently" there were very expansive1280     * clients from the POV of memory usage. */1281    int zeroidx = (CurrentPeakMemUsageSlot+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS;1282    ClientsPeakMemInput[zeroidx] = 0;1283    ClientsPeakMemOutput[zeroidx] = 0;12841285    while(listLength(server.clients) && iterations--) {1286        client *c;1287        listNode *head;12881289        /* Take the current head, process, and then rotate the head to tail.1290         * This way we can fairly iterate all clients step by step. */1291        head = listFirst(server.clients);1292        c = listNodeValue(head);1293        listRotateHeadToTail(server.clients);12941295        /* Clients handled by IO threads will be processed by IOThreadClientsCron. */1296        if (c->tid != IOTHREAD_MAIN_THREAD_ID) continue;12971298        clientsCronRunClient(c);1299    }1300}13011302/* This function handles 'background' operations we are required to do1303 * incrementally in Redis databases, such as active key expiring, resizing,1304 * rehashing. */1305void databasesCron(void) {1306    /* Expire keys by random sampling. Not required for slaves1307     * as master will synthesize DELs for us. */1308    if (server.active_expire_enabled) {1309        if (iAmMaster()) {1310            activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);1311        } else {1312            expireSlaveKeys();1313        }1314    }13151316    /* Defrag keys gradually. */1317    activeDefragCycle();13181319    /* Handle active-trim */1320    if (server.cluster_enabled)1321        asmActiveTrimCycle();13221323    /* Perform hash tables rehashing if needed, but only if there are no1324     * other processes saving the DB on disk. Otherwise rehashing is bad1325     * as will cause a lot of copy-on-write of memory pages. */1326    if (!hasActiveChildProcess()) {1327        /* We use global counters so if we stop the computation at a given1328         * DB we'll be able to start from the successive in the next1329         * cron loop iteration. */1330        static unsigned int resize_db = 0;1331        static unsigned int rehash_db = 0;1332        int dbs_per_call = CRON_DBS_PER_CALL;1333        int j;13341335        /* Don't test more DBs than we have. */1336        if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;13371338        for (j = 0; j < dbs_per_call; j++) {1339            redisDb *db = &server.db[resize_db % server.dbnum];1340            kvstoreTryResizeDicts(db->keys, CRON_DICTS_PER_DB);1341            kvstoreTryResizeDicts(db->expires, CRON_DICTS_PER_DB);1342            resize_db++;1343        }13441345        /* Rehash */1346        if (server.activerehashing) {1347            uint64_t elapsed_us = 0;1348            for (j = 0; j < dbs_per_call; j++) {1349                redisDb *db = &server.db[rehash_db % server.dbnum];1350                elapsed_us += kvstoreIncrementallyRehash(db->keys, INCREMENTAL_REHASHING_THRESHOLD_US - elapsed_us);1351                if (elapsed_us >= INCREMENTAL_REHASHING_THRESHOLD_US)1352                    break;1353                elapsed_us += kvstoreIncrementallyRehash(db->expires, INCREMENTAL_REHASHING_THRESHOLD_US - elapsed_us);1354                if (elapsed_us >= INCREMENTAL_REHASHING_THRESHOLD_US)1355                    break;1356                rehash_db++;1357            }1358        }1359    }1360}13611362static inline void updateCachedTimeWithUs(int update_daylight_info, const long long ustime) {1363    server.ustime = ustime;1364    server.mstime = server.ustime / 1000;1365    time_t unixtime = server.mstime / 1000;1366    atomicSet(server.unixtime, unixtime);13671368    /* To get information about daylight saving time, we need to call1369     * localtime_r and cache the result. However calling localtime_r in this1370     * context is safe since we will never fork() while here, in the main1371     * thread. The logging function will call a thread safe version of1372     * localtime that has no locks. */1373    if (update_daylight_info) {1374        struct tm tm;1375        time_t ut = server.unixtime;1376        localtime_r(&ut,&tm);1377        atomicSet(server.daylight_active, tm.tm_isdst);1378    }1379}13801381/* We take a cached value of the unix time in the global state because with1382 * virtual memory and aging there is to store the current time in objects at1383 * every object access, and accuracy is not needed. To access a global var is1384 * a lot faster than calling time(NULL).1385 *1386 * This function should be fast because it is called at every command execution1387 * in call(), so it is possible to decide if to update the daylight saving1388 * info or not using the 'update_daylight_info' argument. Normally we update1389 * such info only when calling this function from serverCron() but not when1390 * calling it from call(). */1391void updateCachedTime(int update_daylight_info) {1392    const long long us = ustime();1393    updateCachedTimeWithUs(update_daylight_info, us);1394}13951396/* Performing required operations in order to enter an execution unit.1397 * In general, if we are already inside an execution unit then there is nothing to do,1398 * otherwise we need to update cache times so the same cached time will be used all over1399 * the execution unit.1400 * update_cached_time - if 0, will not update the cached time even if required.1401 * us - if not zero, use this time for cached time, otherwise get current time. */1402void enterExecutionUnit(int update_cached_time, long long us) {1403    if (server.execution_nesting++ == 0 && update_cached_time) {1404        if (us == 0) {1405            us = ustime();1406        }1407        updateCachedTimeWithUs(0, us);1408        server.cmd_time_snapshot = server.mstime;1409    }1410}14111412void exitExecutionUnit(void) {1413    --server.execution_nesting;1414}14151416void checkChildrenDone(void) {1417    int statloc = 0;1418    pid_t pid;14191420    if ((pid = waitpid(-1, &statloc, WNOHANG)) != 0) {1421        int exitcode = WIFEXITED(statloc) ? WEXITSTATUS(statloc) : -1;1422        int bysignal = 0;14231424        if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);14251426        /* sigKillChildHandler catches the signal and calls exit(), but we1427         * must make sure not to flag lastbgsave_status, etc incorrectly.1428         * We could directly terminate the child process via SIGUSR11429         * without handling it */1430        if (exitcode == SERVER_CHILD_NOERROR_RETVAL) {1431            bysignal = SIGUSR1;1432            exitcode = 1;1433        }14341435        if (pid == -1) {1436            serverLog(LL_WARNING,"waitpid() returned an error: %s. "1437                "child_type: %s, child_pid = %d",1438                strerror(errno),1439                strChildType(server.child_type),1440                (int) server.child_pid);1441        } else if (pid == server.child_pid) {1442            if (server.child_type == CHILD_TYPE_RDB) {1443                backgroundSaveDoneHandler(exitcode, bysignal);1444            } else if (server.child_type == CHILD_TYPE_AOF) {1445                backgroundRewriteDoneHandler(exitcode, bysignal);1446            } else if (server.child_type == CHILD_TYPE_MODULE) {1447                ModuleForkDoneHandler(exitcode, bysignal);1448            } else {1449                serverPanic("Unknown child type %d for child pid %d", server.child_type, server.child_pid);1450                exit(1);1451            }1452            if (!bysignal && exitcode == 0) receiveChildInfo();1453            resetChildState();1454        } else {1455            if (!ldbRemoveChild(pid)) {1456                serverLog(LL_WARNING,1457                          "Warning, detected child with unmatched pid: %ld",1458                          (long) pid);1459            }1460        }14611462        /* start any pending forks immediately. */1463        replicationStartPendingFork();1464    }1465}14661467/* Record the max memory used since the server was started. */1468void updatePeakMemory(void) {1469    size_t zmalloc_used = zmalloc_used_memory();1470    if (zmalloc_used > server.stat_peak_memory) {1471        server.stat_peak_memory = zmalloc_used;1472        server.stat_peak_memory_time = server.unixtime;1473    }14741475    size_t zmalloc_peak = zmalloc_get_peak_memory();1476    if (zmalloc_peak > server.stat_peak_memory) {1477        server.stat_peak_memory = zmalloc_peak;1478        server.stat_peak_memory_time = zmalloc_get_peak_memory_time();1479    }1480}14811482/* Called from serverCron and cronUpdateMemoryStats to update cached memory metrics. */1483void cronUpdateMemoryStats(void) {1484    updatePeakMemory();14851486    run_with_period(100) {1487        /* Sample the RSS and other metrics here since this is a relatively slow call.1488         * We must sample the zmalloc_used at the same time we take the rss, otherwise1489         * the frag ratio calculate may be off (ratio of two samples at different times) */1490        server.cron_malloc_stats.process_rss = zmalloc_get_rss();1491        server.cron_malloc_stats.zmalloc_used = zmalloc_used_memory();1492        /* Sampling the allocator info can be slow too.1493         * The fragmentation ratio it'll show is potentially more accurate1494         * it excludes other RSS pages such as: shared libraries, LUA and other non-zmalloc1495         * allocations, and allocator reserved pages that can be pursed (all not actual frag) */1496        zmalloc_get_allocator_info(1,1497                                   &server.cron_malloc_stats.allocator_allocated,1498                                   &server.cron_malloc_stats.allocator_active,1499                                   &server.cron_malloc_stats.allocator_resident,1500                                   NULL,1501                                   &server.cron_malloc_stats.allocator_muzzy,1502                                   &server.cron_malloc_stats.allocator_frag_smallbins_bytes);1503        if (server.lua_arena != UINT_MAX) {1504            zmalloc_get_allocator_info_by_arena(server.lua_arena,1505                                                0,1506                                                &server.cron_malloc_stats.lua_allocator_allocated,1507                                                &server.cron_malloc_stats.lua_allocator_active,1508                                                &server.cron_malloc_stats.lua_allocator_resident,1509                                                &server.cron_malloc_stats.lua_allocator_frag_smallbins_bytes);1510        }1511        /* Publish the just-measured (Lua-arena-subtracted) fragmentation1512         * values into the defrag-side tick-local cache so1513         * computeDefragCycles() later this tick can skip its own1514         * duplicate jemalloc measurement. Mirrors1515         * getAllocatorFragmentation()'s Lua subtraction so the cached1516         * value matches the fall-through real measurement exactly.1517         * Publishing with allocated==0 leaves the cache invalid1518         * (sentinel set inside the publish)  defrag should decide on1519         * real data or none. */1520        size_t frag  = server.cron_malloc_stats.allocator_frag_smallbins_bytes;1521        size_t alloc = server.cron_malloc_stats.allocator_allocated;1522        if (alloc > 0 && server.lua_arena != UINT_MAX) {1523            frag  -= server.cron_malloc_stats.lua_allocator_frag_smallbins_bytes;1524            alloc -= server.cron_malloc_stats.lua_allocator_allocated;1525        }1526        defragFragCachePut(frag, alloc);1527        /* in case the allocator isn't providing these stats, fake them so that1528         * fragmentation info still shows some (inaccurate metrics) */1529        if (!server.cron_malloc_stats.allocator_resident)1530            server.cron_malloc_stats.allocator_resident = server.cron_malloc_stats.process_rss;1531        if (!server.cron_malloc_stats.allocator_active)1532            server.cron_malloc_stats.allocator_active = server.cron_malloc_stats.allocator_resident;1533        if (!server.cron_malloc_stats.allocator_allocated)1534            server.cron_malloc_stats.allocator_allocated = server.cron_malloc_stats.zmalloc_used;1535    }1536}15371538/* This is our timer interrupt, called server.hz times per second.1539 * Here is where we do a number of things that need to be done asynchronously.1540 * For instance:1541 *1542 * - Active expired keys collection (it is also performed in a lazy way on1543 *   lookup).1544 * - Software watchdog.1545 * - Update some statistic.1546 * - Incremental rehashing of the DBs hash tables.1547 * - Triggering BGSAVE / AOF rewrite, and handling of terminated children.1548 * - Clients timeout of different kinds.1549 * - Replication reconnection.1550 * - Many more...1551 *1552 * Everything directly called here will be called server.hz times per second,1553 * so in order to throttle execution of things we want to do less frequently1554 * a macro is used: run_with_period(milliseconds) { .... }1555 */15561557int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {1558    int j;1559    UNUSED(eventLoop);1560    UNUSED(id);1561    UNUSED(clientData);15621563    /* Software watchdog: deliver the SIGALRM that will reach the signal1564     * handler if we don't return here fast enough. */1565    if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);15661567    server.hz = server.config_hz;1568    /* Adapt the server.hz value to the number of configured clients. If we have1569     * many clients, we want to call serverCron() with an higher frequency. */1570    if (server.dynamic_hz) {1571        while (listLength(server.clients) / server.hz >1572               MAX_CLIENTS_PER_CLOCK_TICK)1573        {1574            server.hz *= 2;1575            if (server.hz > CONFIG_MAX_HZ) {1576                server.hz = CONFIG_MAX_HZ;1577                break;1578            }1579        }1580    }15811582    /* for debug purposes: skip actual cron work if pause_cron is on */1583    if (server.pause_cron) return 1000/server.hz;15841585    monotime cron_start = getMonotonicUs();15861587    run_with_period(100) {1588        long long stat_net_input_bytes, stat_net_output_bytes;1589        long long stat_net_repl_input_bytes, stat_net_repl_output_bytes;1590        atomicGet(server.stat_net_input_bytes, stat_net_input_bytes);1591        atomicGet(server.stat_net_output_bytes, stat_net_output_bytes);1592        atomicGet(server.stat_net_repl_input_bytes, stat_net_repl_input_bytes);1593        atomicGet(server.stat_net_repl_output_bytes, stat_net_repl_output_bytes);1594        monotime current_time = getMonotonicUs();1595        long long factor = 1000000;  // us1596        trackInstantaneousMetric(STATS_METRIC_COMMAND, server.stat_numcommands, current_time, factor);1597        trackInstantaneousMetric(STATS_METRIC_NET_INPUT, stat_net_input_bytes + stat_net_repl_input_bytes,1598                                 current_time, factor);1599        trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT, stat_net_output_bytes + stat_net_repl_output_bytes,1600                                 current_time, factor);1601        trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION, stat_net_repl_input_bytes, current_time,1602                                 factor);1603        trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION, stat_net_repl_output_bytes,1604                                 current_time, factor);1605        trackInstantaneousMetric(STATS_METRIC_EL_CYCLE, server.duration_stats[EL_DURATION_TYPE_EL].cnt,1606                                 current_time, factor);1607        trackInstantaneousMetric(STATS_METRIC_EL_DURATION, server.duration_stats[EL_DURATION_TYPE_EL].sum,1608                                 server.duration_stats[EL_DURATION_TYPE_EL].cnt, 1);16091610        /* Periodic cleanup of active clients sliding window to clear stale slots1611         * when no client activity occurs for extended periods */1612        statsUpdateActiveClients(NULL);1613    }16141615    /* We have just LRU_BITS bits per object for LRU information.1616     * So we use an (eventually wrapping) LRU clock.1617     *1618     * Note that even if the counter wraps it's not a big problem,1619     * everything will still work but some object will appear younger1620     * to Redis. However for this to happen a given object should never be1621     * touched for all the time needed to the counter to wrap, which is1622     * not likely.1623     *1624     * Note that you can change the resolution altering the1625     * LRU_CLOCK_RESOLUTION define. */1626    server.lruclock = getLRUClock();16271628    cronUpdateMemoryStats();16291630    /* We received a SIGTERM or SIGINT, shutting down here in a safe way, as it is1631     * not ok doing so inside the signal handler. */1632    if (shouldShutdownAsap() && !isShutdownInitiated()) {1633        int shutdownFlags = SHUTDOWN_NOFLAGS;1634        int last_sig_received;1635        atomicGet(server.last_sig_received, last_sig_received);1636        if (last_sig_received == SIGINT && server.shutdown_on_sigint)1637            shutdownFlags = server.shutdown_on_sigint;1638        else if (last_sig_received == SIGTERM && server.shutdown_on_sigterm)1639            shutdownFlags = server.shutdown_on_sigterm;16401641        if (prepareForShutdown(shutdownFlags) == C_OK) exit(0);1642    } else if (isShutdownInitiated()) {1643        if (server.mstime >= server.shutdown_mstime || isReadyToShutdown()) {1644            if (finishShutdown() == C_OK) exit(0);1645            /* Shutdown failed. Continue running. An error has been logged. */1646        }1647    }16481649    /* Show some info about non-empty databases */1650    if (server.verbosity <= LL_VERBOSE) {1651        run_with_period(5000) {1652            for (j = 0; j < server.dbnum; j++) {1653                long long size, used, vkeys;16541655                size = kvstoreBuckets(server.db[j].keys);1656                used = kvstoreSize(server.db[j].keys);1657                vkeys = kvstoreSize(server.db[j].expires);1658                if (used || vkeys) {1659                    serverLog(LL_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);1660                }1661            }1662        }1663    }16641665    /* Show information about connected clients */1666    if (!server.sentinel_mode) {1667        run_with_period(5000) {1668            serverLog(LL_DEBUG,1669                "%lu clients connected (%lu replicas), %zu bytes in use",1670                listLength(server.clients)-listLength(server.slaves),1671                replicationLogicalReplicaCount(),1672                zmalloc_used_memory());1673        }1674    }16751676    /* We need to do a few operations on clients asynchronously. */1677    clientsCron();16781679    /* Handle background operations on Redis databases. */1680    databasesCron();16811682    /* Start a scheduled AOF rewrite if this was requested by the user while1683     * a BGSAVE was in progress. */1684    if (!hasActiveChildProcess() &&1685        server.aof_rewrite_scheduled &&1686        !aofRewriteLimited())1687    {1688        rewriteAppendOnlyFileBackground();1689    }16901691    /* Check if a background saving or AOF rewrite in progress terminated. */1692    if (hasActiveChildProcess() || ldbPendingChildren())1693    {1694        run_with_period(1000) receiveChildInfo();1695        checkChildrenDone();1696    } else {1697        /* If there is not a background saving/rewrite in progress check if1698         * we have to save/rewrite now. */1699        for (j = 0; j < server.saveparamslen; j++) {1700            struct saveparam *sp = server.saveparams+j;17011702            /* Save if we reached the given amount of changes,1703             * the given amount of seconds, and if the latest bgsave was1704             * successful or if, in case of an error, at least1705             * CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed. */1706            if (server.dirty >= sp->changes &&1707                server.unixtime-server.lastsave > sp->seconds &&1708                (server.unixtime-server.lastbgsave_try >1709                 CONFIG_BGSAVE_RETRY_DELAY ||1710                 server.lastbgsave_status == C_OK))1711            {1712                serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",1713                    sp->changes, (int)sp->seconds);1714                rdbSaveInfo rsi, *rsiptr;1715                rsiptr = rdbPopulateSaveInfo(&rsi);1716                rdbSaveBackground(SLAVE_REQ_NONE,server.rdb_filename,rsiptr,RDBFLAGS_NONE);1717                break;1718            }1719        }17201721        /* Trigger an AOF rewrite if needed. */1722        if (server.aof_state == AOF_ON &&1723            !hasActiveChildProcess() &&1724            server.aof_rewrite_perc &&1725            server.aof_current_size > server.aof_rewrite_min_size)1726        {1727            long long base = server.aof_rewrite_base_size ?1728                server.aof_rewrite_base_size : 1;1729            long long growth = (server.aof_current_size*100/base) - 100;1730            if (growth >= server.aof_rewrite_perc && !aofRewriteLimited()) {1731                serverLog(LL_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);1732                rewriteAppendOnlyFileBackground();1733            }1734        }1735    }1736    /* Just for the sake of defensive programming, to avoid forgetting to1737     * call this function when needed. */1738    updateDictResizePolicy();17391740    /* AOF postponed flush: Try at every cron cycle if the slow fsync1741     * completed. */1742    if ((server.aof_state == AOF_ON || server.aof_state == AOF_WAIT_REWRITE) &&1743        server.aof_flush_postponed_start)1744    {1745        flushAppendOnlyFile(0);1746    }17471748    /* AOF write errors: in this case we have a buffer to flush as well and1749     * clear the AOF error in case of success to make the DB writable again,1750     * however to try every second is enough in case of 'hz' is set to1751     * a higher frequency. */1752    run_with_period(1000) {1753        if ((server.aof_state == AOF_ON || server.aof_state == AOF_WAIT_REWRITE) &&1754            server.aof_last_write_status == C_ERR) 1755            {1756                flushAppendOnlyFile(0);1757            }1758    }17591760    /* Clear the paused actions state if needed. */1761    updatePausedActions();17621763    /* Replication cron function -- used to reconnect to master,1764     * detect transfer failures, start background RDB transfers and so forth. 1765     * 1766     * If Redis is trying to failover then run the replication cron faster so1767     * progress on the handshake happens more quickly. */1768    if (server.failover_state != NO_FAILOVER) {1769        run_with_period(100) replicationCron();1770    } else {1771        run_with_period(1000) replicationCron();1772    }17731774    /* Run the Redis Cluster cron. */1775    run_with_period(100) {1776        if (server.cluster_enabled) {1777            clusterCron();1778            asmCron();1779        }1780    }17811782    /* Run the Sentinel timer if we are in sentinel mode. */1783    if (server.sentinel_mode) sentinelTimer();17841785    /* Cleanup expired MIGRATE cached sockets. */1786    run_with_period(1000) {1787        migrateCloseTimedoutSockets();1788    }17891790    /* Cleanup expired IDMP entries from tracked streams */1791    run_with_period(1000) {1792        handleExpiredIdmpEntries();1793    }17941795    /* Periodically shrink pending command reuse pool */1796    run_with_period(2000) {1797        pendingCommandPoolCron();1798    }17991800    /* Resize tracking keys table if needed. This is also done at every1801     * command execution, but we want to be sure that if the last command1802     * executed changes the value via CONFIG SET, the server will perform1803     * the operation even if completely idle. */1804    if (server.tracking_clients) trackingLimitUsedSlots();18051806    /* Check if hotkey tracking duration has expired and auto-stop if needed */1807    if (server.hotkeys && server.hotkeys->active && server.hotkeys->duration > 0) {1808        mstime_t elapsed = (server.mstime - server.hotkeys->start);1809        if (elapsed >= server.hotkeys->duration) {1810            server.hotkeys->active = 0;1811            server.hotkeys->duration = elapsed;1812        }1813    }18141815    /* Start a scheduled BGSAVE if the corresponding flag is set. This is1816     * useful when we are forced to postpone a BGSAVE because an AOF1817     * rewrite is in progress.1818     *1819     * Note: this code must be after the replicationCron() call above so1820     * make sure when refactoring this file to keep this order. This is useful1821     * because we want to give priority to RDB savings for replication. */1822    if (!hasActiveChildProcess() &&1823        server.rdb_bgsave_scheduled &&1824        (server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||1825         server.lastbgsave_status == C_OK))1826    {1827        rdbSaveInfo rsi, *rsiptr;1828        rsiptr = rdbPopulateSaveInfo(&rsi);1829        if (rdbSaveBackground(SLAVE_REQ_NONE,server.rdb_filename,rsiptr,RDBFLAGS_NONE) == C_OK)1830            server.rdb_bgsave_scheduled = 0;1831    }18321833    run_with_period(100) {1834        if (moduleCount()) modulesCron();1835    }18361837    /* Fire the cron loop modules event. */1838    RedisModuleCronLoopV1 ei = {REDISMODULE_CRON_LOOP_VERSION,server.hz};1839    moduleFireServerEvent(REDISMODULE_EVENT_CRON_LOOP,1840                          0,1841                          &ei);18421843    server.cronloops++;18441845    server.el_cron_duration = getMonotonicUs() - cron_start;18461847    return 1000/server.hz;1848}184918501851void blockingOperationStarts(void) {1852    if(!server.blocking_op_nesting++){1853        updateCachedTime(0);1854        server.blocked_last_cron = server.mstime;1855    }1856}18571858void blockingOperationEnds(void) {1859    if(!(--server.blocking_op_nesting)){1860        server.blocked_last_cron = 0;1861    }1862}18631864/* This function fills in the role of serverCron during RDB or AOF loading, and1865 * also during blocked scripts.1866 * It attempts to do its duties at a similar rate as the configured server.hz,1867 * and updates cronloops variable so that similarly to serverCron, the1868 * run_with_period can be used. */1869void whileBlockedCron(void) {1870    /* Here we may want to perform some cron jobs (normally done server.hz times1871     * per second). */18721873    /* Since this function depends on a call to blockingOperationStarts, let's1874     * make sure it was done. */1875    serverAssert(server.blocked_last_cron);18761877    /* In case we were called too soon, leave right away. This way one time1878     * jobs after the loop below don't need an if. and we don't bother to start1879     * latency monitor if this function is called too often. */1880    if (server.blocked_last_cron >= server.mstime)1881        return;18821883    /* Increment server.cronloops so that run_with_period works. */1884    long hz_ms = 1000 / server.hz;1885    int cronloops = (server.mstime - server.blocked_last_cron + (hz_ms - 1)) / hz_ms; /* rounding up */1886    server.blocked_last_cron += cronloops * hz_ms;1887    server.cronloops += cronloops;18881889    mstime_t latency;1890    latencyStartMonitor(latency);18911892    /* Only defragment during AOF loading. */1893    if (isAOFLoadingContext()) defragWhileBlocked();18941895    /* Update memory stats during loading (excluding blocked scripts) */1896    if (server.loading) cronUpdateMemoryStats();18971898    latencyEndMonitor(latency);1899    latencyAddSampleIfNeeded("while-blocked-cron",latency);19001901    /* We received a SIGTERM during loading, shutting down here in a safe way,1902     * as it isn't ok doing so inside the signal handler. */1903    if (shouldShutdownAsap() && server.loading) {1904        if (prepareForShutdown(SHUTDOWN_NOSAVE) == C_OK) exit(0);1905        serverLog(LL_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");1906        atomicSet(server.shutdown_asap, 0);1907        atomicSet(server.last_sig_received, 0);1908    }1909}19101911static void sendGetackToReplicas(void) {1912    robj *argv[3];1913    argv[0] = shared.replconf;1914    argv[1] = shared.getack;1915    argv[2] = shared.special_asterick; /* Not used argument. */1916    replicationFeedSlaves(server.slaves, -1, argv, 3);1917}19181919extern int ProcessingEventsWhileBlocked;19201921/* This function gets called every time Redis is entering the1922 * main loop of the event driven library, that is, before to sleep1923 * for ready file descriptors.1924 *1925 * Note: This function is (currently) called from two functions:1926 * 1. aeMain - The main server loop1927 * 2. processEventsWhileBlocked - Process clients during RDB/AOF load1928 *1929 * If it was called from processEventsWhileBlocked we don't want1930 * to perform all actions (For example, we don't want to expire1931 * keys), but we do need to perform some actions.1932 *1933 * The most important is freeClientsInAsyncFreeQueue but we also1934 * call some other low-risk functions. */1935void beforeSleep(struct aeEventLoop *eventLoop) {1936    UNUSED(eventLoop);19371938    updatePeakMemory();19391940    /* Just call a subset of vital functions in case we are re-entering1941     * the event loop from processEventsWhileBlocked(). Note that in this1942     * case we keep track of the number of events we are processing, since1943     * processEventsWhileBlocked() wants to stop ASAP if there are no longer1944     * events to handle. */1945    if (ProcessingEventsWhileBlocked) {1946        uint64_t processed = 0;1947        processed += connTypeProcessPendingData(server.el);1948        if (server.aof_state == AOF_ON || server.aof_state == AOF_WAIT_REWRITE)1949            flushAppendOnlyFile(0);1950        processed += handleClientsWithPendingWrites();1951        processed += freeClientsInAsyncFreeQueue();19521953        /* Let the clients after the blocking call be processed. */1954        processClientsOfAllIOThreads();1955        /* New connections may have been established while blocked, clients from1956         * IO thread may have replies to write, ensure they are promptly sent to1957         * IO threads. */1958        processed += sendPendingClientsToIOThreads();19591960        server.events_processed_while_blocked += processed;1961        return;1962    }19631964    /* Handle pending data(typical TLS). (must be done before flushAppendOnlyFile) */1965    connTypeProcessPendingData(server.el);19661967    /* If any connection type(typical TLS) still has pending unread data don't sleep at all. */1968    int dont_sleep = connTypeHasPendingData(server.el);19691970    /* Call the Redis Cluster before sleep function. Note that this function1971     * may change the state of Redis Cluster (from ok to fail or vice versa),1972     * so it's a good idea to call it before serving the unblocked clients1973     * later in this function, must be done before blockedBeforeSleep. */1974    if (server.cluster_enabled) {1975        clusterBeforeSleep();1976        asmBeforeSleep();1977    }19781979    /* Handle blocked clients.1980     * must be done before flushAppendOnlyFile, in case of appendfsync=always,1981     * since the unblocked clients may write data. */1982    blockedBeforeSleep();19831984    /* Record cron time in beforeSleep, which is the sum of active-expire, active-defrag and all other1985     * tasks done by cron and beforeSleep, but excluding read, write and AOF, that are counted by other1986     * sets of metrics. */1987    monotime cron_start_time_before_aof = getMonotonicUs();19881989    /* Run a fast expire cycle (the called function will return1990     * ASAP if a fast cycle is not needed). */1991    if (server.active_expire_enabled && iAmMaster())1992        activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);19931994    if (moduleCount()) {1995        moduleFireServerEvent(REDISMODULE_EVENT_EVENTLOOP,1996                              REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP,1997                              NULL);1998    }19992000    /* Send all the slaves an ACK request if at least one client blocked

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.