1 /* 2 * Active memory defragmentation 3 * Try to find key / value allocations that need to be re-allocated in order 4 * to reduce external fragmentation. 5 * We do that by scanning the keyspace and for each pointer we have, we can try to 6 * ask the allocator if moving it to a new address will help reduce fragmentation. 7 * 8 * Copyright (c) 2017, Oran Agra 9 * Copyright (c) 2017, Redis Labs, Inc 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions are met: 14 * 15 * * Redistributions of source code must retain the above copyright notice, 16 * this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of Redis nor the names of its contributors may be used 21 * to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include "server.h" 38 #include <time.h> 39 #include <assert.h> 40 #include <stddef.h> 41 42 #ifdef HAVE_DEFRAG 43 44 /* this method was added to jemalloc in order to help us understand which 45 * pointers are worthwhile moving and which aren't */ 46 int je_get_defrag_hint(void* ptr, int *bin_util, int *run_util); 47 48 /* forward declarations*/ 49 void defragDictBucketCallback(void *privdata, dictEntry **bucketref); 50 dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged); 51 52 /* Defrag helper for generic allocations. 53 * 54 * returns NULL in case the allocatoin wasn't moved. 55 * when it returns a non-null value, the old pointer was already released 56 * and should NOT be accessed. */ 57 void* activeDefragAlloc(void *ptr) { 58 int bin_util, run_util; 59 size_t size; 60 void *newptr; 61 if(!je_get_defrag_hint(ptr, &bin_util, &run_util)) { 62 server.stat_active_defrag_misses++; 63 return NULL; 64 } 65 /* if this run is more utilized than the average utilization in this bin 66 * (or it is full), skip it. This will eventually move all the allocations 67 * from relatively empty runs into relatively full runs. */ 68 if (run_util > bin_util || run_util == 1<<16) { 69 server.stat_active_defrag_misses++; 70 return NULL; 71 } 72 /* move this allocation to a new allocation. 73 * make sure not to use the thread cache. so that we don't get back the same 74 * pointers we try to free */ 75 size = zmalloc_size(ptr); 76 newptr = zmalloc_no_tcache(size); 77 memcpy(newptr, ptr, size); 78 zfree_no_tcache(ptr); 79 return newptr; 80 } 81 82 /*Defrag helper for sds strings 83 * 84 * returns NULL in case the allocatoin wasn't moved. 85 * when it returns a non-null value, the old pointer was already released 86 * and should NOT be accessed. */ 87 sds activeDefragSds(sds sdsptr) { 88 void* ptr = sdsAllocPtr(sdsptr); 89 void* newptr = activeDefragAlloc(ptr); 90 if (newptr) { 91 size_t offset = sdsptr - (char*)ptr; 92 sdsptr = (char*)newptr + offset; 93 return sdsptr; 94 } 95 return NULL; 96 } 97 98 /* Defrag helper for robj and/or string objects 99 * 100 * returns NULL in case the allocatoin wasn't moved. 101 * when it returns a non-null value, the old pointer was already released 102 * and should NOT be accessed. */ 103 robj *activeDefragStringOb(robj* ob, long *defragged) { 104 robj *ret = NULL; 105 if (ob->refcount!=1) 106 return NULL; 107 108 /* try to defrag robj (only if not an EMBSTR type (handled below). */ 109 if (ob->type!=OBJ_STRING || ob->encoding!=OBJ_ENCODING_EMBSTR) { 110 if ((ret = activeDefragAlloc(ob))) { 111 ob = ret; 112 (*defragged)++; 113 } 114 } 115 116 /* try to defrag string object */ 117 if (ob->type == OBJ_STRING) { 118 if(ob->encoding==OBJ_ENCODING_RAW) { 119 sds newsds = activeDefragSds((sds)ob->ptr); 120 if (newsds) { 121 ob->ptr = newsds; 122 (*defragged)++; 123 } 124 } else if (ob->encoding==OBJ_ENCODING_EMBSTR) { 125 /* The sds is embedded in the object allocation, calculate the 126 * offset and update the pointer in the new allocation. */ 127 long ofs = (intptr_t)ob->ptr - (intptr_t)ob; 128 if ((ret = activeDefragAlloc(ob))) { 129 ret->ptr = (void*)((intptr_t)ret + ofs); 130 (*defragged)++; 131 } 132 } else if (ob->encoding!=OBJ_ENCODING_INT) { 133 serverPanic("Unknown string encoding"); 134 } 135 } 136 return ret; 137 } 138 139 /* Defrag helper for dictEntries to be used during dict iteration (called on 140 * each step). Teturns a stat of how many pointers were moved. */ 141 long dictIterDefragEntry(dictIterator *iter) { 142 /* This function is a little bit dirty since it messes with the internals 143 * of the dict and it's iterator, but the benefit is that it is very easy 144 * to use, and require no other chagnes in the dict. */ 145 long defragged = 0; 146 dictht *ht; 147 /* Handle the next entry (if there is one), and update the pointer in the 148 * current entry. */ 149 if (iter->nextEntry) { 150 dictEntry *newde = activeDefragAlloc(iter->nextEntry); 151 if (newde) { 152 defragged++; 153 iter->nextEntry = newde; 154 iter->entry->next = newde; 155 } 156 } 157 /* handle the case of the first entry in the hash bucket. */ 158 ht = &iter->d->ht[iter->table]; 159 if (ht->table[iter->index] == iter->entry) { 160 dictEntry *newde = activeDefragAlloc(iter->entry); 161 if (newde) { 162 iter->entry = newde; 163 ht->table[iter->index] = newde; 164 defragged++; 165 } 166 } 167 return defragged; 168 } 169 170 /* Defrag helper for dict main allocations (dict struct, and hash tables). 171 * receives a pointer to the dict* and implicitly updates it when the dict 172 * struct itself was moved. Returns a stat of how many pointers were moved. */ 173 long dictDefragTables(dict* d) { 174 dictEntry **newtable; 175 long defragged = 0; 176 /* handle the first hash table */ 177 newtable = activeDefragAlloc(d->ht[0].table); 178 if (newtable) 179 defragged++, d->ht[0].table = newtable; 180 /* handle the second hash table */ 181 if (d->ht[1].table) { 182 newtable = activeDefragAlloc(d->ht[1].table); 183 if (newtable) 184 defragged++, d->ht[1].table = newtable; 185 } 186 return defragged; 187 } 188 189 /* Internal function used by zslDefrag */ 190 void zslUpdateNode(zskiplist *zsl, zskiplistNode *oldnode, zskiplistNode *newnode, zskiplistNode **update) { 191 int i; 192 for (i = 0; i < zsl->level; i++) { 193 if (update[i]->level[i].forward == oldnode) 194 update[i]->level[i].forward = newnode; 195 } 196 serverAssert(zsl->header!=oldnode); 197 if (newnode->level[0].forward) { 198 serverAssert(newnode->level[0].forward->backward==oldnode); 199 newnode->level[0].forward->backward = newnode; 200 } else { 201 serverAssert(zsl->tail==oldnode); 202 zsl->tail = newnode; 203 } 204 } 205 206 /* Defrag helper for sorted set. 207 * Update the robj pointer, defrag the skiplist struct and return the new score 208 * reference. We may not access oldele pointer (not even the pointer stored in 209 * the skiplist), as it was already freed. Newele may be null, in which case we 210 * only need to defrag the skiplist, but not update the obj pointer. 211 * When return value is non-NULL, it is the score reference that must be updated 212 * in the dict record. */ 213 double *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) { 214 zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x, *newx; 215 int i; 216 sds ele = newele? newele: oldele; 217 218 /* find the skiplist node referring to the object that was moved, 219 * and all pointers that need to be updated if we'll end up moving the skiplist node. */ 220 x = zsl->header; 221 for (i = zsl->level-1; i >= 0; i--) { 222 while (x->level[i].forward && 223 x->level[i].forward->ele != oldele && /* make sure not to access the 224 ->obj pointer if it matches 225 oldele */ 226 (x->level[i].forward->score < score || 227 (x->level[i].forward->score == score && 228 sdscmp(x->level[i].forward->ele,ele) < 0))) 229 x = x->level[i].forward; 230 update[i] = x; 231 } 232 233 /* update the robj pointer inside the skip list record. */ 234 x = x->level[0].forward; 235 serverAssert(x && score == x->score && x->ele==oldele); 236 if (newele) 237 x->ele = newele; 238 239 /* try to defrag the skiplist record itself */ 240 newx = activeDefragAlloc(x); 241 if (newx) { 242 zslUpdateNode(zsl, x, newx, update); 243 return &newx->score; 244 } 245 return NULL; 246 } 247 248 /* Defrag helpler for sorted set. 249 * Defrag a single dict entry key name, and corresponding skiplist struct */ 250 long activeDefragZsetEntry(zset *zs, dictEntry *de) { 251 sds newsds; 252 double* newscore; 253 long defragged = 0; 254 sds sdsele = dictGetKey(de); 255 if ((newsds = activeDefragSds(sdsele))) 256 defragged++, de->key = newsds; 257 newscore = zslDefrag(zs->zsl, *(double*)dictGetVal(de), sdsele, newsds); 258 if (newscore) { 259 dictSetVal(zs->dict, de, newscore); 260 defragged++; 261 } 262 return defragged; 263 } 264 265 #define DEFRAG_SDS_DICT_NO_VAL 0 266 #define DEFRAG_SDS_DICT_VAL_IS_SDS 1 267 #define DEFRAG_SDS_DICT_VAL_IS_STROB 2 268 #define DEFRAG_SDS_DICT_VAL_VOID_PTR 3 269 270 /* Defrag a dict with sds key and optional value (either ptr, sds or robj string) */ 271 long activeDefragSdsDict(dict* d, int val_type) { 272 dictIterator *di; 273 dictEntry *de; 274 long defragged = 0; 275 di = dictGetIterator(d); 276 while((de = dictNext(di)) != NULL) { 277 sds sdsele = dictGetKey(de), newsds; 278 if ((newsds = activeDefragSds(sdsele))) 279 de->key = newsds, defragged++; 280 /* defrag the value */ 281 if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) { 282 sdsele = dictGetVal(de); 283 if ((newsds = activeDefragSds(sdsele))) 284 de->v.val = newsds, defragged++; 285 } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) { 286 robj *newele, *ele = dictGetVal(de); 287 if ((newele = activeDefragStringOb(ele, &defragged))) 288 de->v.val = newele; 289 } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) { 290 void *newptr, *ptr = dictGetVal(de); 291 if ((newptr = activeDefragAlloc(ptr))) 292 de->v.val = newptr, defragged++; 293 } 294 defragged += dictIterDefragEntry(di); 295 } 296 dictReleaseIterator(di); 297 return defragged; 298 } 299 300 /* Defrag a list of ptr, sds or robj string values */ 301 long activeDefragList(list *l, int val_type) { 302 long defragged = 0; 303 listNode *ln, *newln; 304 for (ln = l->head; ln; ln = ln->next) { 305 if ((newln = activeDefragAlloc(ln))) { 306 if (newln->prev) 307 newln->prev->next = newln; 308 else 309 l->head = newln; 310 if (newln->next) 311 newln->next->prev = newln; 312 else 313 l->tail = newln; 314 ln = newln; 315 defragged++; 316 } 317 if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) { 318 sds newsds, sdsele = ln->value; 319 if ((newsds = activeDefragSds(sdsele))) 320 ln->value = newsds, defragged++; 321 } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) { 322 robj *newele, *ele = ln->value; 323 if ((newele = activeDefragStringOb(ele, &defragged))) 324 ln->value = newele; 325 } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) { 326 void *newptr, *ptr = ln->value; 327 if ((newptr = activeDefragAlloc(ptr))) 328 ln->value = newptr, defragged++; 329 } 330 } 331 return defragged; 332 } 333 334 /* Defrag a list of sds values and a dict with the same sds keys */ 335 long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) { 336 long defragged = 0; 337 sds newsds, sdsele; 338 listNode *ln, *newln; 339 dictIterator *di; 340 dictEntry *de; 341 /* Defrag the list and it's sds values */ 342 for (ln = l->head; ln; ln = ln->next) { 343 if ((newln = activeDefragAlloc(ln))) { 344 if (newln->prev) 345 newln->prev->next = newln; 346 else 347 l->head = newln; 348 if (newln->next) 349 newln->next->prev = newln; 350 else 351 l->tail = newln; 352 ln = newln; 353 defragged++; 354 } 355 sdsele = ln->value; 356 if ((newsds = activeDefragSds(sdsele))) { 357 /* When defragging an sds value, we need to update the dict key */ 358 uint64_t hash = dictGetHash(d, sdsele); 359 replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged); 360 ln->value = newsds; 361 defragged++; 362 } 363 } 364 365 /* Defrag the dict values (keys were already handled) */ 366 di = dictGetIterator(d); 367 while((de = dictNext(di)) != NULL) { 368 if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) { 369 sds newsds, sdsele = dictGetVal(de); 370 if ((newsds = activeDefragSds(sdsele))) 371 de->v.val = newsds, defragged++; 372 } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) { 373 robj *newele, *ele = dictGetVal(de); 374 if ((newele = activeDefragStringOb(ele, &defragged))) 375 de->v.val = newele, defragged++; 376 } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) { 377 void *newptr, *ptr = ln->value; 378 if ((newptr = activeDefragAlloc(ptr))) 379 ln->value = newptr, defragged++; 380 } 381 defragged += dictIterDefragEntry(di); 382 } 383 dictReleaseIterator(di); 384 385 return defragged; 386 } 387 388 /* Utility function that replaces an old key pointer in the dictionary with a 389 * new pointer. Additionally, we try to defrag the dictEntry in that dict. 390 * Oldkey mey be a dead pointer and should not be accessed (we get a 391 * pre-calculated hash value). Newkey may be null if the key pointer wasn't 392 * moved. Return value is the the dictEntry if found, or NULL if not found. 393 * NOTE: this is very ugly code, but it let's us avoid the complication of 394 * doing a scan on another dict. */ 395 dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) { 396 dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash); 397 if (deref) { 398 dictEntry *de = *deref; 399 dictEntry *newde = activeDefragAlloc(de); 400 if (newde) { 401 de = *deref = newde; 402 (*defragged)++; 403 } 404 if (newkey) 405 de->key = newkey; 406 return de; 407 } 408 return NULL; 409 } 410 411 long activeDefragQuickListNodes(quicklist *ql) { 412 quicklistNode *node = ql->head, *newnode; 413 long defragged = 0; 414 unsigned char *newzl; 415 while (node) { 416 if ((newnode = activeDefragAlloc(node))) { 417 if (newnode->prev) 418 newnode->prev->next = newnode; 419 else 420 ql->head = newnode; 421 if (newnode->next) 422 newnode->next->prev = newnode; 423 else 424 ql->tail = newnode; 425 node = newnode; 426 defragged++; 427 } 428 if ((newzl = activeDefragAlloc(node->zl))) 429 defragged++, node->zl = newzl; 430 node = node->next; 431 } 432 return defragged; 433 } 434 435 /* when the value has lots of elements, we want to handle it later and not as 436 * oart of the main dictionary scan. this is needed in order to prevent latency 437 * spikes when handling large items */ 438 void defragLater(redisDb *db, dictEntry *kde) { 439 sds key = sdsdup(dictGetKey(kde)); 440 listAddNodeTail(db->defrag_later, key); 441 } 442 443 long scanLaterList(robj *ob) { 444 quicklist *ql = ob->ptr; 445 if (ob->type != OBJ_LIST || ob->encoding != OBJ_ENCODING_QUICKLIST) 446 return 0; 447 server.stat_active_defrag_scanned+=ql->len; 448 return activeDefragQuickListNodes(ql); 449 } 450 451 typedef struct { 452 zset *zs; 453 long defragged; 454 } scanLaterZsetData; 455 456 void scanLaterZsetCallback(void *privdata, const dictEntry *_de) { 457 dictEntry *de = (dictEntry*)_de; 458 scanLaterZsetData *data = privdata; 459 data->defragged += activeDefragZsetEntry(data->zs, de); 460 server.stat_active_defrag_scanned++; 461 } 462 463 long scanLaterZset(robj *ob, unsigned long *cursor) { 464 if (ob->type != OBJ_ZSET || ob->encoding != OBJ_ENCODING_SKIPLIST) 465 return 0; 466 zset *zs = (zset*)ob->ptr; 467 dict *d = zs->dict; 468 scanLaterZsetData data = {zs, 0}; 469 *cursor = dictScan(d, *cursor, scanLaterZsetCallback, defragDictBucketCallback, &data); 470 return data.defragged; 471 } 472 473 void scanLaterSetCallback(void *privdata, const dictEntry *_de) { 474 dictEntry *de = (dictEntry*)_de; 475 long *defragged = privdata; 476 sds sdsele = dictGetKey(de), newsds; 477 if ((newsds = activeDefragSds(sdsele))) 478 (*defragged)++, de->key = newsds; 479 server.stat_active_defrag_scanned++; 480 } 481 482 long scanLaterSet(robj *ob, unsigned long *cursor) { 483 long defragged = 0; 484 if (ob->type != OBJ_SET || ob->encoding != OBJ_ENCODING_HT) 485 return 0; 486 dict *d = ob->ptr; 487 *cursor = dictScan(d, *cursor, scanLaterSetCallback, defragDictBucketCallback, &defragged); 488 return defragged; 489 } 490 491 void scanLaterHashCallback(void *privdata, const dictEntry *_de) { 492 dictEntry *de = (dictEntry*)_de; 493 long *defragged = privdata; 494 sds sdsele = dictGetKey(de), newsds; 495 if ((newsds = activeDefragSds(sdsele))) 496 (*defragged)++, de->key = newsds; 497 sdsele = dictGetVal(de); 498 if ((newsds = activeDefragSds(sdsele))) 499 (*defragged)++, de->v.val = newsds; 500 server.stat_active_defrag_scanned++; 501 } 502 503 long scanLaterHash(robj *ob, unsigned long *cursor) { 504 long defragged = 0; 505 if (ob->type != OBJ_HASH || ob->encoding != OBJ_ENCODING_HT) 506 return 0; 507 dict *d = ob->ptr; 508 *cursor = dictScan(d, *cursor, scanLaterHashCallback, defragDictBucketCallback, &defragged); 509 return defragged; 510 } 511 512 long defragQuicklist(redisDb *db, dictEntry *kde) { 513 robj *ob = dictGetVal(kde); 514 long defragged = 0; 515 quicklist *ql = ob->ptr, *newql; 516 serverAssert(ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST); 517 if ((newql = activeDefragAlloc(ql))) 518 defragged++, ob->ptr = ql = newql; 519 if (ql->len > server.active_defrag_max_scan_fields) 520 defragLater(db, kde); 521 else 522 defragged += activeDefragQuickListNodes(ql); 523 return defragged; 524 } 525 526 long defragZsetSkiplist(redisDb *db, dictEntry *kde) { 527 robj *ob = dictGetVal(kde); 528 long defragged = 0; 529 zset *zs = (zset*)ob->ptr; 530 zset *newzs; 531 zskiplist *newzsl; 532 dict *newdict; 533 dictEntry *de; 534 struct zskiplistNode *newheader; 535 serverAssert(ob->type == OBJ_ZSET && ob->encoding == OBJ_ENCODING_SKIPLIST); 536 if ((newzs = activeDefragAlloc(zs))) 537 defragged++, ob->ptr = zs = newzs; 538 if ((newzsl = activeDefragAlloc(zs->zsl))) 539 defragged++, zs->zsl = newzsl; 540 if ((newheader = activeDefragAlloc(zs->zsl->header))) 541 defragged++, zs->zsl->header = newheader; 542 if (dictSize(zs->dict) > server.active_defrag_max_scan_fields) 543 defragLater(db, kde); 544 else { 545 dictIterator *di = dictGetIterator(zs->dict); 546 while((de = dictNext(di)) != NULL) { 547 defragged += activeDefragZsetEntry(zs, de); 548 } 549 dictReleaseIterator(di); 550 } 551 /* handle the dict struct */ 552 if ((newdict = activeDefragAlloc(zs->dict))) 553 defragged++, zs->dict = newdict; 554 /* defrag the dict tables */ 555 defragged += dictDefragTables(zs->dict); 556 return defragged; 557 } 558 559 long defragHash(redisDb *db, dictEntry *kde) { 560 long defragged = 0; 561 robj *ob = dictGetVal(kde); 562 dict *d, *newd; 563 serverAssert(ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HT); 564 d = ob->ptr; 565 if (dictSize(d) > server.active_defrag_max_scan_fields) 566 defragLater(db, kde); 567 else 568 defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_VAL_IS_SDS); 569 /* handle the dict struct */ 570 if ((newd = activeDefragAlloc(ob->ptr))) 571 defragged++, ob->ptr = newd; 572 /* defrag the dict tables */ 573 defragged += dictDefragTables(ob->ptr); 574 return defragged; 575 } 576 577 long defragSet(redisDb *db, dictEntry *kde) { 578 long defragged = 0; 579 robj *ob = dictGetVal(kde); 580 dict *d, *newd; 581 serverAssert(ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HT); 582 d = ob->ptr; 583 if (dictSize(d) > server.active_defrag_max_scan_fields) 584 defragLater(db, kde); 585 else 586 defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_NO_VAL); 587 /* handle the dict struct */ 588 if ((newd = activeDefragAlloc(ob->ptr))) 589 defragged++, ob->ptr = newd; 590 /* defrag the dict tables */ 591 defragged += dictDefragTables(ob->ptr); 592 return defragged; 593 } 594 595 /* Defrag callback for radix tree iterator, called for each node, 596 * used in order to defrag the nodes allocations. */ 597 int defragRaxNode(raxNode **noderef) { 598 raxNode *newnode = activeDefragAlloc(*noderef); 599 if (newnode) { 600 *noderef = newnode; 601 return 1; 602 } 603 return 0; 604 } 605 606 /* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */ 607 int scanLaterStraemListpacks(robj *ob, unsigned long *cursor, long long endtime, long long *defragged) { 608 static unsigned char last[sizeof(streamID)]; 609 raxIterator ri; 610 long iterations = 0; 611 if (ob->type != OBJ_STREAM || ob->encoding != OBJ_ENCODING_STREAM) { 612 *cursor = 0; 613 return 0; 614 } 615 616 stream *s = ob->ptr; 617 raxStart(&ri,s->rax); 618 if (*cursor == 0) { 619 /* if cursor is 0, we start new iteration */ 620 defragRaxNode(&s->rax->head); 621 /* assign the iterator node callback before the seek, so that the 622 * initial nodes that are processed till the first item are covered */ 623 ri.node_cb = defragRaxNode; 624 raxSeek(&ri,"^",NULL,0); 625 } else { 626 /* if cursor is non-zero, we seek to the static 'last' */ 627 if (!raxSeek(&ri,">", last, sizeof(last))) { 628 *cursor = 0; 629 return 0; 630 } 631 /* assign the iterator node callback after the seek, so that the 632 * initial nodes that are processed till now aren't covered */ 633 ri.node_cb = defragRaxNode; 634 } 635 636 (*cursor)++; 637 while (raxNext(&ri)) { 638 void *newdata = activeDefragAlloc(ri.data); 639 if (newdata) 640 raxSetData(ri.node, ri.data=newdata), (*defragged)++; 641 if (++iterations > 16) { 642 if (ustime() > endtime) { 643 serverAssert(ri.key_len==sizeof(last)); 644 memcpy(last,ri.key,ri.key_len); 645 raxStop(&ri); 646 return 1; 647 } 648 iterations = 0; 649 } 650 } 651 raxStop(&ri); 652 *cursor = 0; 653 return 0; 654 } 655 656 /* optional callback used defrag each rax element (not including the element pointer itself) */ 657 typedef void *(raxDefragFunction)(raxIterator *ri, void *privdata, long *defragged); 658 659 /* defrag radix tree including: 660 * 1) rax struct 661 * 2) rax nodes 662 * 3) rax entry data (only if defrag_data is specified) 663 * 4) call a callback per element, and allow the callback to return a new pointer for the element */ 664 long defragRadixTree(rax **raxref, int defrag_data, raxDefragFunction *element_cb, void *element_cb_data) { 665 long defragged = 0; 666 raxIterator ri; 667 rax* rax; 668 if ((rax = activeDefragAlloc(*raxref))) 669 defragged++, *raxref = rax; 670 rax = *raxref; 671 raxStart(&ri,rax); 672 ri.node_cb = defragRaxNode; 673 defragRaxNode(&rax->head); 674 raxSeek(&ri,"^",NULL,0); 675 while (raxNext(&ri)) { 676 void *newdata = NULL; 677 if (element_cb) 678 newdata = element_cb(&ri, element_cb_data, &defragged); 679 if (defrag_data && !newdata) 680 newdata = activeDefragAlloc(ri.data); 681 if (newdata) 682 raxSetData(ri.node, ri.data=newdata), defragged++; 683 } 684 raxStop(&ri); 685 return defragged; 686 } 687 688 typedef struct { 689 streamCG *cg; 690 streamConsumer *c; 691 } PendingEntryContext; 692 693 void* defragStreamConsumerPendingEntry(raxIterator *ri, void *privdata, long *defragged) { 694 UNUSED(defragged); 695 PendingEntryContext *ctx = privdata; 696 streamNACK *nack = ri->data, *newnack; 697 nack->consumer = ctx->c; /* update nack pointer to consumer */ 698 newnack = activeDefragAlloc(nack); 699 if (newnack) { 700 /* update consumer group pointer to the nack */ 701 void *prev; 702 raxInsert(ctx->cg->pel, ri->key, ri->key_len, newnack, &prev); 703 serverAssert(prev==nack); 704 /* note: we don't increment 'defragged' that's done by the caller */ 705 } 706 return newnack; 707 } 708 709 void* defragStreamConsumer(raxIterator *ri, void *privdata, long *defragged) { 710 streamConsumer *c = ri->data; 711 streamCG *cg = privdata; 712 void *newc = activeDefragAlloc(c); 713 if (newc) { 714 /* note: we don't increment 'defragged' that's done by the caller */ 715 c = newc; 716 } 717 sds newsds = activeDefragSds(c->name); 718 if (newsds) 719 (*defragged)++, c->name = newsds; 720 if (c->pel) { 721 PendingEntryContext pel_ctx = {cg, c}; 722 *defragged += defragRadixTree(&c->pel, 0, defragStreamConsumerPendingEntry, &pel_ctx); 723 } 724 return newc; /* returns NULL if c was not defragged */ 725 } 726 727 void* defragStreamConsumerGroup(raxIterator *ri, void *privdata, long *defragged) { 728 streamCG *cg = ri->data; 729 UNUSED(privdata); 730 if (cg->consumers) 731 *defragged += defragRadixTree(&cg->consumers, 0, defragStreamConsumer, cg); 732 if (cg->pel) 733 *defragged += defragRadixTree(&cg->pel, 0, NULL, NULL); 734 return NULL; 735 } 736 737 long defragStream(redisDb *db, dictEntry *kde) { 738 long defragged = 0; 739 robj *ob = dictGetVal(kde); 740 serverAssert(ob->type == OBJ_STREAM && ob->encoding == OBJ_ENCODING_STREAM); 741 stream *s = ob->ptr, *news; 742 743 /* handle the main struct */ 744 if ((news = activeDefragAlloc(s))) 745 defragged++, ob->ptr = s = news; 746 747 if (raxSize(s->rax) > server.active_defrag_max_scan_fields) { 748 rax *newrax = activeDefragAlloc(s->rax); 749 if (newrax) 750 defragged++, s->rax = newrax; 751 defragLater(db, kde); 752 } else 753 defragged += defragRadixTree(&s->rax, 1, NULL, NULL); 754 755 if (s->cgroups) 756 defragged += defragRadixTree(&s->cgroups, 1, defragStreamConsumerGroup, NULL); 757 return defragged; 758 } 759 760 /* for each key we scan in the main dict, this function will attempt to defrag 761 * all the various pointers it has. Returns a stat of how many pointers were 762 * moved. */ 763 long defragKey(redisDb *db, dictEntry *de) { 764 sds keysds = dictGetKey(de); 765 robj *newob, *ob; 766 unsigned char *newzl; 767 long defragged = 0; 768 sds newsds; 769 770 /* Try to defrag the key name. */ 771 newsds = activeDefragSds(keysds); 772 if (newsds) 773 defragged++, de->key = newsds; 774 if (dictSize(db->expires)) { 775 /* Dirty code: 776 * I can't search in db->expires for that key after i already released 777 * the pointer it holds it won't be able to do the string compare */ 778 uint64_t hash = dictGetHash(db->dict, de->key); 779 replaceSateliteDictKeyPtrAndOrDefragDictEntry(db->expires, keysds, newsds, hash, &defragged); 780 } 781 782 /* Try to defrag robj and / or string value. */ 783 ob = dictGetVal(de); 784 if ((newob = activeDefragStringOb(ob, &defragged))) { 785 de->v.val = newob; 786 ob = newob; 787 } 788 789 if (ob->type == OBJ_STRING) { 790 /* Already handled in activeDefragStringOb. */ 791 } else if (ob->type == OBJ_LIST) { 792 if (ob->encoding == OBJ_ENCODING_QUICKLIST) { 793 defragged += defragQuicklist(db, de); 794 } else if (ob->encoding == OBJ_ENCODING_ZIPLIST) { 795 if ((newzl = activeDefragAlloc(ob->ptr))) 796 defragged++, ob->ptr = newzl; 797 } else { 798 serverPanic("Unknown list encoding"); 799 } 800 } else if (ob->type == OBJ_SET) { 801 if (ob->encoding == OBJ_ENCODING_HT) { 802 defragged += defragSet(db, de); 803 } else if (ob->encoding == OBJ_ENCODING_INTSET) { 804 intset *newis, *is = ob->ptr; 805 if ((newis = activeDefragAlloc(is))) 806 defragged++, ob->ptr = newis; 807 } else { 808 serverPanic("Unknown set encoding"); 809 } 810 } else if (ob->type == OBJ_ZSET) { 811 if (ob->encoding == OBJ_ENCODING_ZIPLIST) { 812 if ((newzl = activeDefragAlloc(ob->ptr))) 813 defragged++, ob->ptr = newzl; 814 } else if (ob->encoding == OBJ_ENCODING_SKIPLIST) { 815 defragged += defragZsetSkiplist(db, de); 816 } else { 817 serverPanic("Unknown sorted set encoding"); 818 } 819 } else if (ob->type == OBJ_HASH) { 820 if (ob->encoding == OBJ_ENCODING_ZIPLIST) { 821 if ((newzl = activeDefragAlloc(ob->ptr))) 822 defragged++, ob->ptr = newzl; 823 } else if (ob->encoding == OBJ_ENCODING_HT) { 824 defragged += defragHash(db, de); 825 } else { 826 serverPanic("Unknown hash encoding"); 827 } 828 } else if (ob->type == OBJ_STREAM) { 829 defragged += defragStream(db, de); 830 } else if (ob->type == OBJ_MODULE) { 831 /* Currently defragmenting modules private data types 832 * is not supported. */ 833 } else { 834 serverPanic("Unknown object type"); 835 } 836 return defragged; 837 } 838 839 /* Defrag scan callback for the main db dictionary. */ 840 void defragScanCallback(void *privdata, const dictEntry *de) { 841 long defragged = defragKey((redisDb*)privdata, (dictEntry*)de); 842 server.stat_active_defrag_hits += defragged; 843 if(defragged) 844 server.stat_active_defrag_key_hits++; 845 else 846 server.stat_active_defrag_key_misses++; 847 server.stat_active_defrag_scanned++; 848 } 849 850 /* Defrag scan callback for each hash table bicket, 851 * used in order to defrag the dictEntry allocations. */ 852 void defragDictBucketCallback(void *privdata, dictEntry **bucketref) { 853 UNUSED(privdata); /* NOTE: this function is also used by both activeDefragCycle and scanLaterHash, etc. don't use privdata */ 854 while(*bucketref) { 855 dictEntry *de = *bucketref, *newde; 856 if ((newde = activeDefragAlloc(de))) { 857 *bucketref = newde; 858 } 859 bucketref = &(*bucketref)->next; 860 } 861 } 862 863 /* Utility function to get the fragmentation ratio from jemalloc. 864 * It is critical to do that by comparing only heap maps that belong to 865 * jemalloc, and skip ones the jemalloc keeps as spare. Since we use this 866 * fragmentation ratio in order to decide if a defrag action should be taken 867 * or not, a false detection can cause the defragmenter to waste a lot of CPU 868 * without the possibility of getting any results. */ 869 float getAllocatorFragmentation(size_t *out_frag_bytes) { 870 size_t resident, active, allocated; 871 zmalloc_get_allocator_info(&allocated, &active, &resident); 872 float frag_pct = ((float)active / allocated)*100 - 100; 873 size_t frag_bytes = active - allocated; 874 float rss_pct = ((float)resident / allocated)*100 - 100; 875 size_t rss_bytes = resident - allocated; 876 if(out_frag_bytes) 877 *out_frag_bytes = frag_bytes; 878 serverLog(LL_DEBUG, 879 "allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu rss)", 880 allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes); 881 return frag_pct; 882 } 883 884 /* We may need to defrag other globals, one small allcation can hold a full allocator run. 885 * so although small, it is still important to defrag these */ 886 long defragOtherGlobals() { 887 long defragged = 0; 888 889 /* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc. 890 * but we assume most of these are short lived, we only need to defrag allocations 891 * that remain static for a long time */ 892 defragged += activeDefragSdsDict(server.lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB); 893 defragged += activeDefragSdsListAndDict(server.repl_scriptcache_fifo, server.repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL); 894 return defragged; 895 } 896 897 /* returns 0 more work may or may not be needed (see non-zero cursor), 898 * and 1 if time is up and more work is needed. */ 899 int defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) { 900 if (de) { 901 robj *ob = dictGetVal(de); 902 if (ob->type == OBJ_LIST) { 903 server.stat_active_defrag_hits += scanLaterList(ob); 904 *cursor = 0; /* list has no scan, we must finish it in one go */ 905 } else if (ob->type == OBJ_SET) { 906 server.stat_active_defrag_hits += scanLaterSet(ob, cursor); 907 } else if (ob->type == OBJ_ZSET) { 908 server.stat_active_defrag_hits += scanLaterZset(ob, cursor); 909 } else if (ob->type == OBJ_HASH) { 910 server.stat_active_defrag_hits += scanLaterHash(ob, cursor); 911 } else if (ob->type == OBJ_STREAM) { 912 return scanLaterStraemListpacks(ob, cursor, endtime, &server.stat_active_defrag_hits); 913 } else { 914 *cursor = 0; /* object type may have changed since we schedule it for later */ 915 } 916 } else { 917 *cursor = 0; /* object may have been deleted already */ 918 } 919 return 0; 920 } 921 922 /* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */ 923 int defragLaterStep(redisDb *db, long long endtime) { 924 static sds current_key = NULL; 925 static unsigned long cursor = 0; 926 unsigned int iterations = 0; 927 unsigned long long prev_defragged = server.stat_active_defrag_hits; 928 unsigned long long prev_scanned = server.stat_active_defrag_scanned; 929 long long key_defragged; 930 931 do { 932 /* if we're not continuing a scan from the last call or loop, start a new one */ 933 if (!cursor) { 934 listNode *head = listFirst(db->defrag_later); 935 936 /* Move on to next key */ 937 if (current_key) { 938 serverAssert(current_key == head->value); 939 sdsfree(head->value); 940 listDelNode(db->defrag_later, head); 941 cursor = 0; 942 current_key = NULL; 943 } 944 945 /* stop if we reached the last one. */ 946 head = listFirst(db->defrag_later); 947 if (!head) 948 return 0; 949 950 /* start a new key */ 951 current_key = head->value; 952 cursor = 0; 953 } 954 955 /* each time we enter this function we need to fetch the key from the dict again (if it still exists) */ 956 dictEntry *de = dictFind(db->dict, current_key); 957 key_defragged = server.stat_active_defrag_hits; 958 do { 959 int quit = 0; 960 if (defragLaterItem(de, &cursor, endtime)) 961 quit = 1; /* time is up, we didn't finish all the work */ 962 963 /* Don't start a new BIG key in this loop, this is because the 964 * next key can be a list, and scanLaterList must be done in once cycle */ 965 if (!cursor) 966 quit = 1; 967 968 /* Once in 16 scan iterations, 512 pointer reallocations, or 64 fields 969 * (if we have a lot of pointers in one hash bucket, or rehashing), 970 * check if we reached the time limit. */ 971 if (quit || (++iterations > 16 || 972 server.stat_active_defrag_hits - prev_defragged > 512 || 973 server.stat_active_defrag_scanned - prev_scanned > 64)) { 974 if (quit || ustime() > endtime) { 975 if(key_defragged != server.stat_active_defrag_hits) 976 server.stat_active_defrag_key_hits++; 977 else 978 server.stat_active_defrag_key_misses++; 979 return 1; 980 } 981 iterations = 0; 982 prev_defragged = server.stat_active_defrag_hits; 983 prev_scanned = server.stat_active_defrag_scanned; 984 } 985 } while(cursor); 986 if(key_defragged != server.stat_active_defrag_hits) 987 server.stat_active_defrag_key_hits++; 988 else 989 server.stat_active_defrag_key_misses++; 990 } while(1); 991 } 992 993 #define INTERPOLATE(x, x1, x2, y1, y2) ( (y1) + ((x)-(x1)) * ((y2)-(y1)) / ((x2)-(x1)) ) 994 #define LIMIT(y, min, max) ((y)<(min)? min: ((y)>(max)? max: (y))) 995 996 /* decide if defrag is needed, and at what CPU effort to invest in it */ 997 void computeDefragCycles() { 998 size_t frag_bytes; 999 float frag_pct = getAllocatorFragmentation(&frag_bytes); 1000 /* If we're not already running, and below the threshold, exit. */ 1001 if (!server.active_defrag_running) { 1002 if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes) 1003 return; 1004 } 1005 1006 /* Calculate the adaptive aggressiveness of the defrag */ 1007 int cpu_pct = INTERPOLATE(frag_pct, 1008 server.active_defrag_threshold_lower, 1009 server.active_defrag_threshold_upper, 1010 server.active_defrag_cycle_min, 1011 server.active_defrag_cycle_max); 1012 cpu_pct = LIMIT(cpu_pct, 1013 server.active_defrag_cycle_min, 1014 server.active_defrag_cycle_max); 1015 /* We allow increasing the aggressiveness during a scan, but don't 1016 * reduce it. */ 1017 if (!server.active_defrag_running || 1018 cpu_pct > server.active_defrag_running) 1019 { 1020 server.active_defrag_running = cpu_pct; 1021 serverLog(LL_VERBOSE, 1022 "Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%", 1023 frag_pct, frag_bytes, cpu_pct); 1024 } 1025 } 1026 1027 /* Perform incremental defragmentation work from the serverCron. 1028 * This works in a similar way to activeExpireCycle, in the sense that 1029 * we do incremental work across calls. */ 1030 void activeDefragCycle(void) { 1031 static int current_db = -1; 1032 static unsigned long cursor = 0; 1033 static redisDb *db = NULL; 1034 static long long start_scan, start_stat; 1035 unsigned int iterations = 0; 1036 unsigned long long prev_defragged = server.stat_active_defrag_hits; 1037 unsigned long long prev_scanned = server.stat_active_defrag_scanned; 1038 long long start, timelimit, endtime; 1039 mstime_t latency; 1040 int quit = 0; 1041 1042 if (server.aof_child_pid!=-1 || server.rdb_child_pid!=-1) 1043 return; /* Defragging memory while there's a fork will just do damage. */ 1044 1045 /* Once a second, check if we the fragmentation justfies starting a scan 1046 * or making it more aggressive. */ 1047 run_with_period(1000) { 1048 computeDefragCycles(); 1049 } 1050 if (!server.active_defrag_running) 1051 return; 1052 1053 /* See activeExpireCycle for how timelimit is handled. */ 1054 start = ustime(); 1055 timelimit = 1000000*server.active_defrag_running/server.hz/100; 1056 if (timelimit <= 0) timelimit = 1; 1057 endtime = start + timelimit; 1058 latencyStartMonitor(latency); 1059 1060 do { 1061 /* if we're not continuing a scan from the last call or loop, start a new one */ 1062 if (!cursor) { 1063 /* finish any leftovers from previous db before moving to the next one */ 1064 if (db && defragLaterStep(db, endtime)) { 1065 quit = 1; /* time is up, we didn't finish all the work */ 1066 break; /* this will exit the function and we'll continue on the next cycle */ 1067 } 1068 1069 /* Move on to next database, and stop if we reached the last one. */ 1070 if (++current_db >= server.dbnum) { 1071 /* defrag other items not part of the db / keys */ 1072 defragOtherGlobals(); 1073 1074 long long now = ustime(); 1075 size_t frag_bytes; 1076 float frag_pct = getAllocatorFragmentation(&frag_bytes); 1077 serverLog(LL_VERBOSE, 1078 "Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu", 1079 (int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes); 1080 1081 start_scan = now; 1082 current_db = -1; 1083 cursor = 0; 1084 db = NULL; 1085 server.active_defrag_running = 0; 1086 1087 computeDefragCycles(); /* if another scan is needed, start it right away */ 1088 if (server.active_defrag_running != 0 && ustime() < endtime) 1089 continue; 1090 break; 1091 } 1092 else if (current_db==0) { 1093 /* Start a scan from the first database. */ 1094 start_scan = ustime(); 1095 start_stat = server.stat_active_defrag_hits; 1096 } 1097 1098 db = &server.db[current_db]; 1099 cursor = 0; 1100 } 1101 1102 do { 1103 /* before scanning the next bucket, see if we have big keys left from the previous bucket to scan */ 1104 if (defragLaterStep(db, endtime)) { 1105 quit = 1; /* time is up, we didn't finish all the work */ 1106 break; /* this will exit the function and we'll continue on the next cycle */ 1107 } 1108 1109 cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db); 1110 1111 /* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys 1112 * (if we have a lot of pointers in one hash bucket or rehasing), 1113 * check if we reached the time limit. 1114 * But regardless, don't start a new db in this loop, this is because after 1115 * the last db we call defragOtherGlobals, which must be done in once cycle */ 1116 if (!cursor || (++iterations > 16 || 1117 server.stat_active_defrag_hits - prev_defragged > 512 || 1118 server.stat_active_defrag_scanned - prev_scanned > 64)) { 1119 if (!cursor || ustime() > endtime) { 1120 quit = 1; 1121 break; 1122 } 1123 iterations = 0; 1124 prev_defragged = server.stat_active_defrag_hits; 1125 prev_scanned = server.stat_active_defrag_scanned; 1126 } 1127 } while(cursor && !quit); 1128 } while(!quit); 1129 1130 latencyEndMonitor(latency); 1131 latencyAddSampleIfNeeded("active-defrag-cycle",latency); 1132 } 1133 1134 #else /* HAVE_DEFRAG */ 1135 1136 void activeDefragCycle(void) { 1137 /* Not implemented yet. */ 1138 } 1139 1140 #endif 1141