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 "lzf.h" /* LZF compression library */17#include "zipmap.h"18#include "endianconv.h"19#include "fpconv_dtoa.h"20#include "stream.h"21#include "functions.h"22#include "intset.h" /* Compact integer set structure */23#include "bio.h"24#include "cluster_asm.h"25#include "keymeta.h"2627#include <math.h>28#include <fcntl.h>29#include <sys/types.h>30#include <sys/time.h>31#include <sys/resource.h>32#include <sys/wait.h>33#include <arpa/inet.h>34#include <sys/stat.h>35#include <sys/param.h>3637/* This macro is called when the internal RDB structure is corrupt */38#define rdbReportCorruptRDB(...) rdbReportError(1, __LINE__,__VA_ARGS__)39/* This macro is called when RDB read failed (possibly a short read) */40#define rdbReportReadError(...) rdbReportError(0, __LINE__,__VA_ARGS__)4142/* This macro tells if we are in the context of a RESTORE command, and not loading an RDB or AOF. */43#define isRestoreContext() \44 ((server.current_client == NULL || server.current_client->id == CLIENT_ID_AOF) ? 0 : 1)4546char* rdbFileBeingLoaded = NULL; /* used for rdb checking on read error */4748extern int rdbCheckMode;49void rdbCheckError(const char *fmt, ...);50void rdbCheckSetError(const char *fmt, ...);5152#ifdef __GNUC__53void rdbReportError(int corruption_error, int linenum, char *reason, ...) __attribute__ ((format (printf, 3, 4)));54#endif55void rdbReportError(int corruption_error, int linenum, char *reason, ...) {56 va_list ap;57 char msg[1024];58 int len;5960 len = snprintf(msg,sizeof(msg),61 "Internal error in RDB reading offset %llu, function at rdb.c:%d -> ",62 (unsigned long long)server.loading_loaded_bytes, linenum);63 va_start(ap,reason);64 vsnprintf(msg+len,sizeof(msg)-len,reason,ap);65 va_end(ap);6667 if (isRestoreContext()) {68 /* If we're in the context of a RESTORE command, just propagate the error. */69 /* log in VERBOSE, and return (don't exit). */70 serverLog(LL_VERBOSE, "%s", msg);71 return;72 } else if (rdbCheckMode) {73 /* If we're inside the rdb checker, let it handle the error. */74 rdbCheckError("%s",msg);75 } else if (rdbFileBeingLoaded) {76 /* If we're loading an rdb file form disk, run rdb check (and exit) */77 serverLog(LL_WARNING, "%s", msg);78 char *argv[2] = {"",rdbFileBeingLoaded};79 if (anetIsFifo(argv[1])) {80 /* Cannot check RDB FIFO because we cannot reopen the FIFO and check already streamed data. */81 rdbCheckError("Cannot check RDB that is a FIFO: %s", argv[1]);82 return;83 }84 rdbClearHashTemplates(); /* Clear loading map if any */85 redis_check_rdb_main(2,argv,NULL);86 } else if (corruption_error) {87 /* In diskless loading, in case of corrupt file, log and exit. */88 serverLog(LL_WARNING, "%s. Failure loading rdb format", msg);89 } else {90 /* In diskless loading, in case of a short read (not a corrupt91 * file), log and proceed (don't exit). */92 serverLog(LL_WARNING, "%s. Failure loading rdb format from socket, assuming connection error, resuming operation.", msg);93 return;94 }95 serverLog(LL_WARNING, "Terminating server after rdb file reading failure.");96 exit(1);97}9899ssize_t rdbWriteRaw(rio *rdb, void *p, size_t len) {100 if (rdb && rioWrite(rdb,p,len) == 0)101 return -1;102 return len;103}104105static inline int rdbIsDumpPayload(const rio *rdb) {106 /* A NULL rio is used by DEBUG OBJECT, which historically reports the DUMP107 * payload serialization size. */108 return rdb == NULL || (rdb->flags & RIO_FLAG_DUMP_PAYLOAD);109}110111int rdbSaveType(rio *rdb, unsigned char type) {112 return rdbWriteRaw(rdb,&type,1);113}114115/* Load a "type" in RDB format, that is a one byte unsigned integer.116 * This function is not only used to load object types, but also special117 * "types" like the end-of-file type, the EXPIRE type, and so forth. */118int rdbLoadType(rio *rdb) {119 unsigned char type;120 if (rioRead(rdb,&type,1) == 0) return -1;121 return type;122}123124/* This is only used to load old databases stored with the RDB_OPCODE_EXPIRETIME125 * opcode. New versions of Redis store using the RDB_OPCODE_EXPIRETIME_MS126 * opcode. On error -1 is returned, however this could be a valid time, so127 * to check for loading errors the caller should call rioGetReadError() after128 * calling this function. */129time_t rdbLoadTime(rio *rdb) {130 int32_t t32;131 if (rioRead(rdb,&t32,4) == 0) return -1;132 return (time_t)t32;133}134135/* Save a signed 64-bit integer in little-endian format. */136ssize_t rdbSaveSignedInteger(rio *rdb, int64_t val) {137 memrev64ifbe(&val); /* Store in little endian. */138 return rdbWriteRaw(rdb, &val, 8);139}140141/* This function loads a signed 64-bit integer from the RDB file. It gets the142 * version of the RDB because, unfortunately, before Redis 5 (RDB version 9),143 * the function failed to convert data to/from little endian, so RDB files with144 * keys having expires could not be shared between big endian and little endian145 * systems (because the expire time will be totally wrong). The fix for this is146 * just to call memrev64ifbe(), however if we fix this for all the RDB versions,147 * this call will introduce an incompatibility for big endian systems:148 * after upgrading to Redis version 5 they will no longer be able to load their149 * own old RDB files. Because of that, we instead fix the function only for new150 * RDB versions, and load older RDB versions as we used to do in the past,151 * allowing big endian systems to load their own old RDB files.152 *153 * On I/O error the function returns INT64_MAX, however if this is also a154 * valid stored value, the caller should use rioGetReadError() to check for155 * errors after calling this function. */156int64_t rdbLoadSignedInteger(rio *rdb, int rdbver) {157 int64_t val;158 if (rioRead(rdb, &val, 8) == 0) return INT64_MAX;159 if (rdbver >= 9) /* Check the top comment of this function. */160 memrev64ifbe(&val); /* Convert in big endian if the system is BE. */161 return val;162}163164/* Wrappers for millisecond time - these just call the signed integer functions */165ssize_t rdbSaveMillisecondTime(rio *rdb, long long t) {166 return rdbSaveSignedInteger(rdb, (int64_t)t);167}168169long long rdbLoadMillisecondTime(rio *rdb, int rdbver) {170 return (long long)rdbLoadSignedInteger(rdb, rdbver);171}172173/* Saves an encoded length. The first two bits in the first byte are used to174 * hold the encoding type. See the RDB_* definitions for more information175 * on the types of encoding. */176int rdbSaveLen(rio *rdb, uint64_t len) {177 unsigned char buf[2];178 size_t nwritten;179180 if (len < (1<<6)) {181 /* Save a 6 bit len */182 buf[0] = (len&0xFF)|(RDB_6BITLEN<<6);183 if (rdbWriteRaw(rdb,buf,1) == -1) return -1;184 nwritten = 1;185 } else if (len < (1<<14)) {186 /* Save a 14 bit len */187 buf[0] = ((len>>8)&0xFF)|(RDB_14BITLEN<<6);188 buf[1] = len&0xFF;189 if (rdbWriteRaw(rdb,buf,2) == -1) return -1;190 nwritten = 2;191 } else if (len <= UINT32_MAX) {192 /* Save a 32 bit len */193 buf[0] = RDB_32BITLEN;194 if (rdbWriteRaw(rdb,buf,1) == -1) return -1;195 uint32_t len32 = htonl(len);196 if (rdbWriteRaw(rdb,&len32,4) == -1) return -1;197 nwritten = 1+4;198 } else {199 /* Save a 64 bit len */200 buf[0] = RDB_64BITLEN;201 if (rdbWriteRaw(rdb,buf,1) == -1) return -1;202 len = htonu64(len);203 if (rdbWriteRaw(rdb,&len,8) == -1) return -1;204 nwritten = 1+8;205 }206 return nwritten;207}208209210/* Load an encoded length. If the loaded length is a normal length as stored211 * with rdbSaveLen(), the read length is set to '*lenptr'. If instead the212 * loaded length describes a special encoding that follows, then '*isencoded'213 * is set to 1 and the encoding format is stored at '*lenptr'.214 *215 * See the RDB_ENC_* definitions in rdb.h for more information on special216 * encodings.217 *218 * The function returns -1 on error, 0 on success. */219int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr) {220 unsigned char buf[2];221 int type;222223 if (isencoded) *isencoded = 0;224 if (rioRead(rdb,buf,1) == 0) return -1;225 type = (buf[0]&0xC0)>>6;226 if (type == RDB_ENCVAL) {227 /* Read a 6 bit encoding type. */228 if (isencoded) *isencoded = 1;229 *lenptr = buf[0]&0x3F;230 } else if (type == RDB_6BITLEN) {231 /* Read a 6 bit len. */232 *lenptr = buf[0]&0x3F;233 } else if (type == RDB_14BITLEN) {234 /* Read a 14 bit len. */235 if (rioRead(rdb,buf+1,1) == 0) return -1;236 *lenptr = ((buf[0]&0x3F)<<8)|buf[1];237 } else if (buf[0] == RDB_32BITLEN) {238 /* Read a 32 bit len. */239 uint32_t len;240 if (rioRead(rdb,&len,4) == 0) return -1;241 *lenptr = ntohl(len);242 } else if (buf[0] == RDB_64BITLEN) {243 /* Read a 64 bit len. */244 uint64_t len;245 if (rioRead(rdb,&len,8) == 0) return -1;246 *lenptr = ntohu64(len);247 } else {248 rdbReportCorruptRDB(249 "Unknown length encoding %d in rdbLoadLen()",type);250 return -1; /* Never reached. */251 }252 return 0;253}254255/* This is like rdbLoadLenByRef() but directly returns the value read256 * from the RDB stream, signaling an error by returning RDB_LENERR257 * (since it is a too large count to be applicable in any Redis data258 * structure). */259uint64_t rdbLoadLen(rio *rdb, int *isencoded) {260 uint64_t len;261262 if (rdbLoadLenByRef(rdb,isencoded,&len) == -1) return RDB_LENERR;263 return len;264}265266/* Encodes the "value" argument as integer when it fits in the supported ranges267 * for encoded types. If the function successfully encodes the integer, the268 * representation is stored in the buffer pointer to by "enc" and the string269 * length is returned. Otherwise 0 is returned. */270int rdbEncodeInteger(long long value, unsigned char *enc) {271 if (value >= -(1<<7) && value <= (1<<7)-1) {272 enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT8;273 enc[1] = value&0xFF;274 return 2;275 } else if (value >= -(1<<15) && value <= (1<<15)-1) {276 enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT16;277 enc[1] = value&0xFF;278 enc[2] = (value>>8)&0xFF;279 return 3;280 } else if (value >= -((long long)1<<31) && value <= ((long long)1<<31)-1) {281 enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT32;282 enc[1] = value&0xFF;283 enc[2] = (value>>8)&0xFF;284 enc[3] = (value>>16)&0xFF;285 enc[4] = (value>>24)&0xFF;286 return 5;287 } else {288 return 0;289 }290}291292/* Loads an integer-encoded object with the specified encoding type "enctype".293 * The returned value changes according to the flags, see294 * rdbGenericLoadStringObject() for more info. */295void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr, size_t *usable) {296 int plainFlag = flags & RDB_LOAD_PLAIN;297 int sdsFlag = flags & RDB_LOAD_SDS;298 int encode = flags & RDB_LOAD_ENC;299 unsigned char enc[4];300 long long val;301302 if (enctype == RDB_ENC_INT8) {303 if (rioRead(rdb,enc,1) == 0) return NULL;304 val = (signed char)enc[0];305 } else if (enctype == RDB_ENC_INT16) {306 uint16_t v;307 if (rioRead(rdb,enc,2) == 0) return NULL;308 v = ((uint32_t)enc[0])|309 ((uint32_t)enc[1]<<8);310 val = (int16_t)v;311 } else if (enctype == RDB_ENC_INT32) {312 uint32_t v;313 if (rioRead(rdb,enc,4) == 0) return NULL;314 v = ((uint32_t)enc[0])|315 ((uint32_t)enc[1]<<8)|316 ((uint32_t)enc[2]<<16)|317 ((uint32_t)enc[3]<<24);318 val = (int32_t)v;319 } else {320 rdbReportCorruptRDB("Unknown RDB integer encoding type %d",enctype);321 return NULL; /* Never reached. */322 }323 if (plainFlag || sdsFlag) {324 char buf[LONG_STR_SIZE], *p;325 int len = ll2string(buf,sizeof(buf),val);326 if (lenptr) *lenptr = len;327 if (plainFlag) {328 p = zmalloc_usable(len, usable);329 } else {330 debugServerAssert(sdsFlag);331 p = sdsnewlen(SDS_NOINIT,len);332 if (usable) *usable = sdsAllocSize(p);333 } 334 335 memcpy(p,buf,len);336 return p;337 } else if (encode) {338 return createStringObjectFromLongLongForValue(val);339 } else {340 return createStringObjectFromLongLongWithSds(val);341 }342}343344/* String objects in the form "2391" "-100" without any space and with a345 * range of values that can fit in an 8, 16 or 32 bit signed value can be346 * encoded as integers to save space */347int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {348 long long value;349 if (string2ll(s, len, &value)) {350 return rdbEncodeInteger(value, enc);351 } else {352 return 0;353 }354}355356ssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,357 size_t original_len) {358 unsigned char byte;359 ssize_t n, nwritten = 0;360361 /* Data compressed! Let's save it on disk */362 byte = (RDB_ENCVAL<<6)|RDB_ENC_LZF;363 if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;364 nwritten += n;365366 if ((n = rdbSaveLen(rdb,compress_len)) == -1) goto writeerr;367 nwritten += n;368369 if ((n = rdbSaveLen(rdb,original_len)) == -1) goto writeerr;370 nwritten += n;371372 if ((n = rdbWriteRaw(rdb,data,compress_len)) == -1) goto writeerr;373 nwritten += n;374375 return nwritten;376377writeerr:378 return -1;379}380381ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {382 size_t comprlen, outlen;383 void *out;384385 /* We require at least four bytes compression for this to be worth it */386 if (len <= 4) return 0;387 outlen = len-4;388 if ((out = zmalloc(outlen+1)) == NULL) return 0;389 comprlen = lzf_compress(s, len, out, outlen);390 if (comprlen == 0) {391 zfree(out);392 return 0;393 }394 ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);395 zfree(out);396 return nwritten;397}398399/* Load an LZF compressed string in RDB format. The returned value400 * changes according to 'flags'. For more info check the401 * rdbGenericLoadStringObject() function. */402void *rdbLoadLzfStringObject(rio *rdb, int flags, size_t *lenptr, size_t *usable) {403 int plainFlag = flags & RDB_LOAD_PLAIN;404 int sdsFlag = flags & RDB_LOAD_SDS;405 int robjFlag = !(plainFlag || sdsFlag); /* not plain/sds */406407 uint64_t len, clen;408 unsigned char *c = NULL;409 char *val = NULL;410411 if ((clen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;412 if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;413 if ((c = ztrymalloc(clen)) == NULL) {414 serverLog(isRestoreContext()? LL_VERBOSE: LL_WARNING, "rdbLoadLzfStringObject failed allocating %llu bytes", (unsigned long long)clen);415 goto err;416 }417418 /* Allocate our target according to the uncompressed size. */419 if (plainFlag) {420 val = ztrymalloc_usable(len, usable);421 } else {422 debugServerAssert(sdsFlag || robjFlag);423 val = sdstrynewlen(SDS_NOINIT,len);424 if (usable) *usable = sdsAllocSize(val);425 }426427 if (!val) {428 serverLog(isRestoreContext()? LL_VERBOSE: LL_WARNING, "rdbLoadLzfStringObject failed allocating %llu bytes", (unsigned long long)len);429 goto err;430 }431432 if (lenptr) *lenptr = len;433434 /* Load the compressed representation and uncompress it to target. */435 if (rioRead(rdb,c,clen) == 0) goto err;436 if (lzf_decompress(c,clen,val,len) != len) {437 rdbReportCorruptRDB("Invalid LZF compressed string");438 goto err;439 }440 zfree(c);441442 return (robjFlag) ? createObject(OBJ_STRING,val) : (void *) val;443444err:445 zfree(c);446 if (plainFlag) {447 zfree(val);448 } else {449 debugServerAssert(sdsFlag || robjFlag);450 sdsfree(val);451 }452 return NULL;453}454455/* Save a string object as [len][data] on disk. If the object is a string456 * representation of an integer value we try to save it in a special form */457ssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {458 int enclen;459 ssize_t n, nwritten = 0;460461 /* Try integer encoding */462 if (len <= 11) {463 unsigned char buf[5];464 if ((enclen = rdbTryIntegerEncoding((char*)s,len,buf)) > 0) {465 if (rdbWriteRaw(rdb,buf,enclen) == -1) return -1;466 return enclen;467 }468 }469470 /* Try LZF compression - under 20 bytes it's unable to compress even471 * aaaaaaaaaaaaaaaaaa so skip it */472 if (server.rdb_compression && len > 20) {473 n = rdbSaveLzfStringObject(rdb,s,len);474 if (n == -1) return -1;475 if (n > 0) return n;476 /* Return value of 0 means data can't be compressed, save the old way */477 }478479 /* Store verbatim */480 if ((n = rdbSaveLen(rdb,len)) == -1) return -1;481 nwritten += n;482 if (len > 0) {483 if (rdbWriteRaw(rdb,s,len) == -1) return -1;484 nwritten += len;485 }486 return nwritten;487}488489/* Save a long long value as either an encoded string or a string. */490ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {491 unsigned char buf[32];492 ssize_t n, nwritten = 0;493 int enclen = rdbEncodeInteger(value,buf);494 if (enclen > 0) {495 return rdbWriteRaw(rdb,buf,enclen);496 } else {497 /* Encode as string */498 enclen = ll2string((char*)buf,32,value);499 serverAssert(enclen < 32);500 if ((n = rdbSaveLen(rdb,enclen)) == -1) return -1;501 nwritten += n;502 if ((n = rdbWriteRaw(rdb,buf,enclen)) == -1) return -1;503 nwritten += n;504 }505 return nwritten;506}507508/* Like rdbSaveRawString() gets a Redis object instead. */509ssize_t rdbSaveStringObject(rio *rdb, robj *obj) {510 /* Avoid to decode the object, then encode it again, if the511 * object is already integer encoded. */512 if (obj->encoding == OBJ_ENCODING_INT) {513 return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);514 } else {515 serverAssertWithInfo(NULL,obj,sdsEncodedObject(obj));516 return rdbSaveRawString(rdb,obj->ptr,sdslen(obj->ptr));517 }518}519520/* Load a string object from an RDB file according to flags:521 *522 * RDB_LOAD_NONE (no flags): load an RDB object, unencoded.523 * RDB_LOAD_ENC: If the returned type is a Redis object, try to524 * encode it in a special way to be more memory525 * efficient. When this flag is passed the function526 * no longer guarantees that obj->ptr is an SDS string.527 * RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc()528 * instead of a Redis object with an sds in it.529 * RDB_LOAD_SDS: Return an SDS string instead of a Redis object.530 *531 * On I/O error NULL is returned.532 */533void *rdbGenericLoadStringObjectUsable(rio *rdb, int flags, size_t *lenptr, size_t *usable) {534 void *buf;535 int plainFlag = flags & RDB_LOAD_PLAIN;536 int sdsFlag = flags & RDB_LOAD_SDS;537 int robjFlag = !(plainFlag || sdsFlag); /* not plain/sds */538539 int isencoded;540 unsigned long long len;541542 len = rdbLoadLen(rdb,&isencoded);543 if (len == RDB_LENERR) return NULL;544545 if (isencoded) {546 switch(len) {547 case RDB_ENC_INT8:548 case RDB_ENC_INT16:549 case RDB_ENC_INT32:550 return rdbLoadIntegerObject(rdb,len,flags,lenptr,usable);551 case RDB_ENC_LZF:552 return rdbLoadLzfStringObject(rdb,flags,lenptr,usable);553 default:554 rdbReportCorruptRDB("Unknown RDB string encoding type %llu",len);555 return NULL;556 }557 }558559 /* return robj */560 if (robjFlag) {561 if (usable) *usable = 0;562 robj *o = tryCreateStringObject(SDS_NOINIT,len);563 if (!o) {564 serverLog(isRestoreContext()? LL_VERBOSE: LL_WARNING, "rdbGenericLoadStringObject failed allocating %llu bytes", len);565 return NULL;566 }567 if (len && rioRead(rdb,o->ptr,len) == 0) {568 decrRefCount(o);569 return NULL;570 }571 return o;572 }573574 /* plain/sds */575 if (plainFlag) {576 buf = ztrymalloc_usable(len, usable);577 } else {578 debugServerAssert(sdsFlag);579 buf = sdstrynewlen(SDS_NOINIT,len);580 if (usable) *usable = sdsAllocSize(buf);581 } 582 583 if (!buf) {584 serverLog(isRestoreContext()? LL_VERBOSE: LL_WARNING, "rdbGenericLoadStringObject failed allocating %llu bytes", len);585 return NULL;586 }587588 if (lenptr) *lenptr = len;589 if (len && rioRead(rdb,buf,len) == 0) {590 if (plainFlag)591 zfree(buf);592 else593 sdsfree(buf);594 return NULL;595 }596 return buf;597}598599void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) {600 return rdbGenericLoadStringObjectUsable(rdb,flags,lenptr,NULL);601}602603robj *rdbLoadStringObject(rio *rdb) {604 return rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE,NULL);605}606607robj *rdbLoadEncodedStringObject(rio *rdb) {608 return rdbGenericLoadStringObject(rdb,RDB_LOAD_ENC,NULL);609}610611/* Save a double value. Doubles are saved as strings prefixed by an unsigned612 * 8 bit integer specifying the length of the representation.613 * This 8 bit integer has special values in order to specify the following614 * conditions:615 * 253: not a number616 * 254: + inf617 * 255: - inf618 */619ssize_t rdbSaveDoubleValue(rio *rdb, double val) {620 unsigned char buf[128];621 int len;622623 if (isnan(val)) {624 buf[0] = 253;625 len = 1;626 } else if (!isfinite(val)) {627 len = 1;628 buf[0] = (val < 0) ? 255 : 254;629 } else {630 long long lvalue;631 /* Integer printing function is much faster, check if we can safely use it. */632 if (double2ll(val, &lvalue))633 ll2string((char*)buf+1,sizeof(buf)-1,lvalue);634 else {635 const int dlen = fpconv_dtoa(val, (char*)buf+1);636 buf[dlen+1] = '\0';637 }638 buf[0] = strlen((char*)buf+1);639 len = buf[0]+1;640 }641 return rdbWriteRaw(rdb,buf,len);642}643644/* For information about double serialization check rdbSaveDoubleValue() */645int rdbLoadDoubleValue(rio *rdb, double *val) {646 char buf[256];647 unsigned char len;648649 if (rioRead(rdb,&len,1) == 0) return -1;650 switch(len) {651 case 255: *val = R_NegInf; return 0;652 case 254: *val = R_PosInf; return 0;653 case 253: *val = R_Nan; return 0;654 default:655 if (rioRead(rdb,buf,len) == 0) return -1;656 buf[len] = '\0';657 if (sscanf(buf, "%lg", val)!=1) return -1;658 return 0;659 }660}661662/* Saves a double for RDB 8 or greater, where IE754 binary64 format is assumed.663 * We just make sure the integer is always stored in little endian, otherwise664 * the value is copied verbatim from memory to disk.665 *666 * Return -1 on error, the size of the serialized value on success. */667int rdbSaveBinaryDoubleValue(rio *rdb, double val) {668 memrev64ifbe(&val);669 return rdbWriteRaw(rdb,&val,sizeof(val));670}671672/* Loads a double from RDB 8 or greater. See rdbSaveBinaryDoubleValue() for673 * more info. On error -1 is returned, otherwise 0. */674int rdbLoadBinaryDoubleValue(rio *rdb, double *val) {675 if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;676 memrev64ifbe(val);677 return 0;678}679680/* Like rdbSaveBinaryDoubleValue() but single precision. */681int rdbSaveBinaryFloatValue(rio *rdb, float val) {682 memrev32ifbe(&val);683 return rdbWriteRaw(rdb,&val,sizeof(val));684}685686/* Like rdbLoadBinaryDoubleValue() but single precision. */687int rdbLoadBinaryFloatValue(rio *rdb, float *val) {688 if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;689 memrev32ifbe(val);690 return 0;691}692693/* Save the object type of object "o". */694int rdbSaveObjectType(rio *rdb, robj *o) {695 switch (o->type) {696 case OBJ_STRING:697 return rdbSaveType(rdb,RDB_TYPE_STRING);698 case OBJ_LIST:699 if (o->encoding == OBJ_ENCODING_QUICKLIST || o->encoding == OBJ_ENCODING_LISTPACK)700 return rdbSaveType(rdb, RDB_TYPE_LIST_QUICKLIST_2);701 else702 serverPanic("Unknown list encoding");703 case OBJ_SET:704 if (o->encoding == OBJ_ENCODING_INTSET)705 return rdbSaveType(rdb,RDB_TYPE_SET_INTSET);706 else if (o->encoding == OBJ_ENCODING_HT)707 return rdbSaveType(rdb,RDB_TYPE_SET);708 else if (o->encoding == OBJ_ENCODING_LISTPACK)709 return rdbSaveType(rdb,RDB_TYPE_SET_LISTPACK);710 else711 serverPanic("Unknown set encoding");712 case OBJ_ZSET:713 if (o->encoding == OBJ_ENCODING_LISTPACK)714 return rdbSaveType(rdb,RDB_TYPE_ZSET_LISTPACK);715 else if (o->encoding == OBJ_ENCODING_SKIPLIST)716 return rdbSaveType(rdb,RDB_TYPE_ZSET_2);717 else718 serverPanic("Unknown sorted set encoding");719 case OBJ_HASH:720 if (o->encoding == OBJ_ENCODING_LISTPACK)721 return rdbSaveType(rdb,RDB_TYPE_HASH_LISTPACK);722 else if (o->encoding == OBJ_ENCODING_LISTPACK_EX)723 return rdbSaveType(rdb,RDB_TYPE_HASH_LISTPACK_EX);724 else if (o->encoding == OBJ_ENCODING_HT) {725 if (hashTypeGetMinExpire(o, /*accurate*/ 1) == EB_EXPIRE_TIME_INVALID)726 return rdbSaveType(rdb,RDB_TYPE_HASH);727 else728 return rdbSaveType(rdb,RDB_TYPE_HASH_METADATA);729 } else if (o->encoding == OBJ_ENCODING_TMPL_LP ||730 o->encoding == OBJ_ENCODING_TMPL_ARRAY) {731 /* Template encodings. RDB save: compact "ref" form storing only the732 * template id + values. DUMP/RESTORE: "full" form that includes 733 * field names. */734 if (!rdbIsDumpPayload(rdb)) {735 return rdbSaveType(rdb, o->encoding == OBJ_ENCODING_TMPL_LP ?736 RDB_TYPE_HASH_TMPL_LP_REF :737 RDB_TYPE_HASH_TMPL_ARRAY_REF);738 } else {739 return rdbSaveType(rdb, o->encoding == OBJ_ENCODING_TMPL_LP ?740 RDB_TYPE_HASH_TMPL_LP :741 RDB_TYPE_HASH_TMPL_ARRAY);742 }743 } else744 serverPanic("Unknown hash encoding");745 case OBJ_STREAM:746 return rdbSaveType(rdb,RDB_TYPE_STREAM_LISTPACKS_5);747#ifdef ENABLE_GCRA748 case OBJ_GCRA:749 return rdbSaveType(rdb,RDB_TYPE_GCRA);750#endif751 case OBJ_MODULE:752 return rdbSaveType(rdb,RDB_TYPE_MODULE_2);753 case OBJ_ARRAY:754 return rdbSaveType(rdb,RDB_TYPE_ARRAY);755 default:756 serverPanic("Unknown object type");757 }758 return -1; /* avoid warning */759}760761/* Use rdbLoadType() to load a TYPE in RDB format, but returns -1 if the762 * type is not specifically a valid Object Type. */763int rdbLoadObjectType(rio *rdb) {764 int type;765 if ((type = rdbLoadType(rdb)) == -1) return -1;766 if (!rdbIsObjectType(type)) return -1;767 return type;768}769770/* This helper function serializes a consumer group Pending Entries List (PEL)771 * into the RDB file. The 'nacks' argument tells the function if also persist772 * the information about the not acknowledged message, or if to persist773 * just the IDs: this is useful because for the global consumer group PEL774 * we serialized the NACKs as well, but when serializing the local consumer775 * PELs we just add the ID, that will be resolved inside the global PEL to776 * put a reference to the same structure. */777ssize_t rdbSaveStreamPEL(rio *rdb, rax *pel, int nacks) {778 ssize_t n, nwritten = 0;779780 /* Number of entries in the PEL. */781 if ((n = rdbSaveLen(rdb,raxSize(pel))) == -1) return -1;782 nwritten += n;783784 /* Save each entry. */785 raxIterator ri;786 raxStart(&ri,pel);787 raxSeek(&ri,"^",NULL,0);788 while(raxNext(&ri)) {789 /* We store IDs in raw form as 128 big big endian numbers, like790 * they are inside the radix tree key. */791 if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) {792 raxStop(&ri);793 return -1;794 }795 nwritten += n;796797 if (nacks) {798 streamNACK *nack = ri.data;799 if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1) {800 raxStop(&ri);801 return -1;802 }803 nwritten += n;804 if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) {805 raxStop(&ri);806 return -1;807 }808 nwritten += n;809 /* We don't save the consumer name: we'll save the pending IDs810 * for each consumer in the consumer PEL, and resolve the consumer811 * at loading time. */812 }813 }814 raxStop(&ri);815 return nwritten;816}817818/* Serialize the IDMP entries for a stream into the RDB file.819 * This saves all the idempotent producer tracking entries (IID -> stream ID mappings).820 * Expired entries are filtered out. Producers whose entries all expired are still821 * written with count=0; the load side skips them.822 * Format: num_producers, then for each producer: pid, num_entries, entries... */823ssize_t rdbSaveStreamIdmpEntries(rio *rdb, stream *s) {824 ssize_t n, nwritten = 0;825826 /* Save the number of producers. */827 size_t num_producers = s->idmp_producers ? raxSize(s->idmp_producers) : 0;828 if ((n = rdbSaveLen(rdb,num_producers)) == -1) return -1;829 nwritten += n;830831 if (num_producers == 0) return nwritten;832833 uint64_t expire_time = server.mstime - (s->idmp_duration * 1000);834835 /* Iterate through all producers. */836 raxIterator ri;837 raxStart(&ri, s->idmp_producers);838 raxSeek(&ri, "^", NULL, 0);839 while (raxNext(&ri)) {840 idmpProducer *producer = ri.data;841842 /* Save the producer ID (pid). */843 if ((n = rdbSaveRawString(rdb, ri.key, ri.key_len)) == -1) {844 raxStop(&ri);845 return -1;846 }847 nwritten += n;848849 /* Find the first non-expired entry. The linked list is ordered by850 * timestamp, so all entries after the first valid one are also valid. */851 idmpEntry *first_valid = producer->idmp_head;852 size_t expired = 0;853 while (first_valid && first_valid->id.ms <= expire_time) {854 first_valid = first_valid->next;855 expired++;856 }857858 /* Save the number of entries for this producer. */859 size_t count = dictSize(producer->idmp_dict) - expired;860 if ((n = rdbSaveLen(rdb, count)) == -1) {861 raxStop(&ri);862 return -1;863 }864 nwritten += n;865866 /* Save each non-expired entry in insertion order. */867 idmpEntry *entry = first_valid;868 while (entry != NULL) {869 /* Save the IID string (length + data). */870 if ((n = rdbSaveRawString(rdb,(unsigned char *)entry->iid,entry->iid_len)) == -1) {871 raxStop(&ri);872 return -1;873 }874 nwritten += n;875876 /* Save the associated stream ID. */877 if ((n = rdbSaveLen(rdb,entry->id.ms)) == -1) {878 raxStop(&ri);879 return -1;880 }881 nwritten += n;882 if ((n = rdbSaveLen(rdb,entry->id.seq)) == -1) {883 raxStop(&ri);884 return -1;885 }886 nwritten += n;887888 entry = entry->next;889 }890 }891 raxStop(&ri);892 return nwritten;893}894895/* Load IDMP entries for a stream from the RDB file.896 * This loads all the idempotent producer tracking entries (IID -> stream ID mappings)897 * and inserts them into the stream's idmp_producers rax tree.898 * Format: num_producers, then for each producer: pid, num_entries, entries... */899int rdbLoadStreamIdmpEntries(rio *rdb, stream *s) {900 /* Load the number of producers. */901 uint64_t num_producers = rdbLoadLen(rdb, NULL);902 if (num_producers == RDB_LENERR) {903 return -1;904 }905906 if (num_producers == 0) return 0;907908 uint64_t expire_time = server.mstime - (s->idmp_duration * 1000);909910 /* Create the producers rax tree. */911 s->idmp_producers = raxNewEx(0, &s->alloc_size, 0);912 if (s->idmp_producers == NULL) {913 return -1;914 }915916 /* Track pid across error paths so cleanup can free it. */917 char *pid = NULL;918 size_t pid_len = 0;919920 /* Load each producer. */921 for (uint64_t p = 0; p < num_producers; p++) {922 /* Load the producer ID (pid). */923 pid = rdbGenericLoadStringObject(rdb, RDB_LOAD_SDS, &pid_len);924 if (pid == NULL) goto cleanup;925926 /* Load the number of entries for this producer. */927 uint64_t count = rdbLoadLen(rdb, NULL);928 if (count == RDB_LENERR) goto cleanup;929930 /* Skip producers with 0 entries (written by save when all entries931 * were expired). Just consume the RDB data and move on. */932 if (count == 0) {933 sdsfree(pid);934 pid = NULL;935 continue;936 }937938 /* Create the producer. */939 idmpProducer *producer = idmpProducerCreate(&s->alloc_size);940941 /* Insert producer into rax tree. */942 int inserted = raxTryInsert(s->idmp_producers, (unsigned char *)pid, pid_len, producer, NULL);943 if (!inserted) {944 idmpProducerFree(producer, &s->alloc_size);945 goto cleanup;946 }947948 /* Load each entry for this producer. */949 for (uint64_t i = 0; i < count; i++) {950 /* Load the IID string. */951 size_t iid_len;952 char *iid = rdbGenericLoadStringObject(rdb, RDB_LOAD_SDS, &iid_len);953 if (iid == NULL) goto cleanup;954955 /* Load the associated stream ID. */956 streamID id;957 id.ms = rdbLoadLen(rdb, NULL);958 id.seq = rdbLoadLen(rdb, NULL);959 if (rioGetReadError(rdb)) {960 sdsfree(iid);961 goto cleanup;962 }963964 /* Skip entries that have already expired. */965 if (id.ms <= expire_time) {966 sdsfree(iid);967 continue;968 }969970 /* Create the idmpEntry. */971 idmpEntry *entry = idmpEntryCreate(iid, iid_len, &s->alloc_size);972 sdsfree(iid); /* idmpEntryCreate makes a copy */973974 /* Set the stream ID. */975 entry->id = id;976 entry->next = NULL;977978 /* Insert into dict. If insertion fails (e.g., duplicate), skip. */979 int ret = dictAdd(producer->idmp_dict, entry, NULL);980 if (ret != DICT_OK) {981 /* Insertion failed (duplicate). For RDB loading, we'll just skip982 * duplicates rather than failing the entire load. */983 idmpEntryFree(entry, &s->alloc_size);984 } else {985 /* Add to linked list tail. */986 if (producer->idmp_tail == NULL) {987 producer->idmp_head = producer->idmp_tail = entry;988 } else {989 producer->idmp_tail->next = entry;990 producer->idmp_tail = entry;991 }992 }993 }994995 /* If all entries were expired, remove the empty producer. */996 if (producer->idmp_head == NULL) {997 raxRemove(s->idmp_producers, (unsigned char *)pid, pid_len, NULL);998 idmpProducerFree(producer, &s->alloc_size);999 }1000 sdsfree(pid);1001 pid = NULL;1002 }10031004 /* If no producers remain after filtering, free the rax tree. */1005 if (raxSize(s->idmp_producers) == 0) {1006 raxFree(s->idmp_producers);1007 s->idmp_producers = NULL;1008 }10091010 return 0;10111012cleanup:1013 if (pid) sdsfree(pid);1014 /* Clean up partially constructed producers tree on error.1015 * This prevents use-after-free when the stream is later freed. */1016 if (s->idmp_producers) {1017 raxFreeWithCbAndContext(s->idmp_producers, streamFreeIdmpProducerGeneric, s);1018 s->idmp_producers = NULL;1019 }1020 return -1;1021}10221023/* Serialize the consumers of a stream consumer group into the RDB. Helper1024 * function for the stream data type serialization. What we do here is to1025 * persist the consumer metadata, and it's PEL, for each consumer. */1026size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {1027 ssize_t n, nwritten = 0;10281029 /* Number of consumers in this consumer group. */1030 if ((n = rdbSaveLen(rdb,raxSize(cg->consumers))) == -1) return -1;1031 nwritten += n;10321033 /* Save each consumer. */1034 raxIterator ri;1035 raxStart(&ri,cg->consumers);1036 raxSeek(&ri,"^",NULL,0);1037 while(raxNext(&ri)) {1038 streamConsumer *consumer = ri.data;10391040 /* Consumer name. */1041 if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {1042 raxStop(&ri);1043 return -1;1044 }1045 nwritten += n;10461047 /* Seen time. */1048 if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1) {1049 raxStop(&ri);1050 return -1;1051 }1052 nwritten += n;10531054 /* Active time. */1055 if ((n = rdbSaveMillisecondTime(rdb,consumer->active_time)) == -1) {1056 raxStop(&ri);1057 return -1;1058 }1059 nwritten += n;10601061 /* Consumer PEL, without the ACKs (see last parameter of the function1062 * passed with value of 0), at loading time we'll lookup the ID1063 * in the consumer group global PEL and will put a reference in the1064 * consumer local PEL. */1065 if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1) {1066 raxStop(&ri);1067 return -1;1068 }1069 nwritten += n;1070 }1071 raxStop(&ri);1072 return nwritten;1073}10741075/* Save a Redis object.1076 * Returns -1 on error, number of bytes written on success. */1077static ssize_t rdbSaveArrayElement(rio *rdb, uint64_t idx, void *v) {1078 ssize_t n, nwritten = 0;10791080 if ((n = rdbSaveLen(rdb, idx)) == -1) return -1;1081 nwritten += n;10821083 if (arIsInt(v)) {1084 if ((n = rdbSaveLen(rdb, AR_RDB_TAG_INT)) == -1) return -1;1085 nwritten += n;1086 int64_t ival = arToInt(v);1087 if ((n = rdbSaveSignedInteger(rdb, ival)) == -1) return -1;1088 nwritten += n;1089 } else if (arIsFloat(v)) {1090 if ((n = rdbSaveLen(rdb, AR_RDB_TAG_FLOAT)) == -1) return -1;1091 nwritten += n;1092 double d = arToDouble(v);1093 if (rdbSaveBinaryDoubleValue(rdb, d) == -1) return -1;1094 nwritten += 8;1095 } else if (arIsSmallStr(v)) {1096 char buf[AR_SMALLSTR_MAXLEN + 1];1097 int len = arToSmallStr(v, buf);1098 if ((n = rdbSaveLen(rdb, AR_RDB_TAG_SMALLSTR)) == -1) return -1;1099 nwritten += n;1100 if ((n = rdbSaveRawString(rdb, (unsigned char *)buf, len)) == -1) return -1;1101 nwritten += n;1102 } else {1103 if ((n = rdbSaveLen(rdb, AR_RDB_TAG_SDS)) == -1) return -1;1104 nwritten += n;1105 if ((n = rdbSaveRawString(rdb, (unsigned char *)arStringData(v), arStringLen(v))) == -1) return -1;1106 nwritten += n;1107 }11081109 return nwritten;1110}11111112static ssize_t rdbSaveArraySlice(rio *rdb, arSlice *s, uint64_t slice_id,1113 uint32_t slice_size) {1114 ssize_t n, nwritten = 0;11151116 if (s->encoding == AR_SLICE_DENSE) {1117 for (uint32_t i = 0; i < s->layout.dense.winsize; i++) {1118 void *v = s->layout.dense.items[i];1119 if (arIsEmpty(v)) continue;11201121 uint64_t idx = arMakeIdx(slice_id, s->layout.dense.offset + i, slice_size);1122 if ((n = rdbSaveArrayElement(rdb, idx, v)) == -1) return -1;1123 nwritten += n;1124 }1125 } else {1126 uint16_t *offsets = s->layout.sparse.offsets;1127 void **values = s->layout.sparse.values;11281129 for (uint32_t i = 0; i < s->count; i++) {1130 uint64_t idx = arMakeIdx(slice_id, offsets[i], slice_size);1131 if ((n = rdbSaveArrayElement(rdb, idx, values[i])) == -1) return -1;1132 nwritten += n;1133 }1134 }11351136 return nwritten;1137}11381139ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key, int dbid) {1140 ssize_t n = 0, nwritten = 0;11411142 if (o->type == OBJ_STRING) {1143 /* Save a string value */1144 if ((n = rdbSaveStringObject(rdb,o)) == -1) return -1;1145 nwritten += n;1146 } else if (o->type == OBJ_LIST) {1147 /* Save a list value */1148 if (o->encoding == OBJ_ENCODING_QUICKLIST) {1149 quicklist *ql = o->ptr;1150 quicklistNode *node = ql->head;11511152 if ((n = rdbSaveLen(rdb,ql->len)) == -1) return -1;1153 nwritten += n;11541155 while(node) {1156 if ((n = rdbSaveLen(rdb,node->container)) == -1) return -1;1157 nwritten += n;11581159 if (quicklistNodeIsCompressed(node)) {1160 void *data;1161 size_t compress_len = quicklistGetLzf(node, &data);1162 if ((n = rdbSaveLzfBlob(rdb,data,compress_len,node->sz)) == -1) return -1;1163 nwritten += n;1164 } else {1165 if ((n = rdbSaveRawString(rdb,node->entry,node->sz)) == -1) return -1;1166 nwritten += n;1167 }1168 node = node->next;1169 }1170 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {1171 unsigned char *lp = o->ptr;11721173 /* Save list listpack as a fake quicklist that only has a single node. */1174 if ((n = rdbSaveLen(rdb,1)) == -1) return -1;1175 nwritten += n;1176 if ((n = rdbSaveLen(rdb,QUICKLIST_NODE_CONTAINER_PACKED)) == -1) return -1;1177 nwritten += n;1178 if ((n = rdbSaveRawString(rdb,lp,lpBytes(lp))) == -1) return -1;1179 nwritten += n;1180 } else {1181 serverPanic("Unknown list encoding");1182 }1183 } else if (o->type == OBJ_SET) {1184 /* Save a set value */1185 if (o->encoding == OBJ_ENCODING_HT) {1186 dict *set = o->ptr;1187 dictIterator di;1188 dictEntry *de;11891190 if ((n = rdbSaveLen(rdb,dictSize(set))) == -1) {1191 return -1;1192 }1193 nwritten += n;11941195 dictInitIterator(&di, set);1196 while((de = dictNext(&di)) != NULL) {1197 sds ele = dictGetKey(de);1198 if ((n = rdbSaveRawString(rdb,(unsigned char*)ele,sdslen(ele)))1199 == -1)1200 {1201 dictResetIterator(&di);1202 return -1;1203 }1204 nwritten += n;1205 }1206 dictResetIterator(&di);1207 } else if (o->encoding == OBJ_ENCODING_INTSET) {1208 size_t l = intsetBlobLen((intset*)o->ptr);12091210 if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;1211 nwritten += n;1212 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {1213 size_t l = lpBytes((unsigned char *)o->ptr);1214 if ((n = rdbSaveRawString(rdb, o->ptr, l)) == -1) return -1;1215 nwritten += n;1216 } else {1217 serverPanic("Unknown set encoding");1218 }1219 } else if (o->type == OBJ_ZSET) {1220 /* Save a sorted set value */1221 if (o->encoding == OBJ_ENCODING_LISTPACK) {1222 size_t l = lpBytes((unsigned char*)o->ptr);12231224 if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;1225 nwritten += n;1226 } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {1227 zset *zs = o->ptr;1228 zskiplist *zsl = zs->zsl;12291230 if ((n = rdbSaveLen(rdb,zsl->length)) == -1) return -1;1231 nwritten += n;12321233 /* We save the skiplist elements from the greatest to the smallest1234 * (that's trivial since the elements are already ordered in the1235 * skiplist): this improves the load process, since the next loaded1236 * element will always be the smaller, so adding to the skiplist1237 * will always immediately stop at the head, making the insertion1238 * O(1) instead of O(log(N)). */1239 zskiplistNode *zn = zsl->tail;1240 while (zn != NULL) {1241 sds ele = zslGetNodeElement(zn);1242 if ((n = rdbSaveRawString(rdb,1243 (unsigned char*)ele,sdslen(ele))) == -1)1244 {1245 return -1;1246 }1247 nwritten += n;1248 if ((n = rdbSaveBinaryDoubleValue(rdb,zn->score)) == -1)1249 return -1;1250 nwritten += n;1251 zn = zn->backward;1252 }1253 } else {1254 serverPanic("Unknown sorted set encoding");1255 }1256 } else if (o->type == OBJ_HASH) {1257 /* Save a hash value */1258 if (o->encoding == OBJ_ENCODING_TMPL_LP ||1259 o->encoding == OBJ_ENCODING_TMPL_ARRAY) {12601261 hashTemplate *tmpl = hashTypeGetTemplate(o);1262 unsigned long long field_count = tmpl->field_count;12631264 /* If this is a BGSAVE, we can use the compact ref format.1265 * DUMP payload is self-contained (field names included). */1266 if (!rdbIsDumpPayload(rdb)) {1267 if (o->encoding == OBJ_ENCODING_TMPL_LP) {1268 /* TMPL_LP compact: raw listpack blob. The template ID is1269 * the first listpack entry, so no separate field needed. */1270 unsigned char *lp = o->ptr;1271 if ((n = rdbSaveRawString(rdb, lp, lpBytes(lp))) == -1)1272 return -1;1273 nwritten += n;1274 } else {1275 /* TMPL_ARRAY compact: [template_id][value1][value2]... */1276 if ((n = rdbSaveLen(rdb, tmpl->id)) == -1) return -1;1277 nwritten += n;1278 hashTemplateArray *hta = o->ptr;1279 for (unsigned long long i = 0; i < field_count; i++) {1280 sds value = hta->values[i];1281 if ((n = rdbSaveRawString(rdb, (unsigned char *)value, sdslen(value))) == -1)1282 return -1;1283 nwritten += n;1284 }1285 }1286 } else {1287 /* Self-contained format (DUMP/RESTORE).1288 * Layout: [fields_fmt][field names][values]1289 *1290 * fields_fmt (1 len-encoded byte) — how field names are stored:1291 * 0 = FIELDS_LP : field names as one listpack blob. Lets the 1292 * destination resolve the template with one O(1) blob 1293 * lookup. Used when tmpl->fits_in_listpack.1294 * 1 = FIELDS_RAW: [count] then count field-name strings. Used1295 * when the field names don't fit a listpack.1296 *1297 * Values follow per the RDB type:1298 * TMPL_LP : values are one listpack blob1299 * TMPL_ARRAY : field_count value strings1300 *1301 * The two are independent: fits_in_listpack picks the field format,1302 * the value encoding picks the value format (e.g. ARRAY+FIELDS_LP1303 * is valid: huge values but lp-able field names). */1304 int fields_lp = tmpl->fits_in_listpack;1305 if ((n = rdbSaveLen(rdb, fields_lp ? 0 : 1)) == -1) return -1;1306 nwritten += n;13071308 /* Field names. */1309 if (fields_lp) {1310 /* Get listpack blob and skip caching in fork. */1311 int cache = (server.in_fork_child == CHILD_TYPE_NONE);1312 unsigned char *blob = hashTemplateGetFieldsLp(tmpl, &cache);1313 n = rdbSaveRawString(rdb, blob, lpBytes(blob));1314 if (!cache) lpFree(blob);1315 if (n == -1) return -1;1316 nwritten += n;1317 } else {1318 if ((n = rdbSaveLen(rdb, field_count)) == -1) return -1;1319 nwritten += n;1320 for (unsigned long long i = 0; i < field_count; i++) {1321 sds field = tmpl->fields[i];1322 if ((n = rdbSaveRawString(rdb, (unsigned char *)field, sdslen(field))) == -1)1323 return -1;1324 nwritten += n;1325 }1326 }13271328 /* Values. */1329 if (o->encoding == OBJ_ENCODING_TMPL_LP) {1330 unsigned char *lp = o->ptr;1331 if ((n = rdbSaveRawString(rdb, lp, lpBytes(lp))) == -1) return -1;1332 nwritten += n;1333 } else {1334 hashTemplateArray *hta = o->ptr;1335 for (unsigned long long i = 0; i < field_count; i++) {1336 sds value = hta->values[i];1337 if ((n = rdbSaveRawString(rdb, (unsigned char *)value, sdslen(value))) == -1)1338 return -1;1339 nwritten += n;1340 }1341 }1342 }1343 } else if ((o->encoding == OBJ_ENCODING_LISTPACK) ||1344 (o->encoding == OBJ_ENCODING_LISTPACK_EX))1345 {1346 /* Save min/next HFE expiration time if needed */1347 if (o->encoding == OBJ_ENCODING_LISTPACK_EX) {1348 uint64_t minExpire = hashTypeGetMinExpire(o, 0);1349 /* if invalid time then save 0 */1350 if (minExpire == EB_EXPIRE_TIME_INVALID)1351 minExpire = 0;1352 if (rdbSaveMillisecondTime(rdb, minExpire) == -1)1353 return -1;1354 }1355 unsigned char *lp_ptr = hashTypeListpackGetLp(o);1356 size_t l = lpBytes(lp_ptr);13571358 if ((n = rdbSaveRawString(rdb,lp_ptr,l)) == -1) return -1;1359 nwritten += n;1360 } else if (o->encoding == OBJ_ENCODING_HT) {1361 int hashWithMeta = 0; /* RDB_TYPE_HASH_METADATA */1362 dictIterator di;1363 dictEntry *de;1364 /* Determine the hash layout to use based on the presence of at least1365 * one field with a valid TTL. If such a field exists, employ the1366 * RDB_TYPE_HASH_METADATA layout, including tuples of [ttl][field][value].1367 * Otherwise, use the standard RDB_TYPE_HASH layout containing only1368 * the tuples [field][value]. */1369 uint64_t minExpire = hashTypeGetMinExpire(o, 1);13701371 /* if RDB_TYPE_HASH_METADATA (Can have TTLs on fields) */1372 if (minExpire != EB_EXPIRE_TIME_INVALID) {1373 hashWithMeta = 1;1374 /* Save next field expire time of hash */1375 if (rdbSaveMillisecondTime(rdb, minExpire) == -1) {1376 return -1;1377 }1378 }13791380 /* save number of fields in hash */1381 if ((n = rdbSaveLen(rdb,dictSize((dict*)o->ptr))) == -1) {1382 return -1;1383 }1384 nwritten += n;13851386 /* save all hash fields */1387 dictInitIterator(&di, o->ptr);1388 while((de = dictNext(&di)) != NULL) {1389 Entry *entry = dictGetKey(de);1390 sds field = entryGetField(entry);1391 sds value = entryGetValue(entry);13921393 /* save the TTL */1394 if (hashWithMeta) {1395 uint64_t ttl, expiryTime= entryGetExpiry(entry);13961397 /* Saved TTL value:1398 * - 0: Indicates no TTL. This is common case so we keep it small.1399 * - Otherwise: TTL is relative to minExpire (with +1 to avoid 0 that already taken)1400 */1401 ttl = (expiryTime == EB_EXPIRE_TIME_INVALID) ? 0 : expiryTime - minExpire + 1;1402 if ((n = rdbSaveLen(rdb, ttl)) == -1) {1403 dictResetIterator(&di);1404 return -1;1405 }1406 nwritten += n;1407 }14081409 /* save the key */1410 if ((n = rdbSaveRawString(rdb,(unsigned char*)field,1411 sdslen(field))) == -1)1412 {1413 dictResetIterator(&di);1414 return -1;1415 }1416 nwritten += n;14171418 /* save the value */1419 if ((n = rdbSaveRawString(rdb,(unsigned char*)value,1420 sdslen(value))) == -1)1421 {1422 dictResetIterator(&di);1423 return -1;1424 }1425 nwritten += n;1426 }1427 dictResetIterator(&di);1428 } else {1429 serverPanic("Unknown hash encoding");1430 }1431 } else if (o->type == OBJ_STREAM) {1432 /* Store how many listpacks we have inside the radix tree. */1433 stream *s = o->ptr;1434 rax *rax = s->rax;1435 if ((n = rdbSaveLen(rdb,raxSize(rax))) == -1) return -1;1436 nwritten += n;14371438 /* Serialize all the listpacks inside the radix tree as they are,1439 * when loading back, we'll use the first entry of each listpack1440 * to insert it back into the radix tree. */1441 raxIterator ri;1442 raxStart(&ri,rax);1443 raxSeek(&ri,"^",NULL,0);1444 while (raxNext(&ri)) {1445 unsigned char *lp = ri.data;1446 size_t lp_bytes = lpBytes(lp);1447 if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {1448 raxStop(&ri);1449 return -1;1450 }1451 nwritten += n;1452 if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) {1453 raxStop(&ri);1454 return -1;1455 }1456 nwritten += n;1457 }1458 raxStop(&ri);14591460 /* Save the number of elements inside the stream. We cannot obtain1461 * this easily later, since our macro nodes should be checked for1462 * number of items: not a great CPU / space tradeoff. */1463 if ((n = rdbSaveLen(rdb,s->length)) == -1) return -1;1464 nwritten += n;1465 /* Save the last entry ID. */1466 if ((n = rdbSaveLen(rdb,s->last_id.ms)) == -1) return -1;1467 nwritten += n;1468 if ((n = rdbSaveLen(rdb,s->last_id.seq)) == -1) return -1;1469 nwritten += n;1470 /* Save the first entry ID. */1471 if ((n = rdbSaveLen(rdb,s->first_id.ms)) == -1) return -1;1472 nwritten += n;1473 if ((n = rdbSaveLen(rdb,s->first_id.seq)) == -1) return -1;1474 nwritten += n;1475 /* Save the maximal tombstone ID. */1476 if ((n = rdbSaveLen(rdb,s->max_deleted_entry_id.ms)) == -1) return -1;1477 nwritten += n;1478 if ((n = rdbSaveLen(rdb,s->max_deleted_entry_id.seq)) == -1) return -1;1479 nwritten += n;1480 /* Save the offset. */1481 if ((n = rdbSaveLen(rdb,s->entries_added)) == -1) return -1;1482 nwritten += n;14831484 /* The consumer groups and their clients are part of the stream1485 * type, so serialize every consumer group. */14861487 /* Save the number of groups. */1488 size_t num_cgroups = s->cgroups ? raxSize(s->cgroups) : 0;1489 if ((n = rdbSaveLen(rdb,num_cgroups)) == -1) return -1;1490 nwritten += n;14911492 if (num_cgroups) {1493 /* Serialize each consumer group. */1494 raxStart(&ri,s->cgroups);1495 raxSeek(&ri,"^",NULL,0);1496 while(raxNext(&ri)) {1497 streamCG *cg = ri.data;14981499 /* Save the group name. */1500 if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {1501 raxStop(&ri);1502 return -1;1503 }1504 nwritten += n;15051506 /* Last ID. */1507 if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) {1508 raxStop(&ri);1509 return -1;1510 }1511 nwritten += n;1512 if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) {1513 raxStop(&ri);1514 return -1;1515 }1516 nwritten += n;1517 1518 /* Save the group's logical reads counter. */1519 if ((n = rdbSaveLen(rdb,cg->entries_read)) == -1) {1520 raxStop(&ri);1521 return -1;1522 }1523 nwritten += n;15241525 /* Save the global PEL. */1526 if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) {1527 raxStop(&ri);1528 return -1;1529 }1530 nwritten += n;15311532 /* Save the consumers of this group. */1533 if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) {1534 raxStop(&ri);1535 return -1;1536 }1537 nwritten += n;15381539 /* Save NACK zone: count followed by the IDs of NACKed entries. */1540 uint64_t nacked_count = pelListNackedCount(cg);1541 if ((n = rdbSaveLen(rdb, nacked_count)) == -1) {1542 raxStop(&ri);1543 return -1;1544 }1545 nwritten += n;15461547 if (cg->pel_nack_tail) {1548 streamNACK *nack = cg->pel_time_head;1549 while (nack) {1550 unsigned char buf[sizeof(streamID)];1551 streamEncodeID(buf, &nack->id);1552 if ((n = rdbWriteRaw(rdb, buf, sizeof(buf))) == -1) {1553 raxStop(&ri);1554 return -1;1555 }1556 nwritten += n;1557 if (nack == cg->pel_nack_tail) break;1558 nack = nack->pel_next;1559 }1560 }1561 }1562 raxStop(&ri);1563 }15641565 /* Save IDMP (Idempotent Message Producer) configuration and entries. */1566 1567 /* Save IDMP duration (in seconds). */1568 if ((n = rdbSaveLen(rdb,s->idmp_duration)) == -1) return -1;1569 nwritten += n;1570 1571 /* Save IDMP max entries. */1572 if ((n = rdbSaveLen(rdb,s->idmp_max_entries)) == -1) return -1;1573 nwritten += n;1574 1575 /* Save all IDMP entries. */1576 if ((n = rdbSaveStreamIdmpEntries(rdb,s)) == -1) return -1;1577 nwritten += n;15781579 /* Save the all-time count of IIDs added. */1580 if ((n = rdbSaveLen(rdb,s->iids_added)) == -1) return -1;1581 nwritten += n;15821583 /* Save the all-time count of duplicate IIDs detected. */1584 if ((n = rdbSaveLen(rdb,s->iids_duplicates)) == -1) return -1;1585 nwritten += n;1586#ifdef ENABLE_GCRA1587 } else if (o->type == OBJ_GCRA) {1588 long long t;1589 getLongLongFromGCRAObject(o, &t);1590 if ((n = rdbSaveLen(rdb,t)) == -1) return -1;1591 nwritten += n;1592#endif1593 } else if (o->type == OBJ_MODULE) {1594 /* Save a module-specific value. */1595 RedisModuleIO io;1596 moduleValue *mv = o->ptr;1597 moduleType *mt = mv->type;15981599 /* Write the "module" identifier as prefix, so that we'll be able1600 * to call the right module during loading. */1601 int retval = rdbSaveLen(rdb,mt->entity.id);1602 if (retval == -1) return -1;1603 moduleInitIOContext(&io, &mt->entity, rdb, key, dbid);1604 io.bytes += retval;16051606 /* Then write the module-specific representation + EOF marker. */1607 mt->rdb_save(&io,mv->value);1608 retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);1609 if (retval == -1)1610 io.error = 1;1611 else1612 io.bytes += retval;16131614 if (io.ctx) {1615 moduleFreeContext(io.ctx);1616 zfree(io.ctx);1617 }1618 return io.error ? -1 : (ssize_t)io.bytes;1619 } else if (o->type == OBJ_ARRAY) {1620 /* Save an array value. We persist only elements and insert_idx - no1621 * implementation details like slice_size. Arrays are loaded using1622 * the current ar_slice_size config. */1623 redisArray *ar = o->ptr;16241625 /* Save count */1626 if ((n = rdbSaveLen(rdb, ar->count)) == -1) return -1;1627 nwritten += n;16281629 /* Save insert_idx: 0 = none, 1 = has value followed by actual value.1630 * We can't save UINT64_MAX directly with rdbSaveLen/rdbLoadLen because1631 * rdbLoadLen returns UINT64_MAX (RDB_LENERR) to signal an error, making1632 * it impossible to distinguish a valid UINT64_MAX value from an error. */1633 if (ar->insert_idx == AR_INSERT_IDX_NONE) {1634 if ((n = rdbSaveLen(rdb, 0)) == -1) return -1;1635 nwritten += n;1636 } else {1637 if ((n = rdbSaveLen(rdb, 1)) == -1) return -1;1638 nwritten += n;1639 if ((n = rdbSaveLen(rdb, ar->insert_idx)) == -1) return -1;1640 nwritten += n;1641 }16421643 /* Save elements in index order.1644 * We need to iterate through all slices, handling both flat directory1645 * mode and superdir mode. In superdir mode, blocks are sorted by1646 * block_id, so we iterate through blocks in order. */1647 if (ar->superdir) {1648 /* Superdir mode: iterate through blocks */1649 for (uint32_t bi = 0; bi < ar->sdir_len; bi++) {1650 arSDirEntry *e = ar->superdir + bi;1651 uint64_t block_base = e->block_id * AR_SUPER_BLOCK_SLOTS;16521653 for (uint32_t si = 0; si < AR_SUPER_BLOCK_SLOTS; si++) {1654 arSlice *s = e->slots[si];1655 if (!s) continue;1656 uint64_t slice_id = block_base + si;1657 if ((n = rdbSaveArraySlice(rdb, s, slice_id, ar->slice_size)) == -1) return -1;1658 nwritten += n;1659 }1660 }1661 } else {1662 /* Flat directory mode */1663 for (uint64_t slice_id = 0; slice_id <= ar->dir_highest_used && slice_id < ar->dir_alloc; slice_id++) {1664 arSlice *s = ar->dir[slice_id];1665 if (!s) continue;1666 if ((n = rdbSaveArraySlice(rdb, s, slice_id, ar->slice_size)) == -1) return -1;1667 nwritten += n;1668 }1669 }1670 } else {1671 serverPanic("Unknown object type");1672 }1673 return nwritten;1674}16751676/* Return the length the object will have on disk if saved with1677 * the rdbSaveObject() function. Currently we use a trick to get1678 * this length with very little changes to the code. In the future1679 * we could switch to a faster solution. */1680size_t rdbSavedObjectLen(robj *o, robj *key, int dbid) {1681 ssize_t len = rdbSaveObject(NULL,o,key,dbid);1682 serverAssertWithInfo(NULL,o,len != -1);1683 return len;1684}16851686/* Save a key-value pair, with expire time, type, key, value.1687 * On error -1 is returned.1688 * On success if the key was actually saved 1 is returned. */1689int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime, int dbid) {1690 int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU;1691 int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU;16921693 /* Save the expire time */1694 if (expiretime != -1) {1695 if (rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;1696 if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;1697 }16981699 /* Save the LRU info. */1700 if (savelru) {1701 uint64_t idletime = estimateObjectIdleTime(val);1702 idletime /= 1000; /* Using seconds is enough and requires less space.*/1703 if (rdbSaveType(rdb,RDB_OPCODE_IDLE) == -1) return -1;1704 if (rdbSaveLen(rdb,idletime) == -1) return -1;1705 }17061707 /* Save the LFU info. */1708 if (savelfu) {1709 uint8_t buf[1];1710 buf[0] = LFUDecrAndReturn(val);1711 /* We can encode this in exactly two bytes: the opcode and an 81712 * bit counter, since the frequency is logarithmic with a 0-255 range.1713 * Note that we do not store the halving time because to reset it1714 * a single time when loading does not affect the frequency much. */1715 if (rdbSaveType(rdb,RDB_OPCODE_FREQ) == -1) return -1;1716 if (rdbWriteRaw(rdb,buf,1) == -1) return -1;1717 }17181719 /* if needed save key metadata */1720 if (getModuleMetaBits(val->metabits)) {1721 if (rdbSaveKeyMetadata(rdb, key, val, dbid) == -1)1722 return -1;1723 }17241725 /* Save type, key, value */1726 if (rdbSaveObjectType(rdb,val) == -1) return -1;1727 if (rdbSaveStringObject(rdb,key) == -1) return -1;1728 if (rdbSaveObject(rdb,val,key,dbid) == -1) return -1;17291730 /* Delay return if required (for testing) */1731 if (server.rdb_key_save_delay)1732 debugDelay(server.rdb_key_save_delay);17331734 return 1;1735}17361737/* Save an AUX field. */1738ssize_t rdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {1739 ssize_t ret, len = 0;1740 if ((ret = rdbSaveType(rdb,RDB_OPCODE_AUX)) == -1) return -1;1741 len += ret;1742 if ((ret = rdbSaveRawString(rdb,key,keylen)) == -1) return -1;1743 len += ret;1744 if ((ret = rdbSaveRawString(rdb,val,vallen)) == -1) return -1;1745 len += ret;1746 return len;1747}17481749/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained1750 * with strlen(). */1751ssize_t rdbSaveAuxFieldStrStr(rio *rdb, char *key, char *val) {1752 return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));1753}17541755/* Wrapper for strlen(key) + integer type (up to long long range). */1756ssize_t rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {1757 char buf[LONG_STR_SIZE];1758 int vlen = ll2string(buf,sizeof(buf),val);1759 return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);1760}17611762/* Save a few default AUX fields with information about the RDB generated. */1763int rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {1764 int redis_bits = (sizeof(void*) == 8) ? 64 : 32;1765 int aof_base = (rdbflags & RDBFLAGS_AOF_PREAMBLE) != 0;17661767 /* Add a few fields about the state when the RDB was created. */1768 if (rdbSaveAuxFieldStrStr(rdb,"redis-ver",REDIS_VERSION) == -1) return -1;1769 if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1;1770 if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1;1771 if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1;17721773 /* Handle saving options that generate aux fields. */1774 if (rsi) {1775 if (rdbSaveAuxFieldStrInt(rdb,"repl-stream-db",rsi->repl_stream_db)1776 == -1) return -1;1777 if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid)1778 == -1) return -1;1779 if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset)1780 == -1) return -1;1781 }1782 if (rdbSaveAuxFieldStrInt(rdb, "aof-base", aof_base) == -1) return -1;17831784 /* Save the active import ASM task if cluster is enabled. */1785 if (server.cluster_enabled) {1786 sds task_info = asmDumpActiveImportTask();1787 int ret = rdbSaveAuxFieldStrStr(rdb, "cluster-asm-task",1788 task_info ? task_info : "");1789 if (task_info) sdsfree(task_info);1790 if (ret == -1) return -1;1791 }17921793 return 1;1794}17951796ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {1797 /* Save a module-specific aux value. */1798 RedisModuleIO io;1799 int retval = 0;1800 moduleInitIOContext(&io, &mt->entity, rdb, NULL, -1);18011802 /* We save the AUX field header in a temporary buffer so we can support aux_save2 API.1803 * If aux_save2 is used the buffer will be flushed at the first time the module will perform1804 * a write operation to the RDB and will be ignored is case there was no writes. */1805 rio aux_save_headers_rio;1806 rioInitWithBuffer(&aux_save_headers_rio, sdsempty());18071808 if (rdbSaveType(&aux_save_headers_rio, RDB_OPCODE_MODULE_AUX) == -1) goto error;18091810 /* Write the "module" identifier as prefix, so that we'll be able1811 * to call the right module during loading. */1812 if (rdbSaveLen(&aux_save_headers_rio,mt->entity.id) == -1) goto error;18131814 /* write the 'when' so that we can provide it on loading. add a UINT opcode1815 * for backwards compatibility, everything after the MT needs to be prefixed1816 * by an opcode. */1817 if (rdbSaveLen(&aux_save_headers_rio,RDB_MODULE_OPCODE_UINT) == -1) goto error;1818 if (rdbSaveLen(&aux_save_headers_rio,when) == -1) goto error;18191820 /* Then write the module-specific representation + EOF marker. */1821 if (mt->aux_save2) {1822 io.pre_flush_buffer = aux_save_headers_rio.io.buffer.ptr;1823 mt->aux_save2(&io,when);1824 if (io.pre_flush_buffer) {1825 /* aux_save did not save any data to the RDB.1826 * We will avoid saving any data related to this aux type1827 * to allow loading this RDB if the module is not present. */1828 sdsfree(io.pre_flush_buffer);1829 io.pre_flush_buffer = NULL;1830 return 0;1831 }1832 } else {1833 /* Write headers now, aux_save does not do lazy saving of the headers. */1834 retval = rdbWriteRaw(rdb, aux_save_headers_rio.io.buffer.ptr, sdslen(aux_save_headers_rio.io.buffer.ptr));1835 if (retval == -1) goto error;1836 io.bytes += retval;1837 sdsfree(aux_save_headers_rio.io.buffer.ptr);1838 mt->aux_save(&io,when);1839 }1840 retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);1841 serverAssert(!io.pre_flush_buffer);1842 if (retval == -1)1843 io.error = 1;1844 else1845 io.bytes += retval;18461847 if (io.ctx) {1848 moduleFreeContext(io.ctx);1849 zfree(io.ctx);1850 }1851 if (io.error)1852 return -1;1853 return io.bytes;1854error:1855 sdsfree(aux_save_headers_rio.io.buffer.ptr);1856 return -1;1857}18581859ssize_t rdbSaveFunctions(rio *rdb) {1860 dict *functions = functionsLibGet();1861 dictIterator iter;1862 dictEntry *entry = NULL;1863 ssize_t written = 0;1864 ssize_t ret;18651866 dictInitIterator(&iter, functions);1867 while ((entry = dictNext(&iter))) {1868 if ((ret = rdbSaveType(rdb, RDB_OPCODE_FUNCTION2)) < 0) goto werr;1869 written += ret;1870 functionLibInfo *li = dictGetVal(entry);1871 if ((ret = rdbSaveRawString(rdb, (unsigned char *) li->code, sdslen(li->code))) < 0) goto werr;1872 written += ret;1873 }1874 dictResetIterator(&iter);1875 return written;18761877werr:1878 dictResetIterator(&iter);1879 return -1;1880}18811882/* RDB save path (not DUMP): Save the hash template registry (id -> field names)1883 * so REF-encoded hashes, which store only [id][values...], can resolve their 1884 * fields at load time.1885 *1886 * Each template is written as its own RDB_OPCODE_HASH_TEMPLATE record; the1887 * loader reads them until the next opcode differs.1888 * Record: [OPCODE][id][field_count][field1][field2]... */1889ssize_t rdbSaveHashTemplates(rio *rdb) {1890 ssize_t written = 0;1891 ssize_t ret;18921893 if (!server.htemplates || !server.htemplates->by_fields) return 0;18941895 dictIterator *di = dictGetIterator(server.htemplates->by_fields);1896 dictEntry *de;1897 while ((de = dictNext(di)) != NULL) {1898 hashTemplate *tmpl = dictGetKey(de);1899 if (tmpl->key_refcount == 0) continue;19001901 if ((ret = rdbSaveType(rdb, RDB_OPCODE_HASH_TEMPLATE)) < 0) goto werr;1902 written += ret;1903 if ((ret = rdbSaveLen(rdb, tmpl->id)) < 0) goto werr;1904 written += ret;1905 if ((ret = rdbSaveLen(rdb, tmpl->field_count)) < 0) goto werr;1906 written += ret;1907 for (unsigned long long i = 0; i < tmpl->field_count; i++) {1908 sds field = tmpl->fields[i];1909 if ((ret = rdbSaveRawString(rdb, (unsigned char *)field, sdslen(field))) < 0)1910 goto werr;1911 written += ret;1912 }1913 }1914 dictReleaseIterator(di);1915 return written;19161917werr:1918 dictReleaseIterator(di);1919 return -1;1920}19211922ssize_t rdbSaveDb(rio *rdb, int dbid, int rdbflags, long *key_counter, unsigned long long *skipped) {1923 dictEntry *de;1924 ssize_t written = 0;1925 ssize_t res;1926 size_t oldsize = 0;1927 kvstoreIterator kvs_it;1928 static long long info_updated_time = 0;1929 char *pname = (rdbflags & RDBFLAGS_AOF_PREAMBLE) ? "AOF rewrite" : "RDB";19301931 redisDb *db = server.db + dbid;1932 unsigned long long int db_size = kvstoreSize(db->keys);1933 if (db_size == 0) return 0;19341935 /* Write the SELECT DB opcode */1936 if ((res = rdbSaveType(rdb,RDB_OPCODE_SELECTDB)) < 0) goto werr;1937 written += res;1938 if ((res = rdbSaveLen(rdb, dbid)) < 0) goto werr;1939 written += res;19401941 /* Write the RESIZE DB opcode. */1942 unsigned long long expires_size = kvstoreSize(db->expires);1943 if ((res = rdbSaveType(rdb,RDB_OPCODE_RESIZEDB)) < 0) goto werr;1944 written += res;1945 if ((res = rdbSaveLen(rdb,db_size)) < 0) goto werr;1946 written += res;1947 if ((res = rdbSaveLen(rdb,expires_size)) < 0) goto werr;1948 written += res;19491950 kvstoreIteratorInit(&kvs_it, db->keys);1951 int last_slot = -1;1952 /* Iterate this DB writing every entry */1953 while ((de = kvstoreIteratorNext(&kvs_it)) != NULL) {1954 int curr_slot = kvstoreIteratorGetCurrentDictIndex(&kvs_it);1955 /* Save slot info. */1956 if (server.cluster_enabled && curr_slot != last_slot) {1957 if ((res = rdbSaveType(rdb, RDB_OPCODE_SLOT_INFO)) < 0) goto werr2;1958 written += res;1959 if ((res = rdbSaveLen(rdb, curr_slot)) < 0) goto werr2;1960 written += res;1961 if ((res = rdbSaveLen(rdb, kvstoreDictSize(db->keys, curr_slot))) < 0) goto werr2;1962 written += res;1963 if ((res = rdbSaveLen(rdb, kvstoreDictSize(db->expires, curr_slot))) < 0) goto werr2;1964 written += res;1965 /* Dismiss bucket arrays of the previous slot to reduce CoW.1966 * The final slot is not dismissed since the child exits shortly after. */1967 if (server.in_fork_child && last_slot != -1)1968 dismissDictBucketsMemory(kvstoreGetDict(db->keys, last_slot));1969 last_slot = curr_slot;1970 }1971 kvobj *kv = dictGetKV(de);1972 robj key;1973 long long expire;1974 size_t rdb_bytes_before_key = rdb->processed_bytes;19751976 /* Skip keys that are being trimmed */1977 if (server.cluster_enabled && isSlotInTrimJob(curr_slot)) {1978 (*skipped)++;1979 continue;1980 }19811982 initStaticStringObject(key,kvobjGetKey(kv));1983 expire = kvobjGetExpire(kv);1984 if (server.memory_tracking_enabled)1985 oldsize = kvobjAllocSize(kv);1986 res = rdbSaveKeyValuePair(rdb, &key, kv, expire, dbid);1987 if (server.memory_tracking_enabled)1988 updateSlotAllocSize(db, curr_slot, kv, oldsize, kvobjAllocSize(kv));1989 if (res < 0) goto werr2;1990 written += res;19911992 /* In fork child process, we can try to release memory back to the1993 * OS and possibly avoid or decrease COW. We give the dismiss1994 * mechanism a hint about an estimated size of the object we stored. */1995 size_t dump_size = rdb->processed_bytes - rdb_bytes_before_key;1996 if (server.in_fork_child && dump_size > server.page_size/2)1997 dismissObject(kv, dump_size);19981999 /* Update child info every 1 second (approximately).2000 * in order to avoid calling mstime() on each iteration, we will
Findings
✓ No findings reported for this file.