From c1aaad06d85c89ab7abebd5cefab026bdcb086ab Mon Sep 17 00:00:00 2001 From: chenyangyang Date: Mon, 16 Nov 2020 16:34:04 +0800 Subject: 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. --- src/lazyfree.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'src/lazyfree.c') 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); } -- cgit v1.2.1