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.