Return the amount of work needed in order to free an object. * The return value is not always the actual number of allocations the * object is composed of, but a number proportional to it. * * For strings the function always returns 1. * * For aggregated objects represented by hash tables or other data structures * the function just returns the number of elements the object is composed of.
| 84 | * For lists the function returns the number of elements in the quicklist |
| 85 | * representing the list. */ |
| 86 | size_t lazyfreeGetFreeEffort(robj *key, robj *obj) { |
| 87 | if (obj->type == OBJ_LIST) { |
| 88 | quicklist *ql = (quicklist*)ptrFromObj(obj); |
| 89 | return ql->len; |
| 90 | } else if (obj->type == OBJ_SET && obj->encoding == OBJ_ENCODING_HT) { |
| 91 | dict *ht = (dict*)ptrFromObj(obj); |
| 92 | return dictSize(ht); |
| 93 | } else if (obj->type == OBJ_ZSET && obj->encoding == OBJ_ENCODING_SKIPLIST){ |
| 94 | zset *zs = (zset*)ptrFromObj(obj); |
| 95 | return zs->zsl->length; |
| 96 | } else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) { |
| 97 | dict *ht = (dict*)ptrFromObj(obj); |
| 98 | return dictSize(ht); |
| 99 | } else if (obj->type == OBJ_STREAM) { |
| 100 | size_t effort = 0; |
| 101 | stream *s = (stream*)ptrFromObj(obj); |
| 102 | |
| 103 | /* Make a best effort estimate to maintain constant runtime. Every macro |
| 104 | * node in the Stream is one allocation. */ |
| 105 | effort += s->rax->numnodes; |
| 106 | |
| 107 | /* Every consumer group is an allocation and so are the entries in its |
| 108 | * PEL. We use size of the first group's PEL as an estimate for all |
| 109 | * others. */ |
| 110 | if (s->cgroups && raxSize(s->cgroups)) { |
| 111 | raxIterator ri; |
| 112 | streamCG *cg; |
| 113 | raxStart(&ri,s->cgroups); |
| 114 | raxSeek(&ri,"^",NULL,0); |
| 115 | /* There must be at least one group so the following should always |
| 116 | * work. */ |
| 117 | serverAssert(raxNext(&ri)); |
| 118 | cg = (streamCG*)ri.data; |
| 119 | effort += raxSize(s->cgroups)*(1+raxSize(cg->pel)); |
| 120 | raxStop(&ri); |
| 121 | } |
| 122 | return effort; |
| 123 | } else if (obj->type == OBJ_MODULE) { |
| 124 | moduleValue *mv = (moduleValue*)ptrFromObj(obj); |
| 125 | moduleType *mt = mv->type; |
| 126 | if (mt->free_effort != NULL) { |
| 127 | size_t effort = mt->free_effort(key,mv->value); |
| 128 | /* If the module's free_effort returns 0, it will use asynchronous free |
| 129 | memory by default */ |
| 130 | return effort == 0 ? ULONG_MAX : effort; |
| 131 | } else { |
| 132 | return 1; |
| 133 | } |
| 134 | } else { |
| 135 | return 1; /* Everything else is a single allocation. */ |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /* Delete a key, value, and associated expiration entry if any, from the DB. |
| 140 | * If there are enough allocations to free the value object may be put into |