summaryrefslogtreecommitdiff
path: root/src/lazyfree.c
diff options
context:
space:
mode:
authorchenyangyang <chenyang8094@users.noreply.github.com>2020-11-16 16:34:04 +0800
committerGitHub <noreply@github.com>2020-11-16 10:34:04 +0200
commitc1aaad06d85c89ab7abebd5cefab026bdcb086ab (patch)
treed0bf36d0b1452381818b63d63ad0bd9e581a6b57 /src/lazyfree.c
parentd8fd48c4363dd5265bb944c6ec881368d67f4afd (diff)
downloadredis-c1aaad06d85c89ab7abebd5cefab026bdcb086ab.tar.gz
Modules callbacks for lazy free effort, and unlink (#7912)
Add two optional callbacks to the RedisModuleTypeMethods structure, which is `free_effort` and `unlink`. the `free_effort` callback indicates the effort required to free a module memory. Currently, if the effort exceeds LAZYFREE_THRESHOLD, the module memory may be released asynchronously. the `unlink` callback indicates the key has been removed from the DB by redis, and may soon be freed by a background thread. Add `lazyfreed_objects` info field, which represents the number of objects that have been lazyfreed since redis was started. Add `RM_GetTypeMethodVersion` API, which return the current redis-server runtime value of `REDISMODULE_TYPE_METHOD_VERSION`. You can use that when calling `RM_CreateDataType` to know which fields of RedisModuleTypeMethods are gonna be supported and which will be ignored.
Diffstat (limited to 'src/lazyfree.c')
-rw-r--r--src/lazyfree.c40
1 files changed, 33 insertions, 7 deletions
diff --git a/src/lazyfree.c b/src/lazyfree.c
index b0fc26fcf..5a78d5a55 100644
--- a/src/lazyfree.c
+++ b/src/lazyfree.c
@@ -4,6 +4,7 @@
#include "cluster.h"
static redisAtomic size_t lazyfree_objects = 0;
+static redisAtomic size_t lazyfreed_objects = 0;
/* Return the number of currently pending objects to free. */
size_t lazyfreeGetPendingObjectsCount(void) {
@@ -12,6 +13,13 @@ size_t lazyfreeGetPendingObjectsCount(void) {
return aux;
}
+/* Return the number of objects that have been freed. */
+size_t lazyfreeGetFreedObjectsCount(void) {
+ size_t aux;
+ atomicGet(lazyfreed_objects,aux);
+ return aux;
+}
+
/* 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.
@@ -27,7 +35,7 @@ size_t lazyfreeGetPendingObjectsCount(void) {
*
* For lists the function returns the number of elements in the quicklist
* representing the list. */
-size_t lazyfreeGetFreeEffort(robj *obj) {
+size_t lazyfreeGetFreeEffort(robj *key, robj *obj) {
if (obj->type == OBJ_LIST) {
quicklist *ql = obj->ptr;
return ql->len;
@@ -64,6 +72,17 @@ size_t lazyfreeGetFreeEffort(robj *obj) {
raxStop(&ri);
}
return effort;
+ } else if (obj->type == OBJ_MODULE) {
+ moduleValue *mv = obj->ptr;
+ moduleType *mt = mv->type;
+ if (mt->free_effort != NULL) {
+ size_t effort = mt->free_effort(key,mv->value);
+ /* If the module's free_effort returns 0, it will use asynchronous free
+ memory by default */
+ return effort == 0 ? ULONG_MAX : effort;
+ } else {
+ return 1;
+ }
} else {
return 1; /* Everything else is a single allocation. */
}
@@ -85,7 +104,11 @@ int dbAsyncDelete(redisDb *db, robj *key) {
dictEntry *de = dictUnlink(db->dict,key->ptr);
if (de) {
robj *val = dictGetVal(de);
- size_t free_effort = lazyfreeGetFreeEffort(val);
+
+ /* Tells the module that the key has been unlinked from the database. */
+ moduleNotifyKeyUnlink(key,val);
+
+ size_t free_effort = lazyfreeGetFreeEffort(key,val);
/* If releasing the object is too much work, do it in the background
* by adding the object to the lazy free list.
@@ -114,13 +137,13 @@ int dbAsyncDelete(redisDb *db, robj *key) {
}
/* Free an object, if the object is huge enough, free it in async way. */
-void freeObjAsync(robj *o) {
- size_t free_effort = lazyfreeGetFreeEffort(o);
- if (free_effort > LAZYFREE_THRESHOLD && o->refcount == 1) {
+void freeObjAsync(robj *key, robj *obj) {
+ size_t free_effort = lazyfreeGetFreeEffort(key,obj);
+ if (free_effort > LAZYFREE_THRESHOLD && obj->refcount == 1) {
atomicIncr(lazyfree_objects,1);
- bioCreateBackgroundJob(BIO_LAZY_FREE,o,NULL,NULL);
+ bioCreateBackgroundJob(BIO_LAZY_FREE,obj,NULL,NULL);
} else {
- decrRefCount(o);
+ decrRefCount(obj);
}
}
@@ -152,6 +175,7 @@ void slotToKeyFlushAsync(void) {
void lazyfreeFreeObjectFromBioThread(robj *o) {
decrRefCount(o);
atomicDecr(lazyfree_objects,1);
+ atomicIncr(lazyfreed_objects,1);
}
/* Release a database from the lazyfree thread. The 'db' pointer is the
@@ -164,6 +188,7 @@ void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) {
dictRelease(ht1);
dictRelease(ht2);
atomicDecr(lazyfree_objects,numkeys);
+ atomicIncr(lazyfreed_objects,numkeys);
}
/* Release the skiplist mapping Redis Cluster keys to slots in the
@@ -172,4 +197,5 @@ void lazyfreeFreeSlotsMapFromBioThread(rax *rt) {
size_t len = rt->numele;
raxFree(rt);
atomicDecr(lazyfree_objects,len);
+ atomicIncr(lazyfreed_objects,len);
}