aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/src/object.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/src/object.c')
-rw-r--r--examples/redis-unstable/src/object.c1821
1 files changed, 1821 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/object.c b/examples/redis-unstable/src/object.c
new file mode 100644
index 0000000..d4915f2
--- /dev/null
+++ b/examples/redis-unstable/src/object.c
@@ -0,0 +1,1821 @@
1/* Redis Object implementation.
2 *
3 * Copyright (c) 2009-Present, Redis Ltd.
4 * All rights reserved.
5 *
6 * Copyright (c) 2024-present, Valkey contributors.
7 * All rights reserved.
8 *
9 * Licensed under your choice of (a) the Redis Source Available License 2.0
10 * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
11 * GNU Affero General Public License v3 (AGPLv3).
12 */
13
14#include "server.h"
15#include "functions.h"
16#include "intset.h" /* Compact integer set structure */
17#include "cluster_asm.h"
18#include <math.h>
19#include <ctype.h>
20
21#ifdef __CYGWIN__
22#define strtold(a,b) ((long double)strtod((a),(b)))
23#endif
24
25/* Map a metadata ID (bit index) to its compacted slot number among set bits,
26 * then return a pointer to that slot. Caller must ensure the ID bit is set. */
27uint64_t *kvobjMetaRef(kvobj *kv, int metaId) {
28 uint32_t bits = kv->metabits;
29
30 /* Expiry is always the first metadata */
31 if (likely(metaId == 0)) return ((uint64_t *)kv) - 1;
32
33 uint32_t maskId = 1u << metaId;
34 serverAssert(bits & maskId);
35
36 /* Count set bits with lower IDs to get the compacted slot index. */
37 uint32_t lowerMask = maskId - 1u;
38 int metaSlot = __builtin_popcount(bits & lowerMask);
39 return ((uint64_t *)kv) - metaSlot - 1;
40}
41
42/* For objects with large embedded keys, we reserve space for an expire field,
43 * so if expire is set later, we don't need to reallocate the object. */
44#define KEY_SIZE_TO_INCLUDE_EXPIRE_THRESHOLD 128
45
46/* ===================== Creation and parsing of objects ==================== */
47
48/* Creates kvobj (with embedded key and optional metadata)
49 *
50 * keyMetaBits - bitmask of active metadata classes to allocate space for (bit 0 is
51 * reserved for expiration).
52 *
53 * Example of "mykey" with expiration and metadata :
54 *
55 * +------------+------------+-----------+------------------+------------------------+
56 * | m.meta (8) | expiry (8) | robj (16) | key-hdr-size (1) | sdshdr5 "mykey" \0 (7) |
57 * +------------+------------+-----------+------------------+------------------------+
58 * ^
59 * |
60 * kvobjCreate() returns pointer to here
61 */
62kvobj *kvobjCreate(int type, const sds key, void *ptr, uint32_t keyMetaBits) {
63 /* Determine embedded key and expiration flags */
64 serverAssert(key != NULL);
65
66 /* If key is large and expire is not set, add space for it. */
67 size_t key_sds_len = sdslen(key);
68 if (key_sds_len >= KEY_SIZE_TO_INCLUDE_EXPIRE_THRESHOLD)
69 keyMetaBits |= KEY_META_MASK_EXPIRE;
70
71 /* Now that keyMetaBits is finalized, compute metadata size. */
72 uint32_t sizeMetas = getNumMeta(keyMetaBits) * sizeof(uint64_t);
73
74 /* Calculate embedded key size */
75 char key_sds_type = sdsReqType(key_sds_len);
76 size_t key_sds_size = sdsReqSize(key_sds_len, key_sds_type);
77
78 /* Compute the base object size */
79 size_t min_size = sizeof(robj);
80 min_size += sizeMetas;
81 min_size += 1 + key_sds_size; /* 1 byte for SDS header size */
82
83 /* Allocate object memory */
84 char *alloc = zmalloc(min_size);
85 kvobj *kv = (kvobj *) (alloc + sizeMetas);
86 kv->type = type;
87 kv->encoding = OBJ_ENCODING_RAW;
88 kv->ptr = ptr;
89 kv->refcount = 1;
90 kv->lru = 0;
91 kv->iskvobj = 1;
92 kv->metabits = keyMetaBits;
93
94 /* The memory after the struct where we embedded data. */
95 char *data = (void *)(kv + 1);
96
97 /* Store embedded key. */
98 *data++ = sdsHdrSize(key_sds_type);
99 sdsnewplacement(data, key_sds_size, key_sds_type, key, key_sds_len);
100
101 /* Reset each allocated metadata to its reset_value (such as Expiry=-1, etc) */
102 keyMetaResetValues(kv);
103
104 return kv;
105}
106
107robj *createObject(int type, void *ptr) {
108 robj *o = zmalloc(sizeof(*o));
109 o->type = type;
110 o->encoding = OBJ_ENCODING_RAW;
111 o->ptr = ptr;
112 o->refcount = 1;
113 o->lru = 0;
114 o->iskvobj = 0;
115 o->metabits = 0;
116 return o;
117}
118
119void initObjectLRUOrLFU(robj *o) {
120 if (o->refcount == OBJ_SHARED_REFCOUNT)
121 return;
122 /* Set the LRU to the current lruclock (seconds resolution), or
123 * alternatively the LFU counter. */
124 if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
125 o->lru = (LFUGetTimeInMinutes() << 8) | LFU_INIT_VAL;
126 } else {
127 o->lru = LRU_CLOCK();
128 }
129 return;
130}
131
132/* Set a special refcount in the object to make it "shared":
133 * incrRefCount and decrRefCount() will test for this special refcount
134 * and will not touch the object. This way it is free to access shared
135 * objects such as small integers from different threads without any
136 * mutex.
137 *
138 * A common pattern to create shared objects:
139 *
140 * robj *myobject = makeObjectShared(createObject(...));
141 *
142 */
143robj *makeObjectShared(robj *o) {
144 serverAssert(o->refcount == 1);
145 o->refcount = OBJ_SHARED_REFCOUNT;
146 return o;
147}
148
149/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
150 * string object where o->ptr points to a proper sds string. */
151robj *createRawStringObject(const char *ptr, size_t len) {
152 return createObject(OBJ_STRING, sdsnewlen(ptr,len));
153}
154
155/* Creates a new embedded string object and copies the content of key, val and
156 * expire to the new object. LRU is set to 0.
157 *
158 * Example of kvobj "mykey" with embedded "myvalue" (16+1+7+11 = 35bytes):
159 * +-----------+------------------+------------------------+----------------------------+
160 * | robj (16) | key-hdr-size (1) | sdshdr5 "mykey" \0 (7) | sdshdr8 "myvalue" \0 (11) |
161 * +-----------+------------------+------------------------+----------------------------+
162 */
163static kvobj *kvobjCreateEmbedString(const char *val_ptr, size_t val_len,
164 const sds key, uint32_t keyMetaBits)
165{
166 kvobj *o;
167 debugServerAssert(key != NULL);
168 uint32_t sizeMetas = getNumMeta(keyMetaBits) * sizeof(uint64_t);
169
170 /* Calculate sizes for embedded key */
171 size_t key_sds_len = sdslen(key);
172 char key_sds_type = sdsReqType(key_sds_len);
173 size_t key_sds_size = sdsReqSize(key_sds_len, key_sds_type);
174
175 /* Calculate size for embedded value (always SDS_TYPE_8) */
176 size_t val_sds_size = sdsReqSize(val_len, SDS_TYPE_8);
177
178 /* Compute base object size */
179 size_t min_size = sizeof(robj) + val_sds_size;
180 min_size += sizeMetas;
181 min_size += 1 + key_sds_size; /* 1 byte for SDS header size */
182
183 /* Allocate object memory */
184 size_t bufsize = 0;
185 char *alloc = zmalloc_usable(min_size, &bufsize);
186 o = (kvobj *) (alloc + sizeMetas);
187
188 o->type = OBJ_STRING;
189 o->encoding = OBJ_ENCODING_EMBSTR;
190 o->refcount = 1;
191 o->lru = 0;
192 o->metabits = keyMetaBits;
193 o->iskvobj = 1;
194
195 /* The memory after the struct where we embedded data. */
196 char *data = (char *)(o + 1);
197 /* Store embedded key */
198 *data++ = sdsHdrSize(key_sds_type);
199 sdsnewplacement(data, key_sds_size, key_sds_type, key, key_sds_len);
200 data += key_sds_size;
201
202 /* Copy embedded value (EMBSTR) always as SDS TYPE 8. Account for unused
203 * memory in the SDS alloc field. */
204 size_t remaining_size = bufsize - (data - alloc);
205 o->ptr = sdsnewplacement(data, remaining_size, SDS_TYPE_8, val_ptr, val_len);
206
207 keyMetaResetValues(o); /* modules + expire */
208
209 return o;
210}
211
212/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
213 * an object where the sds string is actually an unmodifiable string
214 * allocated in the same chunk as the object itself.
215 *
216 * Example of robj with embedded "myvalue" (16+1+11 = 28 bytes):
217 * +-----------+------------------+----------------------------+
218 * | robj (16) | key-hdr-size (1) | sdshdr8 "myvalue" \0 (11) |
219 * +-----------+------------------+----------------------------+
220 */
221robj *createEmbeddedStringObject(const char *val_ptr, size_t val_len) {
222 /* Calculate size for embedded value (always SDS_TYPE_8) */
223 size_t val_sds_size = sdsReqSize(val_len, SDS_TYPE_8);
224
225 /* Allocate object memory */
226 size_t bufsize = 0;
227 robj *o = zmalloc_usable(sizeof(robj) + val_sds_size, &bufsize);
228 o->type = OBJ_STRING;
229 o->encoding = OBJ_ENCODING_EMBSTR;
230 o->refcount = 1;
231 o->lru = 0;
232 o->metabits = 0;
233 o->iskvobj = 0;
234
235 /* The memory after the struct where we embedded data. */
236 char *data = (char *)(o + 1);
237
238 /* Copy embedded value (EMBSTR) always as SDS TYPE 8. Account for unused
239 * memory in the SDS alloc field. */
240 size_t remaining_size = bufsize - (data - (char *)(void *)o);
241 o->ptr = sdsnewplacement(data, remaining_size, SDS_TYPE_8, val_ptr, val_len);
242 return o;
243}
244
245sds kvobjGetKey(const kvobj *kv) {
246 unsigned char *data = (void *)(kv + 1);
247 debugServerAssert(kv->iskvobj);
248 uint8_t hdr_size = *(uint8_t *)data;
249 data += 1 + hdr_size;
250 return (sds)data;
251}
252
253long long kvobjGetExpire(const kvobj *kv) {
254 if (kv->metabits & KEY_META_MASK_EXPIRE) {
255 return (long long) (*kvobjMetaRef((kvobj *)kv, KEY_META_ID_EXPIRE));
256 } else {
257 return -1;
258 }
259}
260
261/* This functions may reallocate the value. The new allocation is returned and
262 * the old object's reference counter is decremented and possibly freed. Use the
263 * returned object instead of 'val' after calling this function. */
264kvobj *kvobjSetExpire(kvobj *kv, long long expire) {
265 /* If kv not expirable, then we need to realloc to add expire metadata */
266 if (!(kv->metabits & KEY_META_MASK_EXPIRE)) {
267 /* Nothing to do if kv not expirable and expire is -1 */
268 if (expire == -1)
269 return kv;
270
271 kv = kvobjSet(kvobjGetKey(kv), kv, kv->metabits | KEY_META_MASK_EXPIRE);
272 }
273
274 /* kv is expirable. Update expire field. */
275 *kvobjMetaRef(kv, KEY_META_ID_EXPIRE) = expire;
276 return kv;
277}
278
279/* This functions may reallocate the value. The new allocation is returned and
280 * the old object's reference counter is decremented and possibly freed. Use the
281 * returned object instead of 'val' after calling this function. */
282kvobj *kvobjSet(sds key, robj *val, uint32_t keyMetaBits) {
283 kvobj *kv;
284 if (val->type == OBJ_STRING && val->encoding == OBJ_ENCODING_EMBSTR) {
285 size_t len = sdslen(val->ptr);
286
287 /* Embed when the sum is less than a cache line (Metadata is discarded
288 * since we don't have to be accurate and it is placed before the object) */
289 size_t size = sizeof(kvobj);
290 size += (key != NULL) * (sdslen(key) + 3); /* hdr size (1) + hdr (1) + nullterm (1) */
291 size += 4 + len; /* embstr header (3) + nullterm (1) */
292 if (size <= CACHE_LINE_SIZE) {
293 kv = kvobjCreateEmbedString(val->ptr, len, key, keyMetaBits);
294 } else {
295 kv = kvobjCreate(OBJ_STRING, key, sdsnewlen(val->ptr, len), keyMetaBits);
296 }
297 } else {
298 /* Create a new object with embedded key. Reuse ptr if possible. */
299 void *valptr;
300 if (val->refcount == 1) {
301 /* Reuse the ptr. There are no other references to val. */
302 valptr = val->ptr;
303 val->ptr = NULL;
304 } else if (val->type == OBJ_STRING &&
305 val->encoding == OBJ_ENCODING_INT) {
306 /* The pointer is not allocated memory. We can just copy the pointer. */
307 valptr = val->ptr;
308 } else if (val->type == OBJ_STRING &&
309 val->encoding == OBJ_ENCODING_RAW) {
310 /* Dup the string. */
311 valptr = sdsdup(val->ptr);
312 } else {
313 /* There are multiple references to this non-string object. Most types
314 * can be duplicated, but for a module type is not always possible. */
315 serverPanic("Not implemented");
316 }
317 kv = kvobjCreate(val->type, key, valptr, keyMetaBits);
318 kv->encoding = val->encoding;
319 }
320
321 kv->lru = val->lru;
322
323 /* Transfer module metadata from `val` to new `kv` (if `val` of type kvobj with metadata). */
324 if (val->metabits & KEY_META_MASK_MODULES)
325 keyMetaTransition((kvobj *) val, kv);
326
327 decrRefCount(val);
328 return kv;
329}
330
331/* Create a string object with EMBSTR encoding if it is smaller than
332 * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
333 * used.
334 *
335 * The current limit of 44 is chosen so that the biggest string object
336 * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
337#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
338robj *createStringObject(const char *ptr, size_t len) {
339 if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
340 return createEmbeddedStringObject(ptr,len);
341 else
342 return createRawStringObject(ptr,len);
343}
344
345/* Same as CreateRawStringObject, can return NULL if allocation fails */
346robj *tryCreateRawStringObject(const char *ptr, size_t len) {
347 sds str = sdstrynewlen(ptr,len);
348 if (!str) return NULL;
349 return createObject(OBJ_STRING, str);
350}
351
352/* Same as createStringObject, can return NULL if allocation fails */
353robj *tryCreateStringObject(const char *ptr, size_t len) {
354 if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
355 return createEmbeddedStringObject(ptr,len);
356 else
357 return tryCreateRawStringObject(ptr,len);
358}
359
360/* Create a string object from a long long value according to the specified flag. */
361#define LL2STROBJ_AUTO 0 /* automatically create the optimal string object */
362#define LL2STROBJ_NO_SHARED 1 /* disallow shared objects */
363#define LL2STROBJ_NO_INT_ENC 2 /* disallow integer encoded objects. */
364robj *createStringObjectFromLongLongWithOptions(long long value, int flag) {
365 robj *o;
366
367 if (value >= 0 && value < OBJ_SHARED_INTEGERS && flag == LL2STROBJ_AUTO) {
368 o = shared.integers[value];
369 } else {
370 if ((value >= LONG_MIN && value <= LONG_MAX) && flag != LL2STROBJ_NO_INT_ENC) {
371 o = createObject(OBJ_STRING, NULL);
372 o->encoding = OBJ_ENCODING_INT;
373 o->ptr = (void*)((long)value);
374 } else {
375 char buf[LONG_STR_SIZE];
376 int len = ll2string(buf, sizeof(buf), value);
377 o = createStringObject(buf, len);
378 }
379 }
380 return o;
381}
382
383/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding
384 * to create a shared object if possible. */
385robj *createStringObjectFromLongLong(long long value) {
386 return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_AUTO);
387}
388
389/* The function avoids returning a shared integer when LFU/LRU info
390 * are needed, that is, when the object is used as a value in the key
391 * space(for instance when the INCR command is used), and Redis is
392 * configured to evict based on LFU/LRU, so we want LFU/LRU values
393 * specific for each key. */
394robj *createStringObjectFromLongLongForValue(long long value) {
395 if (server.maxmemory == 0 || !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) {
396 /* If the maxmemory policy permits, we can still return shared integers */
397 return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_AUTO);
398 } else {
399 return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_NO_SHARED);
400 }
401}
402
403/* Create a string object that contains an sds inside it. That means it can't be
404 * integer encoded (OBJ_ENCODING_INT), and it'll always be an EMBSTR type. */
405robj *createStringObjectFromLongLongWithSds(long long value) {
406 return createStringObjectFromLongLongWithOptions(value, LL2STROBJ_NO_INT_ENC);
407}
408
409/* Create a string object from a long double. If humanfriendly is non-zero
410 * it does not use exponential format and trims trailing zeroes at the end,
411 * however this results in loss of precision. Otherwise exp format is used
412 * and the output of snprintf() is not modified.
413 *
414 * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
415robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
416 char buf[MAX_LONG_DOUBLE_CHARS];
417 int len = ld2string(buf,sizeof(buf),value,humanfriendly? LD_STR_HUMAN: LD_STR_AUTO);
418 return createStringObject(buf,len);
419}
420
421/* Duplicate a string object, with the guarantee that the returned object
422 * has the same encoding as the original one.
423 *
424 * This function also guarantees that duplicating a small integer object
425 * (or a string object that contains a representation of a small integer)
426 * will always result in a fresh object that is unshared (refcount == 1).
427 *
428 * The resulting object always has refcount set to 1. */
429robj *dupStringObject(const robj *o) {
430 robj *d;
431
432 serverAssert(o->type == OBJ_STRING);
433
434 switch(o->encoding) {
435 case OBJ_ENCODING_RAW:
436 return createRawStringObject(o->ptr,sdslen(o->ptr));
437 case OBJ_ENCODING_EMBSTR:
438 return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
439 case OBJ_ENCODING_INT:
440 d = createObject(OBJ_STRING, NULL);
441 d->encoding = OBJ_ENCODING_INT;
442 d->ptr = o->ptr;
443 return d;
444 default:
445 serverPanic("Wrong encoding.");
446 break;
447 }
448}
449
450robj *createQuicklistObject(int fill, int compress) {
451 quicklist *l = quicklistNew(fill, compress);
452 robj *o = createObject(OBJ_LIST,l);
453 o->encoding = OBJ_ENCODING_QUICKLIST;
454 return o;
455}
456
457robj *createListListpackObject(void) {
458 unsigned char *lp = lpNew(0);
459 robj *o = createObject(OBJ_LIST,lp);
460 o->encoding = OBJ_ENCODING_LISTPACK;
461 return o;
462}
463
464robj *createSetObject(void) {
465 dict *d = dictCreate(&setDictType);
466 robj *o = createObject(OBJ_SET,d);
467 o->encoding = OBJ_ENCODING_HT;
468 return o;
469}
470
471robj *createIntsetObject(void) {
472 intset *is = intsetNew();
473 robj *o = createObject(OBJ_SET,is);
474 o->encoding = OBJ_ENCODING_INTSET;
475 return o;
476}
477
478robj *createSetListpackObject(void) {
479 unsigned char *lp = lpNew(0);
480 robj *o = createObject(OBJ_SET, lp);
481 o->encoding = OBJ_ENCODING_LISTPACK;
482 return o;
483}
484
485robj *createHashObject(void) {
486 unsigned char *zl = lpNew(0);
487 robj *o = createObject(OBJ_HASH, zl);
488 o->encoding = OBJ_ENCODING_LISTPACK;
489 return o;
490}
491
492robj *createZsetObject(void) {
493 zset *zs = zmalloc(sizeof(*zs));
494 robj *o;
495
496 zs->dict = dictCreate(&zsetDictType);
497 zs->zsl = zslCreate();
498 o = createObject(OBJ_ZSET,zs);
499 o->encoding = OBJ_ENCODING_SKIPLIST;
500 return o;
501}
502
503robj *createZsetListpackObject(void) {
504 unsigned char *lp = lpNew(0);
505 robj *o = createObject(OBJ_ZSET,lp);
506 o->encoding = OBJ_ENCODING_LISTPACK;
507 return o;
508}
509
510robj *createStreamObject(void) {
511 stream *s = streamNew();
512 robj *o = createObject(OBJ_STREAM,s);
513 o->encoding = OBJ_ENCODING_STREAM;
514 return o;
515}
516
517robj *createModuleObject(moduleType *mt, void *value) {
518 moduleValue *mv = zmalloc(sizeof(*mv));
519 mv->type = mt;
520 mv->value = value;
521 return createObject(OBJ_MODULE,mv);
522}
523
524void freeStringObject(robj *o) {
525 if (o->encoding == OBJ_ENCODING_RAW) {
526 sdsfree(o->ptr);
527 }
528}
529
530void freeListObject(robj *o) {
531 if (o->encoding == OBJ_ENCODING_QUICKLIST) {
532 quicklistRelease(o->ptr);
533 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {
534 lpFree(o->ptr);
535 } else {
536 serverPanic("Unknown list encoding type");
537 }
538}
539
540void freeSetObject(robj *o) {
541 switch (o->encoding) {
542 case OBJ_ENCODING_HT:
543#ifdef DEBUG_ASSERTIONS
544 dictEmpty(o->ptr, NULL);
545 debugServerAssert(*htGetMetadataSize(o->ptr) == 0);
546#endif
547 dictRelease((dict*) o->ptr);
548 break;
549 case OBJ_ENCODING_INTSET:
550 case OBJ_ENCODING_LISTPACK:
551 zfree(o->ptr);
552 break;
553 default:
554 serverPanic("Unknown set encoding type");
555 }
556}
557
558void freeZsetObject(robj *o) {
559 zset *zs;
560 switch (o->encoding) {
561 case OBJ_ENCODING_SKIPLIST:
562 zs = o->ptr;
563 dictRelease(zs->dict);
564 zslFree(zs->zsl);
565 zfree(zs);
566 break;
567 case OBJ_ENCODING_LISTPACK:
568 zfree(o->ptr);
569 break;
570 default:
571 serverPanic("Unknown sorted set encoding");
572 }
573}
574
575void freeHashObject(robj *o) {
576 hashTypeFree(o);
577}
578
579void freeModuleObject(robj *o) {
580 moduleValue *mv = o->ptr;
581 mv->type->free(mv->value);
582 zfree(mv);
583}
584
585void freeStreamObject(robj *o) {
586 freeStream(o->ptr);
587}
588
589void incrRefCount(robj *o) {
590 if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT - 1) {
591 o->refcount++;
592 } else {
593 if (o->refcount == OBJ_SHARED_REFCOUNT) {
594 /* Nothing to do: this refcount is immutable. */
595 } else if (o->refcount == OBJ_STATIC_REFCOUNT) {
596 serverPanic("You tried to retain an object allocated in the stack");
597 } else {
598 serverPanic("You tried to retain an object with maximum refcount");
599 }
600 }
601}
602
603void decrRefCount(robj *o) {
604 if (o->refcount == OBJ_SHARED_REFCOUNT)
605 return; /* Nothing to do: this refcount is immutable. */
606
607 if (unlikely(o->refcount <= 0)) {
608 serverPanic("illegal decrRefCount for object with: type %u, encoding %u, refcount %d",
609 o->type, o->encoding, o->refcount);
610 }
611
612 if (--(o->refcount) == 0) {
613 void *alloc = o;
614
615 if (o->iskvobj) {
616 /* eval real allocation pointer */
617 alloc = kvobjGetAllocPtr(o);
618 /* if kvobj has metadata attached. */
619 if (getModuleMetaBits(o->metabits))
620 keyMetaOnFree((kvobj *)o);
621 }
622
623 if (o->ptr != NULL) {
624 switch(o->type) {
625 case OBJ_STRING: freeStringObject(o); break;
626 case OBJ_LIST: freeListObject(o); break;
627 case OBJ_SET: freeSetObject(o); break;
628 case OBJ_ZSET: freeZsetObject(o); break;
629 case OBJ_HASH: freeHashObject(o); break;
630 case OBJ_MODULE: freeModuleObject(o); break;
631 case OBJ_STREAM: freeStreamObject(o); break;
632 default: serverPanic("Unknown object type"); break;
633 }
634 }
635 zfree(alloc);
636 }
637}
638
639/* See dismissObject() */
640void dismissSds(sds s) {
641 dismissMemory(sdsAllocPtr(s), sdsAllocSize(s));
642}
643
644/* See dismissObject() */
645void dismissStringObject(robj *o) {
646 if (o->encoding == OBJ_ENCODING_RAW) {
647 dismissSds(o->ptr);
648 }
649}
650
651/* See dismissObject() */
652void dismissListObject(robj *o, size_t size_hint) {
653 if (o->encoding == OBJ_ENCODING_QUICKLIST) {
654 quicklist *ql = o->ptr;
655 serverAssert(ql->len != 0);
656 /* We iterate all nodes only when average node size is bigger than a
657 * page size, and there's a high chance we'll actually dismiss something. */
658 if (size_hint / ql->len >= server.page_size) {
659 quicklistNode *node = ql->head;
660 while (node) {
661 if (quicklistNodeIsCompressed(node)) {
662 dismissMemory(node->entry, ((quicklistLZF*)node->entry)->sz);
663 } else {
664 dismissMemory(node->entry, node->sz);
665 }
666 node = node->next;
667 }
668 }
669 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {
670 dismissMemory(o->ptr, lpBytes((unsigned char*)o->ptr));
671 } else {
672 serverPanic("Unknown list encoding type");
673 }
674}
675
676/* See dismissObject() */
677void dismissSetObject(robj *o, size_t size_hint) {
678 if (o->encoding == OBJ_ENCODING_HT) {
679 dict *set = o->ptr;
680 serverAssert(dictSize(set) != 0);
681 /* We iterate all nodes only when average member size is bigger than a
682 * page size, and there's a high chance we'll actually dismiss something. */
683 if (size_hint / dictSize(set) >= server.page_size) {
684 dictEntry *de;
685 dictIterator di;
686 dictInitIterator(&di, set);
687 while ((de = dictNext(&di)) != NULL) {
688 dismissSds(dictGetKey(de));
689 }
690 dictResetIterator(&di);
691 }
692
693 /* Dismiss hash table memory. */
694 dismissMemory(set->ht_table[0], DICTHT_SIZE(set->ht_size_exp[0])*sizeof(dictEntry*));
695 dismissMemory(set->ht_table[1], DICTHT_SIZE(set->ht_size_exp[1])*sizeof(dictEntry*));
696 } else if (o->encoding == OBJ_ENCODING_INTSET) {
697 dismissMemory(o->ptr, intsetBlobLen((intset*)o->ptr));
698 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {
699 dismissMemory(o->ptr, lpBytes((unsigned char *)o->ptr));
700 } else {
701 serverPanic("Unknown set encoding type");
702 }
703}
704
705/* See dismissObject() */
706void dismissZsetObject(robj *o, size_t size_hint) {
707 if (o->encoding == OBJ_ENCODING_SKIPLIST) {
708 zset *zs = o->ptr;
709 zskiplist *zsl = zs->zsl;
710 serverAssert(zsl->length != 0);
711 /* We iterate all nodes only when average member size is bigger than a
712 * page size, and there's a high chance we'll actually dismiss something. */
713 if (size_hint / zsl->length >= server.page_size) {
714 zskiplistNode *zn = zsl->header->level[0].forward;
715 while (zn != NULL) {
716 zskiplistNode *next = zn->level[0].forward;
717 dismissMemory(zn, 0);
718 zn = next;
719 }
720 }
721
722 /* Dismiss hash table memory. */
723 dict *d = zs->dict;
724 dismissMemory(d->ht_table[0], DICTHT_SIZE(d->ht_size_exp[0])*sizeof(dictEntry*));
725 dismissMemory(d->ht_table[1], DICTHT_SIZE(d->ht_size_exp[1])*sizeof(dictEntry*));
726 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {
727 dismissMemory(o->ptr, lpBytes((unsigned char*)o->ptr));
728 } else {
729 serverPanic("Unknown zset encoding type");
730 }
731}
732
733/* See dismissObject() */
734void dismissHashObject(robj *o, size_t size_hint) {
735 if (o->encoding == OBJ_ENCODING_HT) {
736 dict *d = o->ptr;
737 serverAssert(dictSize(d) != 0);
738 /* We iterate all fields only when average field/value size is bigger than
739 * a page size, and there's a high chance we'll actually dismiss something. */
740 if (size_hint / dictSize(d) >= server.page_size) {
741 dictEntry *de;
742 dictIterator di;
743 dictInitIterator(&di, d);
744 while ((de = dictNext(&di)) != NULL) {
745 entryDismissMemory((Entry *) dictGetKey(de));
746 }
747 dictResetIterator(&di);
748 }
749
750 /* Dismiss hash table memory. */
751 dismissMemory(d->ht_table[0], DICTHT_SIZE(d->ht_size_exp[0])*sizeof(dictEntry*));
752 dismissMemory(d->ht_table[1], DICTHT_SIZE(d->ht_size_exp[1])*sizeof(dictEntry*));
753 } else if (o->encoding == OBJ_ENCODING_LISTPACK) {
754 dismissMemory(o->ptr, lpBytes((unsigned char*)o->ptr));
755 } else if (o->encoding == OBJ_ENCODING_LISTPACK_EX) {
756 listpackEx *lpt = o->ptr;
757 dismissMemory(lpt->lp, lpBytes((unsigned char*)lpt->lp));
758 } else {
759 serverPanic("Unknown hash encoding type");
760 }
761}
762
763/* See dismissObject() */
764void dismissStreamObject(robj *o, size_t size_hint) {
765 stream *s = o->ptr;
766 rax *rax = s->rax;
767 if (raxSize(rax) == 0) return;
768
769 /* Iterate only on stream entries, although size_hint may include serialized
770 * consumer groups info, but usually, stream entries take up most of
771 * the space. */
772 if (size_hint / raxSize(rax) >= server.page_size) {
773 raxIterator ri;
774 raxStart(&ri,rax);
775 raxSeek(&ri,"^",NULL,0);
776 while (raxNext(&ri)) {
777 dismissMemory(ri.data, lpBytes(ri.data));
778 }
779 raxStop(&ri);
780 }
781}
782
783/* When creating a snapshot in a fork child process, the main process and child
784 * process share the same physical memory pages, and if / when the parent
785 * modifies any keys due to write traffic, it'll cause CoW which consume
786 * physical memory. In the child process, after serializing the key and value,
787 * the data is definitely not accessed again, so to avoid unnecessary CoW, we
788 * try to release their memory back to OS. see dismissMemory().
789 *
790 * Because of the cost of iterating all node/field/member/entry of complex data
791 * types, we iterate and dismiss them only when approximate average we estimate
792 * the size of an individual allocation is more than a page size of OS.
793 * 'size_hint' is the size of serialized value. This method is not accurate, but
794 * it can reduce unnecessary iteration for complex data types that are probably
795 * not going to release any memory. */
796void dismissObject(robj *o, size_t size_hint) {
797 /* madvise(MADV_DONTNEED) may not work if Transparent Huge Pages is enabled. */
798 if (server.thp_enabled) return;
799
800 /* Currently we use zmadvise_dontneed only when we use jemalloc with Linux.
801 * so we avoid these pointless loops when they're not going to do anything. */
802#if defined(USE_JEMALLOC) && defined(__linux__)
803 if (o->refcount != 1) return;
804 switch(o->type) {
805 case OBJ_STRING: dismissStringObject(o); break;
806 case OBJ_LIST: dismissListObject(o, size_hint); break;
807 case OBJ_SET: dismissSetObject(o, size_hint); break;
808 case OBJ_ZSET: dismissZsetObject(o, size_hint); break;
809 case OBJ_HASH: dismissHashObject(o, size_hint); break;
810 case OBJ_STREAM: dismissStreamObject(o, size_hint); break;
811 default: break;
812 }
813#else
814 UNUSED(o); UNUSED(size_hint);
815#endif
816}
817
818int checkType(client *c, robj *o, int type) {
819 /* A NULL is considered an empty key */
820 if (o && o->type != type) {
821 addReplyErrorObject(c,shared.wrongtypeerr);
822 return 1;
823 }
824 return 0;
825}
826
827int isSdsRepresentableAsLongLong(sds s, long long *llval) {
828 return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;
829}
830
831int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
832 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
833 if (o->encoding == OBJ_ENCODING_INT) {
834 if (llval) *llval = (long) o->ptr;
835 return C_OK;
836 } else {
837 return isSdsRepresentableAsLongLong(o->ptr,llval);
838 }
839}
840
841/* Optimize the SDS string inside the string object to require little space,
842 * in case there is more than 10% of free space at the end of the SDS. */
843void trimStringObjectIfNeeded(robj *o, int trim_small_values) {
844 if (o->encoding != OBJ_ENCODING_RAW) return;
845 /* A string may have free space in the following cases:
846 * 1. When an arg len is greater than PROTO_MBULK_BIG_ARG the query buffer may be used directly as the SDS string.
847 * 2. When utilizing the argument caching mechanism in Lua.
848 * 3. When calling from RM_TrimStringAllocation (trim_small_values is true). */
849 size_t len = sdslen(o->ptr);
850 if (len >= PROTO_MBULK_BIG_ARG ||
851 trim_small_values||
852 (server.executing_client && server.executing_client->flags & CLIENT_SCRIPT && len < LUA_CMD_OBJCACHE_MAX_LEN)) {
853 if (sdsavail(o->ptr) > len/10) {
854 o->ptr = sdsRemoveFreeSpace(o->ptr, 0);
855 }
856 }
857}
858
859/* Try to encode a string object in order to save space */
860robj *tryObjectEncodingEx(robj *o, int try_trim) {
861 long value;
862 sds s = o->ptr;
863 size_t len;
864
865 /* Make sure this is a string object, the only type we encode
866 * in this function. Other types use encoded memory efficient
867 * representations but are handled by the commands implementing
868 * the type. */
869 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
870
871 /* We try some specialized encoding only for objects that are
872 * RAW or EMBSTR encoded, in other words objects that are still
873 * in represented by an actually array of chars. */
874 if (!sdsEncodedObject(o)) return o;
875
876 /* It's not safe to encode shared objects: shared objects can be shared
877 * everywhere in the "object space" of Redis and may end in places where
878 * they are not handled. We handle them only as values in the keyspace. */
879 if (o->refcount > 1) return o;
880
881 /* Check if we can represent this string as a long integer.
882 * Note that we are sure that a string larger than 20 chars is not
883 * representable as a 32 nor 64 bit integer. */
884 len = sdslen(s);
885 if (len <= 20 && string2l(s,len,&value)) {
886 /* This object is encodable as a long. */
887 if (o->encoding == OBJ_ENCODING_RAW) {
888 sdsfree(o->ptr);
889 o->encoding = OBJ_ENCODING_INT;
890 o->ptr = (void*) value;
891 return o;
892 } else if (o->encoding == OBJ_ENCODING_EMBSTR) {
893 decrRefCount(o);
894 return createStringObjectFromLongLongForValue(value);
895 }
896 }
897
898 /* If the string is small and is still RAW encoded,
899 * try the EMBSTR encoding which is more efficient.
900 * In this representation the object and the SDS string are allocated
901 * in the same chunk of memory to save space and cache misses. */
902 if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {
903 robj *emb;
904
905 if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
906 emb = createEmbeddedStringObject(s,sdslen(s));
907 decrRefCount(o);
908 return emb;
909 }
910
911 /* We can't encode the object...
912 * Do the last try, and at least optimize the SDS string inside */
913 if (try_trim)
914 trimStringObjectIfNeeded(o, 0);
915
916 /* Return the original object. */
917 return o;
918}
919
920robj *tryObjectEncoding(robj *o) {
921 return tryObjectEncodingEx(o, 1);
922}
923
924size_t getObjectLength(robj *o) {
925 switch(o->type) {
926 case OBJ_STRING: return stringObjectLen(o);
927 case OBJ_LIST: return listTypeLength(o);
928 case OBJ_SET: return setTypeSize(o);
929 case OBJ_ZSET: return zsetLength(o);
930 case OBJ_HASH: return hashTypeLength(o, 0);
931 case OBJ_STREAM: return streamLength(o);
932 default: return 0;
933 }
934}
935
936/* Get a decoded version of an encoded object (returned as a new object).
937 * If the object is already raw-encoded just increment the ref count. */
938robj *getDecodedObject(robj *o) {
939 robj *dec;
940
941 if (sdsEncodedObject(o)) {
942 incrRefCount(o);
943 return o;
944 }
945 if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {
946 char buf[32];
947
948 ll2string(buf,32,(long)o->ptr);
949 dec = createStringObject(buf,strlen(buf));
950 return dec;
951 } else {
952 serverPanic("Unknown encoding type");
953 }
954}
955
956/* Compare two string objects via strcmp() or strcoll() depending on flags.
957 * Note that the objects may be integer-encoded. In such a case we
958 * use ll2string() to get a string representation of the numbers on the stack
959 * and compare the strings, it's much faster than calling getDecodedObject().
960 *
961 * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison
962 * is used. */
963
964#define REDIS_COMPARE_BINARY (1<<0)
965#define REDIS_COMPARE_COLL (1<<1)
966
967int compareStringObjectsWithFlags(const robj *a, const robj *b, int flags) {
968 serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);
969 char bufa[128], bufb[128], *astr, *bstr;
970 size_t alen, blen, minlen;
971
972 if (a == b) return 0;
973 if (sdsEncodedObject(a)) {
974 astr = a->ptr;
975 alen = sdslen(astr);
976 } else {
977 alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);
978 astr = bufa;
979 }
980 if (sdsEncodedObject(b)) {
981 bstr = b->ptr;
982 blen = sdslen(bstr);
983 } else {
984 blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
985 bstr = bufb;
986 }
987 if (flags & REDIS_COMPARE_COLL) {
988 return strcoll(astr,bstr);
989 } else {
990 int cmp;
991
992 minlen = (alen < blen) ? alen : blen;
993 cmp = memcmp(astr,bstr,minlen);
994 if (cmp == 0) return alen-blen;
995 return cmp;
996 }
997}
998
999/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */
1000int compareStringObjects(const robj *a, const robj *b) {
1001 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);
1002}
1003
1004/* Wrapper for compareStringObjectsWithFlags() using collation. */
1005int collateStringObjects(const robj *a, const robj *b) {
1006 return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);
1007}
1008
1009/* Equal string objects return 1 if the two objects are the same from the
1010 * point of view of a string comparison, otherwise 0 is returned. Note that
1011 * this function is faster than checking for (compareStringObject(a,b) == 0)
1012 * because it can perform some more optimization. */
1013int equalStringObjects(robj *a, robj *b) {
1014 if (a->encoding == OBJ_ENCODING_INT &&
1015 b->encoding == OBJ_ENCODING_INT){
1016 /* If both strings are integer encoded just check if the stored
1017 * long is the same. */
1018 return a->ptr == b->ptr;
1019 } else {
1020 if (sdsEncodedObject(a) && sdsEncodedObject(b)
1021 && sdslen(a->ptr) != sdslen(b->ptr))
1022 {
1023 return 0;
1024 }
1025 return compareStringObjects(a,b) == 0;
1026 }
1027}
1028
1029size_t stringObjectLen(robj *o) {
1030 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
1031 if (sdsEncodedObject(o)) {
1032 return sdslen(o->ptr);
1033 } else {
1034 return sdigits10((long)o->ptr);
1035 }
1036}
1037
1038size_t stringObjectAllocSize(const robj *o) {
1039 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
1040 if(o->encoding == OBJ_ENCODING_INT) {
1041 /* Value already counted (reuse the "ptr" in header to store int) */
1042 return 0;
1043 } else if(o->encoding == OBJ_ENCODING_RAW) {
1044 return sdsAllocSize(o->ptr);
1045 } else if(o->encoding == OBJ_ENCODING_EMBSTR) {
1046 /* Value already counted (Value embedded in the object as well) */
1047 return 0;
1048 } else {
1049 serverPanic("Unknown string encoding");
1050 }
1051}
1052
1053int getDoubleFromObject(const robj *o, double *target) {
1054 double value;
1055
1056 if (o == NULL) {
1057 value = 0;
1058 } else {
1059 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
1060 if (sdsEncodedObject(o)) {
1061 if (!string2d(o->ptr, sdslen(o->ptr), &value))
1062 return C_ERR;
1063 } else if (o->encoding == OBJ_ENCODING_INT) {
1064 value = (long)o->ptr;
1065 } else {
1066 serverPanic("Unknown string encoding");
1067 }
1068 }
1069 *target = value;
1070 return C_OK;
1071}
1072
1073int getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {
1074 double value;
1075 if (getDoubleFromObject(o, &value) != C_OK) {
1076 if (msg != NULL) {
1077 addReplyError(c,(char*)msg);
1078 } else {
1079 addReplyError(c,"value is not a valid float");
1080 }
1081 return C_ERR;
1082 }
1083 *target = value;
1084 return C_OK;
1085}
1086
1087int getLongDoubleFromObject(robj *o, long double *target) {
1088 long double value;
1089
1090 if (o == NULL) {
1091 value = 0;
1092 } else {
1093 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
1094 if (sdsEncodedObject(o)) {
1095 if (!string2ld(o->ptr, sdslen(o->ptr), &value))
1096 return C_ERR;
1097 } else if (o->encoding == OBJ_ENCODING_INT) {
1098 value = (long)o->ptr;
1099 } else {
1100 serverPanic("Unknown string encoding");
1101 }
1102 }
1103 *target = value;
1104 return C_OK;
1105}
1106
1107int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {
1108 long double value;
1109 if (getLongDoubleFromObject(o, &value) != C_OK) {
1110 if (msg != NULL) {
1111 addReplyError(c,(char*)msg);
1112 } else {
1113 addReplyError(c,"value is not a valid float");
1114 }
1115 return C_ERR;
1116 }
1117 *target = value;
1118 return C_OK;
1119}
1120
1121int getLongLongFromObject(robj *o, long long *target) {
1122 long long value;
1123
1124 if (o == NULL) {
1125 value = 0;
1126 } else {
1127 serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
1128 if (sdsEncodedObject(o)) {
1129 if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;
1130 } else if (o->encoding == OBJ_ENCODING_INT) {
1131 value = (long)o->ptr;
1132 } else {
1133 serverPanic("Unknown string encoding");
1134 }
1135 }
1136 if (target) *target = value;
1137 return C_OK;
1138}
1139
1140int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {
1141 long long value;
1142 if (getLongLongFromObject(o, &value) != C_OK) {
1143 if (msg != NULL) {
1144 addReplyError(c,(char*)msg);
1145 } else {
1146 addReplyError(c,"value is not an integer or out of range");
1147 }
1148 return C_ERR;
1149 }
1150 *target = value;
1151 return C_OK;
1152}
1153
1154int getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
1155 long long value;
1156
1157 if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;
1158 if (value < LONG_MIN || value > LONG_MAX) {
1159 if (msg != NULL) {
1160 addReplyError(c,(char*)msg);
1161 } else {
1162 addReplyError(c,"value is out of range");
1163 }
1164 return C_ERR;
1165 }
1166 *target = value;
1167 return C_OK;
1168}
1169
1170int getRangeLongFromObjectOrReply(client *c, robj *o, long min, long max, long *target, const char *msg) {
1171 if (getLongFromObjectOrReply(c, o, target, msg) != C_OK) return C_ERR;
1172 if (*target < min || *target > max) {
1173 if (msg != NULL) {
1174 addReplyError(c,(char*)msg);
1175 } else {
1176 addReplyErrorFormat(c,"value is out of range, value must between %ld and %ld", min, max);
1177 }
1178 return C_ERR;
1179 }
1180 return C_OK;
1181}
1182
1183int getPositiveLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
1184 if (msg) {
1185 return getRangeLongFromObjectOrReply(c, o, 0, LONG_MAX, target, msg);
1186 } else {
1187 return getRangeLongFromObjectOrReply(c, o, 0, LONG_MAX, target, "value is out of range, must be positive");
1188 }
1189}
1190
1191int getIntFromObjectOrReply(client *c, robj *o, int *target, const char *msg) {
1192 long value;
1193
1194 if (getRangeLongFromObjectOrReply(c, o, INT_MIN, INT_MAX, &value, msg) != C_OK)
1195 return C_ERR;
1196
1197 *target = value;
1198 return C_OK;
1199}
1200
1201char *strEncoding(int encoding) {
1202 switch(encoding) {
1203 case OBJ_ENCODING_RAW: return "raw";
1204 case OBJ_ENCODING_INT: return "int";
1205 case OBJ_ENCODING_HT: return "hashtable";
1206 case OBJ_ENCODING_QUICKLIST: return "quicklist";
1207 case OBJ_ENCODING_LISTPACK: return "listpack";
1208 case OBJ_ENCODING_LISTPACK_EX: return "listpackex";
1209 case OBJ_ENCODING_INTSET: return "intset";
1210 case OBJ_ENCODING_SKIPLIST: return "skiplist";
1211 case OBJ_ENCODING_EMBSTR: return "embstr";
1212 case OBJ_ENCODING_STREAM: return "stream";
1213 default: return "unknown";
1214 }
1215}
1216
1217/* =========================== Memory introspection ========================= */
1218
1219/* Returns the size in bytes consumed by the object header, key and value in RAM.
1220 * Note that the returned value is just an approximation, especially in the
1221 * case of aggregated data types where only "sample_size" elements
1222 * are checked and averaged to estimate the total size. */
1223#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */
1224size_t kvobjComputeSize(robj *key, kvobj *o, size_t sample_size, int dbid) {
1225 if (o->type == OBJ_STRING ||
1226 o->type == OBJ_LIST ||
1227 o->type == OBJ_SET ||
1228 o->type == OBJ_ZSET ||
1229 o->type == OBJ_HASH ||
1230 o->type == OBJ_STREAM)
1231 {
1232 return kvobjAllocSize(o);
1233 } else if (o->type == OBJ_MODULE) {
1234 return zmalloc_size(o) + moduleGetMemUsage(key, o, sample_size, dbid);
1235 }
1236 serverPanic("Unknown object type");
1237}
1238
1239size_t kvobjAllocSize(kvobj *o) {
1240 /* All kv-objects has at least kvobj header and embedded key */
1241 size_t asize = zmalloc_size(kvobjGetAllocPtr(o));
1242
1243 if (o->type == OBJ_STRING) {
1244 asize += stringObjectAllocSize(o);
1245 } else if (o->type == OBJ_LIST) {
1246 asize += listTypeAllocSize(o);
1247 } else if (o->type == OBJ_SET) {
1248 asize += setTypeAllocSize(o);
1249 } else if (o->type == OBJ_ZSET) {
1250 asize += zsetAllocSize(o);
1251 } else if (o->type == OBJ_HASH) {
1252 asize += hashTypeAllocSize(o);
1253 } else if (o->type == OBJ_STREAM) {
1254 stream *s = o->ptr;
1255 asize += s->alloc_size;
1256 } else if (o->type == OBJ_MODULE) {
1257 /* TODO: Provide moduleGetAllocSize() module API for O(1) allocation size retrieval */
1258 }
1259 return asize;
1260}
1261
1262/* Release data obtained with getMemoryOverheadData(). */
1263void freeMemoryOverheadData(struct redisMemOverhead *mh) {
1264 zfree(mh->db);
1265 zfree(mh);
1266}
1267
1268/* Return a struct redisMemOverhead filled with memory overhead
1269 * information used for the MEMORY OVERHEAD and INFO command. The returned
1270 * structure pointer should be freed calling freeMemoryOverheadData(). */
1271struct redisMemOverhead *getMemoryOverheadData(void) {
1272 int j;
1273 size_t mem_total = 0;
1274 size_t mem = 0;
1275 size_t zmalloc_used = zmalloc_used_memory();
1276 struct redisMemOverhead *mh = zcalloc(sizeof(*mh));
1277
1278 mh->total_allocated = zmalloc_used;
1279 mh->startup_allocated = server.initial_memory_usage;
1280 mh->peak_allocated = server.stat_peak_memory;
1281 mh->total_frag =
1282 (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.zmalloc_used;
1283 mh->total_frag_bytes =
1284 server.cron_malloc_stats.process_rss - server.cron_malloc_stats.zmalloc_used;
1285 /* Starting with redis 7.4, the lua memory is part of the total memory usage
1286 * of redis, and that includes RSS and all other memory metrics. We only want
1287 * to deduct it from active defrag. */
1288 size_t frag_smallbins_bytes =
1289 server.cron_malloc_stats.allocator_frag_smallbins_bytes - server.cron_malloc_stats.lua_allocator_frag_smallbins_bytes;
1290 size_t allocated =
1291 server.cron_malloc_stats.allocator_allocated - server.cron_malloc_stats.lua_allocator_allocated;
1292 mh->allocator_frag = (float)frag_smallbins_bytes / allocated + 1;
1293 mh->allocator_frag_bytes = frag_smallbins_bytes;
1294 mh->allocator_rss =
1295 (float)server.cron_malloc_stats.allocator_resident / server.cron_malloc_stats.allocator_active;
1296 mh->allocator_rss_bytes =
1297 server.cron_malloc_stats.allocator_resident - server.cron_malloc_stats.allocator_active;
1298 mh->rss_extra =
1299 (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.allocator_resident;
1300 mh->rss_extra_bytes =
1301 server.cron_malloc_stats.process_rss - server.cron_malloc_stats.allocator_resident;
1302
1303 mem_total += server.initial_memory_usage;
1304
1305 /* Replication backlog and replicas share one global replication buffer,
1306 * only if replication buffer memory is more than the repl backlog setting,
1307 * we consider the excess as replicas' memory. Otherwise, replication buffer
1308 * memory is the consumption of repl backlog. */
1309 if (listLength(server.slaves) &&
1310 (long long)server.repl_buffer_mem > server.repl_backlog_size)
1311 {
1312 mh->clients_slaves = server.repl_buffer_mem - server.repl_backlog_size;
1313 mh->repl_backlog = server.repl_backlog_size;
1314 } else {
1315 mh->clients_slaves = 0;
1316 mh->repl_backlog = server.repl_buffer_mem;
1317 }
1318 if (server.repl_backlog) {
1319 /* The approximate memory of rax tree for indexed blocks. */
1320 mh->repl_backlog +=
1321 server.repl_backlog->blocks_index->numnodes * sizeof(raxNode) +
1322 raxSize(server.repl_backlog->blocks_index) * sizeof(void*);
1323 }
1324
1325 mh->replica_fullsync_buffer = server.repl_full_sync_buffer.mem_used;
1326 mem_total += mh->replica_fullsync_buffer;
1327 mem_total += mh->repl_backlog;
1328 mem_total += mh->clients_slaves;
1329
1330 /* Computing the memory used by the clients would be O(N) if done
1331 * here online. We use our values computed incrementally by
1332 * updateClientMemoryUsage(). */
1333 mh->clients_normal = server.stat_clients_type_memory[CLIENT_TYPE_MASTER]+
1334 server.stat_clients_type_memory[CLIENT_TYPE_PUBSUB]+
1335 server.stat_clients_type_memory[CLIENT_TYPE_NORMAL];
1336 mem_total += mh->clients_normal;
1337
1338 mh->cluster_links = server.stat_cluster_links_memory;
1339 mem_total += mh->cluster_links;
1340
1341 mem = 0;
1342 if (server.aof_state != AOF_OFF) {
1343 mem += sdsZmallocSize(server.aof_buf);
1344 }
1345 mh->aof_buffer = mem;
1346 mem_total+=mem;
1347
1348 mem = evalScriptsMemoryEngine();
1349 mh->eval_caches = mem;
1350 mem_total+=mem;
1351 mh->functions_caches = functionsMemoryEngine();
1352 mem_total+=mh->functions_caches;
1353
1354 mh->script_vm = evalScriptsMemoryVM();
1355 mh->script_vm += functionsMemoryVM();
1356 mem_total+=mh->script_vm;
1357
1358 /* Cluster atomic slot migration buffers. */
1359 mh->asm_import_input_buffer = asmGetImportInputBufferSize();
1360 mh->asm_migrate_output_buffer = asmGetMigrateOutputBufferSize();
1361 mem_total += mh->asm_import_input_buffer;
1362 mem_total += mh->asm_migrate_output_buffer;
1363
1364 for (j = 0; j < server.dbnum; j++) {
1365 redisDb *db = server.db+j;
1366 if (!kvstoreNumAllocatedDicts(db->keys)) continue;
1367
1368 unsigned long long keyscount = kvstoreSize(db->keys);
1369
1370 mh->total_keys += keyscount;
1371 mh->db = zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1));
1372 mh->db[mh->num_dbs].dbid = j;
1373
1374 mem = kvstoreMemUsage(db->keys) +
1375 keyscount * sizeof(robj);
1376 mh->db[mh->num_dbs].overhead_ht_main = mem;
1377 mem_total+=mem;
1378
1379 mem = kvstoreMemUsage(db->expires);
1380 mh->db[mh->num_dbs].overhead_ht_expires = mem;
1381 mem_total+=mem;
1382
1383 mh->num_dbs++;
1384
1385 mh->overhead_db_hashtable_lut += kvstoreOverheadHashtableLut(db->keys);
1386 mh->overhead_db_hashtable_lut += kvstoreOverheadHashtableLut(db->expires);
1387 mh->overhead_db_hashtable_rehashing += kvstoreOverheadHashtableRehashing(db->keys);
1388 mh->overhead_db_hashtable_rehashing += kvstoreOverheadHashtableRehashing(db->expires);
1389 mh->db_dict_rehashing_count += kvstoreDictRehashingCount(db->keys);
1390 mh->db_dict_rehashing_count += kvstoreDictRehashingCount(db->expires);
1391 }
1392
1393 /* Hotkeys memory overhead */
1394 mem_total += hotkeysGetMemoryUsage(server.hotkeys);
1395
1396 mh->overhead_total = mem_total;
1397 mh->dataset = zmalloc_used - mem_total;
1398 mh->peak_perc = (float)zmalloc_used*100/mh->peak_allocated;
1399
1400 /* Metrics computed after subtracting the startup memory from
1401 * the total memory. */
1402 size_t net_usage = 1;
1403 if (zmalloc_used > mh->startup_allocated)
1404 net_usage = zmalloc_used - mh->startup_allocated;
1405 mh->dataset_perc = (float)mh->dataset*100/net_usage;
1406 mh->bytes_per_key = mh->total_keys ? (mh->dataset / mh->total_keys) : 0;
1407
1408 return mh;
1409}
1410
1411/* Helper for "MEMORY allocator-stats", used as a callback for the jemalloc
1412 * stats output. */
1413void inputCatSds(void *result, const char *str) {
1414 /* result is actually a (sds *), so re-cast it here */
1415 sds *info = (sds *)result;
1416 *info = sdscat(*info, str);
1417}
1418
1419/* This implements MEMORY DOCTOR. An human readable analysis of the Redis
1420 * memory condition. */
1421sds getMemoryDoctorReport(void) {
1422 int empty = 0; /* Instance is empty or almost empty. */
1423 int big_peak = 0; /* Memory peak is much larger than used mem. */
1424 int high_frag = 0; /* High fragmentation. */
1425 int high_alloc_frag = 0;/* High allocator fragmentation. */
1426 int high_proc_rss = 0; /* High process rss overhead. */
1427 int high_alloc_rss = 0; /* High rss overhead. */
1428 int big_slave_buf = 0; /* Slave buffers are too big. */
1429 int big_client_buf = 0; /* Client buffers are too big. */
1430 int many_scripts = 0; /* Script cache has too many scripts. */
1431 int num_reports = 0;
1432 struct redisMemOverhead *mh = getMemoryOverheadData();
1433
1434 if (mh->total_allocated < (1024*1024*5)) {
1435 empty = 1;
1436 num_reports++;
1437 } else {
1438 /* Peak is > 150% of current used memory? */
1439 if (((float)mh->peak_allocated / mh->total_allocated) > 1.5) {
1440 big_peak = 1;
1441 num_reports++;
1442 }
1443
1444 /* Fragmentation is higher than 1.4 and 10MB ?*/
1445 if (mh->total_frag > 1.4 && mh->total_frag_bytes > 10<<20) {
1446 high_frag = 1;
1447 num_reports++;
1448 }
1449
1450 /* External fragmentation is higher than 1.1 and 10MB? */
1451 if (mh->allocator_frag > 1.1 && mh->allocator_frag_bytes > 10<<20) {
1452 high_alloc_frag = 1;
1453 num_reports++;
1454 }
1455
1456 /* Allocator rss is higher than 1.1 and 10MB ? */
1457 if (mh->allocator_rss > 1.1 && mh->allocator_rss_bytes > 10<<20) {
1458 high_alloc_rss = 1;
1459 num_reports++;
1460 }
1461
1462 /* Non-Allocator rss is higher than 1.1 and 10MB ? */
1463 if (mh->rss_extra > 1.1 && mh->rss_extra_bytes > 10<<20) {
1464 high_proc_rss = 1;
1465 num_reports++;
1466 }
1467
1468 /* Clients using more than 200k each average? */
1469 long numslaves = listLength(server.slaves);
1470 long numclients = listLength(server.clients)-numslaves;
1471 if (mh->clients_normal / numclients > (1024*200)) {
1472 big_client_buf = 1;
1473 num_reports++;
1474 }
1475
1476 /* Slaves using more than 10 MB each? */
1477 if (numslaves > 0 && mh->clients_slaves > (1024*1024*10)) {
1478 big_slave_buf = 1;
1479 num_reports++;
1480 }
1481
1482 /* Too many scripts are cached? */
1483 if (dictSize(evalScriptsDict()) > 1000) {
1484 many_scripts = 1;
1485 num_reports++;
1486 }
1487 }
1488
1489 sds s;
1490 if (num_reports == 0) {
1491 s = sdsnew(
1492 "Hi Sam, I can't find any memory issue in your instance. "
1493 "I can only account for what occurs on this base.\n");
1494 } else if (empty == 1) {
1495 s = sdsnew(
1496 "Hi Sam, this instance is empty or is using very little memory, "
1497 "my issues detector can't be used in these conditions. "
1498 "Please, leave for your mission on Earth and fill it with some data. "
1499 "The new Sam and I will be back to our programming as soon as I "
1500 "finished rebooting.\n");
1501 } else {
1502 s = sdsnew("Sam, I detected a few issues in this Redis instance memory implants:\n\n");
1503 if (big_peak) {
1504 s = sdscat(s," * Peak memory: In the past this instance used more than 150% the memory that is currently using. The allocator is normally not able to release memory after a peak, so you can expect to see a big fragmentation ratio, however this is actually harmless and is only due to the memory peak, and if the Redis instance Resident Set Size (RSS) is currently bigger than expected, the memory will be used as soon as you fill the Redis instance with more data. If the memory peak was only occasional and you want to try to reclaim memory, please try the MEMORY PURGE command, otherwise the only other option is to shutdown and restart the instance.\n\n");
1505 }
1506 if (high_frag) {
1507 s = sdscatprintf(s," * High total RSS: This instance has a memory fragmentation and RSS overhead greater than 1.4 (this means that the Resident Set Size of the Redis process is much larger than the sum of the logical allocations Redis performed). This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. If the problem is a large peak memory, then there is no issue. Otherwise, make sure you are using the Jemalloc allocator and not the default libc malloc. Note: The currently used allocator is \"%s\".\n\n", ZMALLOC_LIB);
1508 }
1509 if (high_alloc_frag) {
1510 s = sdscatprintf(s," * High allocator fragmentation: This instance has an allocator external fragmentation greater than 1.1. This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. You can try enabling 'activedefrag' config option.\n\n");
1511 }
1512 if (high_alloc_rss) {
1513 s = sdscatprintf(s," * High allocator RSS overhead: This instance has an RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the allocator is much larger than the sum what the allocator actually holds). This problem is usually due to a large peak memory (check if there is a peak memory entry above in the report), you can try the MEMORY PURGE command to reclaim it.\n\n");
1514 }
1515 if (high_proc_rss) {
1516 s = sdscatprintf(s," * High process RSS overhead: This instance has non-allocator RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the Redis process is much larger than the RSS the allocator holds). This problem may be due to Lua scripts or Modules.\n\n");
1517 }
1518 if (big_slave_buf) {
1519 s = sdscat(s," * Big replica buffers: The replica output buffers in this instance are greater than 10MB for each replica (on average). This likely means that there is some replica instance that is struggling receiving data, either because it is too slow or because of networking issues. As a result, data piles on the master output buffers. Please try to identify what replica is not receiving data correctly and why. You can use the INFO output in order to check the replicas delays and the CLIENT LIST command to check the output buffers of each replica.\n\n");
1520 }
1521 if (big_client_buf) {
1522 s = sdscat(s," * Big client buffers: The clients output buffers in this instance are greater than 200K per client (on average). This may result from different causes, like Pub/Sub clients subscribed to channels bot not receiving data fast enough, so that data piles on the Redis instance output buffer, or clients sending commands with large replies or very large sequences of commands in the same pipeline. Please use the CLIENT LIST command in order to investigate the issue if it causes problems in your instance, or to understand better why certain clients are using a big amount of memory.\n\n");
1523 }
1524 if (many_scripts) {
1525 s = sdscat(s," * Many scripts: There seem to be many cached scripts in this instance (more than 1000). This may be because scripts are generated and `EVAL`ed, instead of being parameterized (with KEYS and ARGV), `SCRIPT LOAD`ed and `EVALSHA`ed. Unless `SCRIPT FLUSH` is called periodically, the scripts' caches may end up consuming most of your memory.\n\n");
1526 }
1527 s = sdscat(s,"I'm here to keep you safe, Sam. I want to help you.\n");
1528 }
1529 freeMemoryOverheadData(mh);
1530 return s;
1531}
1532
1533/* Set the object LRU/LFU depending on server.maxmemory_policy.
1534 * The lfu_freq arg is only relevant if policy is MAXMEMORY_FLAG_LFU.
1535 * The lru_idle and lru_clock args are only relevant if policy
1536 * is MAXMEMORY_FLAG_LRU.
1537 * Either or both of them may be <0, in that case, nothing is set. */
1538int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
1539 long long lru_clock, int lru_multiplier) {
1540 if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
1541 if (lfu_freq >= 0) {
1542 serverAssert(lfu_freq <= 255);
1543 val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;
1544 return 1;
1545 }
1546 } else if (lru_idle >= 0) {
1547 /* Provided LRU idle time is in seconds. Scale
1548 * according to the LRU clock resolution this Redis
1549 * instance was compiled with (normally 1000 ms, so the
1550 * below statement will expand to lru_idle*1000/1000. */
1551 lru_idle = lru_idle*lru_multiplier/LRU_CLOCK_RESOLUTION;
1552 long lru_abs = lru_clock - lru_idle; /* Absolute access time. */
1553 /* If the LRU field underflows (since lru_clock is a wrapping clock),
1554 * we need to make it positive again. This will be handled by the unwrapping
1555 * code in estimateObjectIdleTime. I.e. imagine a day when lru_clock
1556 * wrap arounds (happens once in some 6 months), and becomes a low
1557 * value, like 10, an lru_idle of 1000 should be near LRU_CLOCK_MAX. */
1558 if (lru_abs < 0)
1559 lru_abs += LRU_CLOCK_MAX;
1560 val->lru = lru_abs;
1561 return 1;
1562 }
1563 return 0;
1564}
1565
1566/* ======================= The OBJECT and MEMORY commands =================== */
1567
1568/* This is a helper function for the OBJECT command. We need to lookup keys
1569 * without any modification of LRU or other parameters. */
1570kvobj *kvobjCommandLookup(client *c, robj *key) {
1571 return lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH|LOOKUP_NONOTIFY);
1572}
1573
1574kvobj *kvobjCommandLookupOrReply(client *c, robj *key, robj *reply) {
1575 kvobj *kv = kvobjCommandLookup(c,key);
1576 if (!kv) addReplyOrErrorObject(c, reply);
1577 return kv;
1578}
1579
1580/* Object command allows to inspect the internals of a Redis Object.
1581 * Usage: OBJECT <refcount|encoding|idletime|freq> <key> */
1582void objectCommand(client *c) {
1583 kvobj *kv;
1584
1585 if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
1586 const char *help[] = {
1587"ENCODING <key>",
1588" Return the kind of internal representation used in order to store the value",
1589" associated with a <key>.",
1590"FREQ <key>",
1591" Return the access frequency index of the <key>. The returned integer is",
1592" proportional to the logarithm of the recent access frequency of the key.",
1593"IDLETIME <key>",
1594" Return the idle time of the <key>, that is the approximated number of",
1595" seconds elapsed since the last access to the key.",
1596"REFCOUNT <key>",
1597" Return the number of references of the value associated with the specified",
1598" <key>.",
1599NULL
1600 };
1601 addReplyHelp(c, help);
1602 } else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
1603 if ((kv = kvobjCommandLookupOrReply(c, c->argv[2], shared.null[c->resp]))
1604 == NULL) return;
1605 addReplyLongLong(c, kv->refcount);
1606 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
1607 if ((kv = kvobjCommandLookupOrReply(c, c->argv[2], shared.null[c->resp]))
1608 == NULL) return;
1609 addReplyBulkCString(c,strEncoding(kv->encoding));
1610 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
1611 if ((kv = kvobjCommandLookupOrReply(c, c->argv[2], shared.null[c->resp]))
1612 == NULL) return;
1613 if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
1614 addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
1615 return;
1616 }
1617 addReplyLongLong(c, estimateObjectIdleTime(kv) / 1000);
1618 } else if (!strcasecmp(c->argv[1]->ptr,"freq") && c->argc == 3) {
1619 if ((kv = kvobjCommandLookupOrReply(c, c->argv[2], shared.null[c->resp]))
1620 == NULL) return;
1621 if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
1622 addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
1623 return;
1624 }
1625 /* LFUDecrAndReturn should be called
1626 * in case of the key has not been accessed for a long time,
1627 * because we update the access time only
1628 * when the key is read or overwritten. */
1629 addReplyLongLong(c,LFUDecrAndReturn(kv));
1630 } else {
1631 addReplySubcommandSyntaxError(c);
1632 }
1633}
1634
1635/* The memory command will eventually be a complete interface for the
1636 * memory introspection capabilities of Redis.
1637 *
1638 * Usage: MEMORY usage <key> */
1639void memoryCommand(client *c) {
1640 if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) {
1641 const char *help[] = {
1642"DOCTOR",
1643" Return memory problems reports.",
1644"MALLOC-STATS",
1645" Return internal statistics report from the memory allocator.",
1646"PURGE",
1647" Attempt to purge dirty pages for reclamation by the allocator.",
1648"STATS",
1649" Return information about the memory usage of the server.",
1650"USAGE <key> [SAMPLES <count>]",
1651" Return memory in bytes used by <key> and its value. Nested values are",
1652" sampled up to <count> times (default: 5, 0 means sample all).",
1653NULL
1654 };
1655 addReplyHelp(c, help);
1656 } else if (!strcasecmp(c->argv[1]->ptr,"usage") && c->argc >= 3) {
1657 kvobj *kv;
1658 long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;
1659 for (int j = 3; j < c->argc; j++) {
1660 if (!strcasecmp(c->argv[j]->ptr,"samples") &&
1661 j+1 < c->argc)
1662 {
1663 if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL)
1664 == C_ERR) return;
1665 if (samples < 0) {
1666 addReplyErrorObject(c,shared.syntaxerr);
1667 return;
1668 }
1669 if (samples == 0) samples = LLONG_MAX;
1670 j++; /* skip option argument. */
1671 } else {
1672 addReplyErrorObject(c,shared.syntaxerr);
1673 return;
1674 }
1675 }
1676 if ((kv = dbFind(c->db, c->argv[2]->ptr)) == NULL) {
1677 addReplyNull(c);
1678 return;
1679 }
1680 size_t usage = kvobjComputeSize(c->argv[2], kv, samples, c->db->id);
1681 addReplyLongLong(c,usage);
1682 } else if (!strcasecmp(c->argv[1]->ptr,"stats") && c->argc == 2) {
1683 struct redisMemOverhead *mh = getMemoryOverheadData();
1684
1685 addReplyMapLen(c,33+mh->num_dbs);
1686
1687 addReplyBulkCString(c,"peak.allocated");
1688 addReplyLongLong(c,mh->peak_allocated);
1689
1690 addReplyBulkCString(c,"total.allocated");
1691 addReplyLongLong(c,mh->total_allocated);
1692
1693 addReplyBulkCString(c,"startup.allocated");
1694 addReplyLongLong(c,mh->startup_allocated);
1695
1696 addReplyBulkCString(c,"replication.backlog");
1697 addReplyLongLong(c,mh->repl_backlog);
1698
1699 addReplyBulkCString(c,"replica.fullsync.buffer");
1700 addReplyLongLong(c,mh->replica_fullsync_buffer);
1701
1702 addReplyBulkCString(c,"clients.slaves");
1703 addReplyLongLong(c,mh->clients_slaves);
1704
1705 addReplyBulkCString(c,"clients.normal");
1706 addReplyLongLong(c,mh->clients_normal);
1707
1708 addReplyBulkCString(c,"cluster.links");
1709 addReplyLongLong(c,mh->cluster_links);
1710
1711 addReplyBulkCString(c,"aof.buffer");
1712 addReplyLongLong(c,mh->aof_buffer);
1713
1714 addReplyBulkCString(c,"lua.caches");
1715 addReplyLongLong(c,mh->eval_caches);
1716
1717 addReplyBulkCString(c,"functions.caches");
1718 addReplyLongLong(c,mh->functions_caches);
1719
1720 addReplyBulkCString(c,"script.VMs");
1721 addReplyLongLong(c,mh->script_vm);
1722
1723 for (size_t j = 0; j < mh->num_dbs; j++) {
1724 char dbname[32];
1725 snprintf(dbname,sizeof(dbname),"db.%zd",mh->db[j].dbid);
1726 addReplyBulkCString(c,dbname);
1727 addReplyMapLen(c,2);
1728
1729 addReplyBulkCString(c,"overhead.hashtable.main");
1730 addReplyLongLong(c,mh->db[j].overhead_ht_main);
1731
1732 addReplyBulkCString(c,"overhead.hashtable.expires");
1733 addReplyLongLong(c,mh->db[j].overhead_ht_expires);
1734 }
1735
1736 addReplyBulkCString(c,"overhead.db.hashtable.lut");
1737 addReplyLongLong(c, mh->overhead_db_hashtable_lut);
1738
1739 addReplyBulkCString(c,"overhead.db.hashtable.rehashing");
1740 addReplyLongLong(c, mh->overhead_db_hashtable_rehashing);
1741
1742 addReplyBulkCString(c,"overhead.total");
1743 addReplyLongLong(c,mh->overhead_total);
1744
1745 addReplyBulkCString(c,"db.dict.rehashing.count");
1746 addReplyLongLong(c, mh->db_dict_rehashing_count);
1747
1748 addReplyBulkCString(c,"keys.count");
1749 addReplyLongLong(c,mh->total_keys);
1750
1751 addReplyBulkCString(c,"keys.bytes-per-key");
1752 addReplyLongLong(c,mh->bytes_per_key);
1753
1754 addReplyBulkCString(c,"dataset.bytes");
1755 addReplyLongLong(c,mh->dataset);
1756
1757 addReplyBulkCString(c,"dataset.percentage");
1758 addReplyDouble(c,mh->dataset_perc);
1759
1760 addReplyBulkCString(c,"peak.percentage");
1761 addReplyDouble(c,mh->peak_perc);
1762
1763 addReplyBulkCString(c,"allocator.allocated");
1764 addReplyLongLong(c,server.cron_malloc_stats.allocator_allocated);
1765
1766 addReplyBulkCString(c,"allocator.active");
1767 addReplyLongLong(c,server.cron_malloc_stats.allocator_active);
1768
1769 addReplyBulkCString(c,"allocator.resident");
1770 addReplyLongLong(c,server.cron_malloc_stats.allocator_resident);
1771
1772 addReplyBulkCString(c,"allocator.muzzy");
1773 addReplyLongLong(c,server.cron_malloc_stats.allocator_muzzy);
1774
1775 addReplyBulkCString(c,"allocator-fragmentation.ratio");
1776 addReplyDouble(c,mh->allocator_frag);
1777
1778 addReplyBulkCString(c,"allocator-fragmentation.bytes");
1779 addReplyLongLong(c,mh->allocator_frag_bytes);
1780
1781 addReplyBulkCString(c,"allocator-rss.ratio");
1782 addReplyDouble(c,mh->allocator_rss);
1783
1784 addReplyBulkCString(c,"allocator-rss.bytes");
1785 addReplyLongLong(c,mh->allocator_rss_bytes);
1786
1787 addReplyBulkCString(c,"rss-overhead.ratio");
1788 addReplyDouble(c,mh->rss_extra);
1789
1790 addReplyBulkCString(c,"rss-overhead.bytes");
1791 addReplyLongLong(c,mh->rss_extra_bytes);
1792
1793 addReplyBulkCString(c,"fragmentation"); /* this is the total RSS overhead, including fragmentation */
1794 addReplyDouble(c,mh->total_frag); /* it is kept here for backwards compatibility */
1795
1796 addReplyBulkCString(c,"fragmentation.bytes");
1797 addReplyLongLong(c,mh->total_frag_bytes);
1798
1799 freeMemoryOverheadData(mh);
1800 } else if (!strcasecmp(c->argv[1]->ptr,"malloc-stats") && c->argc == 2) {
1801#if defined(USE_JEMALLOC)
1802 sds info = sdsempty();
1803 je_malloc_stats_print(inputCatSds, &info, NULL);
1804 addReplyVerbatim(c,info,sdslen(info),"txt");
1805 sdsfree(info);
1806#else
1807 addReplyBulkCString(c,"Stats not supported for the current allocator");
1808#endif
1809 } else if (!strcasecmp(c->argv[1]->ptr,"doctor") && c->argc == 2) {
1810 sds report = getMemoryDoctorReport();
1811 addReplyVerbatim(c,report,sdslen(report),"txt");
1812 sdsfree(report);
1813 } else if (!strcasecmp(c->argv[1]->ptr,"purge") && c->argc == 2) {
1814 if (jemalloc_purge() == 0)
1815 addReply(c, shared.ok);
1816 else
1817 addReplyError(c, "Error purging dirty pages");
1818 } else {
1819 addReplySubcommandSyntaxError(c);
1820 }
1821}