123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738 |
- /* This module is used to test a use case of a module that stores information
- * about keys in global memory, and relies on the enhanced data type callbacks to
- * get key name and dbid on various operations.
- *
- * it simulates a simple memory allocator. The smallest allocation unit of
- * the allocator is a mem block with a size of 4KB. Multiple mem blocks are combined
- * using a linked list. These linked lists are placed in a global dict named 'mem_pool'.
- * Each db has a 'mem_pool'. You can use the 'mem.alloc' command to allocate a specified
- * number of mem blocks, and use 'mem.free' to release the memory. Use 'mem.write', 'mem.read'
- * to write and read the specified mem block (note that each mem block can only be written once).
- * Use 'mem.usage' to get the memory usage under different dbs, and it will return the size
- * mem blocks and used mem blocks under the db.
- * The specific structure diagram is as follows:
- *
- *
- * Global variables of the module:
- *
- * mem blocks link
- * ┌─────┬─────┐
- * │ │ │ ┌───┐ ┌───┐ ┌───┐
- * │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│
- * │ │ │ └───┘ └───┘ └───┘
- * ├─────┼─────┤
- * ┌───────┐ ┌────► │ │ │ ┌───┐ ┌───┐
- * │ │ │ │ k2 │ ───┼───►│4KB├───►│4KB│
- * │ db0 ├──────┘ │ │ │ └───┘ └───┘
- * │ │ ├─────┼─────┤
- * ├───────┤ │ │ │ ┌───┐ ┌───┐ ┌───┐
- * │ │ │ k3 │ ───┼───►│4KB├───►│4KB├───►│4KB│
- * │ db1 ├──►null │ │ │ └───┘ └───┘ └───┘
- * │ │ └─────┴─────┘
- * ├───────┤ dict
- * │ │
- * │ db2 ├─────────┐
- * │ │ │
- * ├───────┤ │ ┌─────┬─────┐
- * │ │ │ │ │ │ ┌───┐ ┌───┐ ┌───┐
- * │ db3 ├──►null │ │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│
- * │ │ │ │ │ │ └───┘ └───┘ └───┘
- * └───────┘ │ ├─────┼─────┤
- * mem_pool[MAX_DB] │ │ │ │ ┌───┐ ┌───┐
- * └──►│ k2 │ ───┼───►│4KB├───►│4KB│
- * │ │ │ └───┘ └───┘
- * └─────┴─────┘
- * dict
- *
- *
- * Keys in redis database:
- *
- * ┌───────┐
- * │ size │
- * ┌───────────►│ used │
- * │ │ mask │
- * ┌─────┬─────┐ │ └───────┘ ┌───────┐
- * │ │ │ │ MemAllocObject │ size │
- * │ k1 │ ───┼─┘ ┌───────────►│ used │
- * │ │ │ │ │ mask │
- * ├─────┼─────┤ ┌───────┐ ┌─────┬─────┐ │ └───────┘
- * │ │ │ │ size │ │ │ │ │ MemAllocObject
- * │ k2 │ ───┼─────────────►│ used │ │ k1 │ ───┼─┘
- * │ │ │ │ mask │ │ │ │
- * ├─────┼─────┤ └───────┘ ├─────┼─────┤
- * │ │ │ MemAllocObject │ │ │
- * │ k3 │ ───┼─┐ │ k2 │ ───┼─┐
- * │ │ │ │ │ │ │ │
- * └─────┴─────┘ │ ┌───────┐ └─────┴─────┘ │ ┌───────┐
- * redis db[0] │ │ size │ redis db[1] │ │ size │
- * └───────────►│ used │ └───────────►│ used │
- * │ mask │ │ mask │
- * └───────┘ └───────┘
- * MemAllocObject MemAllocObject
- *
- **/
- #include "redismodule.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <stdint.h>
- static RedisModuleType *MemAllocType;
- #define MAX_DB 16
- RedisModuleDict *mem_pool[MAX_DB];
- typedef struct MemAllocObject {
- long long size;
- long long used;
- uint64_t mask;
- } MemAllocObject;
- MemAllocObject *createMemAllocObject(void) {
- MemAllocObject *o = RedisModule_Calloc(1, sizeof(*o));
- return o;
- }
- /*---------------------------- mem block apis ------------------------------------*/
- #define BLOCK_SIZE 4096
- struct MemBlock {
- char block[BLOCK_SIZE];
- struct MemBlock *next;
- };
- void MemBlockFree(struct MemBlock *head) {
- if (head) {
- struct MemBlock *block = head->next, *next;
- RedisModule_Free(head);
- while (block) {
- next = block->next;
- RedisModule_Free(block);
- block = next;
- }
- }
- }
- struct MemBlock *MemBlockCreate(long long num) {
- if (num <= 0) {
- return NULL;
- }
- struct MemBlock *head = RedisModule_Calloc(1, sizeof(struct MemBlock));
- struct MemBlock *block = head;
- while (--num) {
- block->next = RedisModule_Calloc(1, sizeof(struct MemBlock));
- block = block->next;
- }
- return head;
- }
- long long MemBlockNum(const struct MemBlock *head) {
- long long num = 0;
- const struct MemBlock *block = head;
- while (block) {
- num++;
- block = block->next;
- }
- return num;
- }
- size_t MemBlockWrite(struct MemBlock *head, long long block_index, const char *data, size_t size) {
- size_t w_size = 0;
- struct MemBlock *block = head;
- while (block_index-- && block) {
- block = block->next;
- }
- if (block) {
- size = size > BLOCK_SIZE ? BLOCK_SIZE:size;
- memcpy(block->block, data, size);
- w_size += size;
- }
- return w_size;
- }
- int MemBlockRead(struct MemBlock *head, long long block_index, char *data, size_t size) {
- size_t r_size = 0;
- struct MemBlock *block = head;
- while (block_index-- && block) {
- block = block->next;
- }
- if (block) {
- size = size > BLOCK_SIZE ? BLOCK_SIZE:size;
- memcpy(data, block->block, size);
- r_size += size;
- }
- return r_size;
- }
- void MemPoolFreeDb(RedisModuleCtx *ctx, int dbid) {
- RedisModuleString *key;
- void *tdata;
- RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0);
- while((key = RedisModule_DictNext(ctx, iter, &tdata)) != NULL) {
- MemBlockFree((struct MemBlock *)tdata);
- }
- RedisModule_DictIteratorStop(iter);
- RedisModule_FreeDict(NULL, mem_pool[dbid]);
- mem_pool[dbid] = RedisModule_CreateDict(NULL);
- }
- struct MemBlock *MemBlockClone(const struct MemBlock *head) {
- struct MemBlock *newhead = NULL;
- if (head) {
- newhead = RedisModule_Calloc(1, sizeof(struct MemBlock));
- memcpy(newhead->block, head->block, BLOCK_SIZE);
- struct MemBlock *newblock = newhead;
- const struct MemBlock *oldblock = head->next;
- while (oldblock) {
- newblock->next = RedisModule_Calloc(1, sizeof(struct MemBlock));
- newblock = newblock->next;
- memcpy(newblock->block, oldblock->block, BLOCK_SIZE);
- oldblock = oldblock->next;
- }
- }
- return newhead;
- }
- /*---------------------------- event handler ------------------------------------*/
- void swapDbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) {
- REDISMODULE_NOT_USED(ctx);
- REDISMODULE_NOT_USED(e);
- REDISMODULE_NOT_USED(sub);
- RedisModuleSwapDbInfo *ei = data;
- // swap
- RedisModuleDict *tmp = mem_pool[ei->dbnum_first];
- mem_pool[ei->dbnum_first] = mem_pool[ei->dbnum_second];
- mem_pool[ei->dbnum_second] = tmp;
- }
- void flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) {
- REDISMODULE_NOT_USED(ctx);
- REDISMODULE_NOT_USED(e);
- int i;
- RedisModuleFlushInfo *fi = data;
- RedisModule_AutoMemory(ctx);
- if (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) {
- if (fi->dbnum != -1) {
- MemPoolFreeDb(ctx, fi->dbnum);
- } else {
- for (i = 0; i < MAX_DB; i++) {
- MemPoolFreeDb(ctx, i);
- }
- }
- }
- }
- /*---------------------------- command implementation ------------------------------------*/
- /* MEM.ALLOC key block_num */
- int MemAlloc_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc != 3) {
- return RedisModule_WrongArity(ctx);
- }
- long long block_num;
- if ((RedisModule_StringToLongLong(argv[2], &block_num) != REDISMODULE_OK) || block_num <= 0) {
- return RedisModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0");
- }
- RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);
- int type = RedisModule_KeyType(key);
- if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) {
- return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
- }
- MemAllocObject *o;
- if (type == REDISMODULE_KEYTYPE_EMPTY) {
- o = createMemAllocObject();
- RedisModule_ModuleTypeSetValue(key, MemAllocType, o);
- } else {
- o = RedisModule_ModuleTypeGetValue(key);
- }
- struct MemBlock *mem = MemBlockCreate(block_num);
- RedisModule_Assert(mem != NULL);
- RedisModule_DictSet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], mem);
- o->size = block_num;
- o->used = 0;
- o->mask = 0;
- RedisModule_ReplyWithLongLong(ctx, block_num);
- RedisModule_ReplicateVerbatim(ctx);
- return REDISMODULE_OK;
- }
- /* MEM.FREE key */
- int MemFree_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc != 2) {
- return RedisModule_WrongArity(ctx);
- }
- RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
- int type = RedisModule_KeyType(key);
- if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) {
- return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
- }
- int ret = 0;
- MemAllocObject *o;
- if (type == REDISMODULE_KEYTYPE_EMPTY) {
- RedisModule_ReplyWithLongLong(ctx, ret);
- return REDISMODULE_OK;
- } else {
- o = RedisModule_ModuleTypeGetValue(key);
- }
- int nokey;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey);
- if (!nokey && mem) {
- RedisModule_DictDel(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], NULL);
- MemBlockFree(mem);
- o->used = 0;
- o->size = 0;
- o->mask = 0;
- ret = 1;
- }
- RedisModule_ReplyWithLongLong(ctx, ret);
- RedisModule_ReplicateVerbatim(ctx);
- return REDISMODULE_OK;
- }
- /* MEM.WRITE key block_index data */
- int MemWrite_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc != 4) {
- return RedisModule_WrongArity(ctx);
- }
- long long block_index;
- if ((RedisModule_StringToLongLong(argv[2], &block_index) != REDISMODULE_OK) || block_index < 0) {
- return RedisModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0");
- }
- RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);
- int type = RedisModule_KeyType(key);
- if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) {
- return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
- }
- MemAllocObject *o;
- if (type == REDISMODULE_KEYTYPE_EMPTY) {
- return RedisModule_ReplyWithError(ctx, "ERR Memory has not been allocated");
- } else {
- o = RedisModule_ModuleTypeGetValue(key);
- }
- if (o->mask & (1UL << block_index)) {
- return RedisModule_ReplyWithError(ctx, "ERR block is busy");
- }
- int ret = 0;
- int nokey;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey);
- if (!nokey && mem) {
- size_t len;
- const char *buf = RedisModule_StringPtrLen(argv[3], &len);
- ret = MemBlockWrite(mem, block_index, buf, len);
- o->mask |= (1UL << block_index);
- o->used++;
- }
- RedisModule_ReplyWithLongLong(ctx, ret);
- RedisModule_ReplicateVerbatim(ctx);
- return REDISMODULE_OK;
- }
- /* MEM.READ key block_index */
- int MemRead_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc != 3) {
- return RedisModule_WrongArity(ctx);
- }
- long long block_index;
- if ((RedisModule_StringToLongLong(argv[2], &block_index) != REDISMODULE_OK) || block_index < 0) {
- return RedisModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0");
- }
- RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
- int type = RedisModule_KeyType(key);
- if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) {
- return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
- }
- MemAllocObject *o;
- if (type == REDISMODULE_KEYTYPE_EMPTY) {
- return RedisModule_ReplyWithError(ctx, "ERR Memory has not been allocated");
- } else {
- o = RedisModule_ModuleTypeGetValue(key);
- }
- if (!(o->mask & (1UL << block_index))) {
- return RedisModule_ReplyWithNull(ctx);
- }
- int nokey;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey);
- RedisModule_Assert(nokey == 0 && mem != NULL);
-
- char buf[BLOCK_SIZE];
- MemBlockRead(mem, block_index, buf, sizeof(buf));
-
- /* Assuming that the contents are all c-style strings */
- RedisModule_ReplyWithStringBuffer(ctx, buf, strlen(buf));
- return REDISMODULE_OK;
- }
- /* MEM.USAGE dbid */
- int MemUsage_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc != 2) {
- return RedisModule_WrongArity(ctx);
- }
- long long dbid;
- if ((RedisModule_StringToLongLong(argv[1], (long long *)&dbid) != REDISMODULE_OK)) {
- return RedisModule_ReplyWithError(ctx, "ERR invalid value: must be a integer");
- }
- if (dbid < 0 || dbid >= MAX_DB) {
- return RedisModule_ReplyWithError(ctx, "ERR dbid out of range");
- }
- long long size = 0, used = 0;
- void *data;
- RedisModuleString *key;
- RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0);
- while((key = RedisModule_DictNext(ctx, iter, &data)) != NULL) {
- int dbbackup = RedisModule_GetSelectedDb(ctx);
- RedisModule_SelectDb(ctx, dbid);
- RedisModuleKey *openkey = RedisModule_OpenKey(ctx, key, REDISMODULE_READ);
- int type = RedisModule_KeyType(openkey);
- RedisModule_Assert(type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(openkey) == MemAllocType);
- MemAllocObject *o = RedisModule_ModuleTypeGetValue(openkey);
- used += o->used;
- size += o->size;
- RedisModule_CloseKey(openkey);
- RedisModule_SelectDb(ctx, dbbackup);
- }
- RedisModule_DictIteratorStop(iter);
- RedisModule_ReplyWithArray(ctx, 4);
- RedisModule_ReplyWithSimpleString(ctx, "total");
- RedisModule_ReplyWithLongLong(ctx, size);
- RedisModule_ReplyWithSimpleString(ctx, "used");
- RedisModule_ReplyWithLongLong(ctx, used);
- return REDISMODULE_OK;
- }
- /* MEM.ALLOCANDWRITE key block_num block_index data block_index data ... */
- int MemAllocAndWrite_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- RedisModule_AutoMemory(ctx);
- if (argc < 3) {
- return RedisModule_WrongArity(ctx);
- }
- long long block_num;
- if ((RedisModule_StringToLongLong(argv[2], &block_num) != REDISMODULE_OK) || block_num <= 0) {
- return RedisModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0");
- }
- RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);
- int type = RedisModule_KeyType(key);
- if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) {
- return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
- }
- MemAllocObject *o;
- if (type == REDISMODULE_KEYTYPE_EMPTY) {
- o = createMemAllocObject();
- RedisModule_ModuleTypeSetValue(key, MemAllocType, o);
- } else {
- o = RedisModule_ModuleTypeGetValue(key);
- }
- struct MemBlock *mem = MemBlockCreate(block_num);
- RedisModule_Assert(mem != NULL);
- RedisModule_DictSet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], mem);
- o->used = 0;
- o->mask = 0;
- o->size = block_num;
- int i = 3;
- long long block_index;
- for (; i < argc; i++) {
- /* Security is guaranteed internally, so no security check. */
- RedisModule_StringToLongLong(argv[i], &block_index);
- size_t len;
- const char * buf = RedisModule_StringPtrLen(argv[i + 1], &len);
- MemBlockWrite(mem, block_index, buf, len);
- o->used++;
- o->mask |= (1UL << block_index);
- }
- RedisModule_ReplyWithSimpleString(ctx, "OK");
- RedisModule_ReplicateVerbatim(ctx);
- return REDISMODULE_OK;
- }
- /*---------------------------- type callbacks ------------------------------------*/
- void *MemAllocRdbLoad(RedisModuleIO *rdb, int encver) {
- if (encver != 0) {
- return NULL;
- }
- MemAllocObject *o = createMemAllocObject();
- o->size = RedisModule_LoadSigned(rdb);
- o->used = RedisModule_LoadSigned(rdb);
- o->mask = RedisModule_LoadUnsigned(rdb);
- const RedisModuleString *key = RedisModule_GetKeyNameFromIO(rdb);
- int dbid = RedisModule_GetDbIdFromIO(rdb);
- if (o->size) {
- size_t size;
- char *tmpbuf;
- long long num = o->size;
- struct MemBlock *head = RedisModule_Calloc(1, sizeof(struct MemBlock));
- tmpbuf = RedisModule_LoadStringBuffer(rdb, &size);
- memcpy(head->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size);
- RedisModule_Free(tmpbuf);
- struct MemBlock *block = head;
- while (--num) {
- block->next = RedisModule_Calloc(1, sizeof(struct MemBlock));
- block = block->next;
- tmpbuf = RedisModule_LoadStringBuffer(rdb, &size);
- memcpy(block->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size);
- RedisModule_Free(tmpbuf);
- }
- RedisModule_DictSet(mem_pool[dbid], (RedisModuleString *)key, head);
- }
-
- return o;
- }
- void MemAllocRdbSave(RedisModuleIO *rdb, void *value) {
- MemAllocObject *o = value;
- RedisModule_SaveSigned(rdb, o->size);
- RedisModule_SaveSigned(rdb, o->used);
- RedisModule_SaveUnsigned(rdb, o->mask);
- const RedisModuleString *key = RedisModule_GetKeyNameFromIO(rdb);
- int dbid = RedisModule_GetDbIdFromIO(rdb);
- if (o->size) {
- int nokey;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey);
- RedisModule_Assert(nokey == 0 && mem != NULL);
- struct MemBlock *block = mem;
- while (block) {
- RedisModule_SaveStringBuffer(rdb, block->block, BLOCK_SIZE);
- block = block->next;
- }
- }
- }
- void MemAllocAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) {
- MemAllocObject *o = (MemAllocObject *)value;
- if (o->size) {
- int dbid = RedisModule_GetDbIdFromIO(aof);
- int nokey;
- size_t i = 0, j = 0;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey);
- RedisModule_Assert(nokey == 0 && mem != NULL);
- size_t array_size = o->size * 2;
- RedisModuleString ** string_array = RedisModule_Calloc(array_size, sizeof(RedisModuleString *));
- while (mem) {
- string_array[i] = RedisModule_CreateStringFromLongLong(NULL, j);
- string_array[i + 1] = RedisModule_CreateString(NULL, mem->block, BLOCK_SIZE);
- mem = mem->next;
- i += 2;
- j++;
- }
- RedisModule_EmitAOF(aof, "mem.allocandwrite", "slv", key, o->size, string_array, array_size);
- for (i = 0; i < array_size; i++) {
- RedisModule_FreeString(NULL, string_array[i]);
- }
- RedisModule_Free(string_array);
- } else {
- RedisModule_EmitAOF(aof, "mem.allocandwrite", "sl", key, o->size);
- }
- }
- void MemAllocFree(void *value) {
- RedisModule_Free(value);
- }
- void MemAllocUnlink(RedisModuleString *key, const void *value) {
- REDISMODULE_NOT_USED(key);
- REDISMODULE_NOT_USED(value);
- /* When unlink and unlink2 exist at the same time, we will only call unlink2. */
- RedisModule_Assert(0);
- }
- void MemAllocUnlink2(RedisModuleKeyOptCtx *ctx, const void *value) {
- MemAllocObject *o = (MemAllocObject *)value;
- const RedisModuleString *key = RedisModule_GetKeyNameFromOptCtx(ctx);
- int dbid = RedisModule_GetDbIdFromOptCtx(ctx);
-
- if (o->size) {
- void *oldval;
- RedisModule_DictDel(mem_pool[dbid], (RedisModuleString *)key, &oldval);
- RedisModule_Assert(oldval != NULL);
- MemBlockFree((struct MemBlock *)oldval);
- }
- }
- void MemAllocDigest(RedisModuleDigest *md, void *value) {
- MemAllocObject *o = (MemAllocObject *)value;
- RedisModule_DigestAddLongLong(md, o->size);
- RedisModule_DigestAddLongLong(md, o->used);
- RedisModule_DigestAddLongLong(md, o->mask);
- int dbid = RedisModule_GetDbIdFromDigest(md);
- const RedisModuleString *key = RedisModule_GetKeyNameFromDigest(md);
-
- if (o->size) {
- int nokey;
- struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey);
- RedisModule_Assert(nokey == 0 && mem != NULL);
- struct MemBlock *block = mem;
- while (block) {
- RedisModule_DigestAddStringBuffer(md, (unsigned char *)block->block, BLOCK_SIZE);
- block = block->next;
- }
- }
- }
- void *MemAllocCopy2(RedisModuleKeyOptCtx *ctx, const void *value) {
- const MemAllocObject *old = value;
- MemAllocObject *new = createMemAllocObject();
- new->size = old->size;
- new->used = old->used;
- new->mask = old->mask;
- int from_dbid = RedisModule_GetDbIdFromOptCtx(ctx);
- int to_dbid = RedisModule_GetToDbIdFromOptCtx(ctx);
- const RedisModuleString *fromkey = RedisModule_GetKeyNameFromOptCtx(ctx);
- const RedisModuleString *tokey = RedisModule_GetToKeyNameFromOptCtx(ctx);
- if (old->size) {
- int nokey;
- struct MemBlock *oldmem = (struct MemBlock *)RedisModule_DictGet(mem_pool[from_dbid], (RedisModuleString *)fromkey, &nokey);
- RedisModule_Assert(nokey == 0 && oldmem != NULL);
- struct MemBlock *newmem = MemBlockClone(oldmem);
- RedisModule_Assert(newmem != NULL);
- RedisModule_DictSet(mem_pool[to_dbid], (RedisModuleString *)tokey, newmem);
- }
- return new;
- }
- size_t MemAllocMemUsage2(RedisModuleKeyOptCtx *ctx, const void *value) {
- REDISMODULE_NOT_USED(ctx);
- uint64_t size = 0;
- MemAllocObject *o = (MemAllocObject *)value;
- size += sizeof(*o);
- size += o->size * sizeof(struct MemBlock);
- return size;
- }
- size_t MemAllocMemFreeEffort2(RedisModuleKeyOptCtx *ctx, const void *value) {
- REDISMODULE_NOT_USED(ctx);
- MemAllocObject *o = (MemAllocObject *)value;
- return o->size;
- }
- int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
- REDISMODULE_NOT_USED(argv);
- REDISMODULE_NOT_USED(argc);
- if (RedisModule_Init(ctx, "datatype2", 1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- RedisModuleTypeMethods tm = {
- .version = REDISMODULE_TYPE_METHOD_VERSION,
- .rdb_load = MemAllocRdbLoad,
- .rdb_save = MemAllocRdbSave,
- .aof_rewrite = MemAllocAofRewrite,
- .free = MemAllocFree,
- .digest = MemAllocDigest,
- .unlink = MemAllocUnlink,
- // .defrag = MemAllocDefrag, // Tested in defragtest.c
- .unlink2 = MemAllocUnlink2,
- .copy2 = MemAllocCopy2,
- .mem_usage2 = MemAllocMemUsage2,
- .free_effort2 = MemAllocMemFreeEffort2,
- };
- MemAllocType = RedisModule_CreateDataType(ctx, "mem_alloc", 0, &tm);
- if (MemAllocType == NULL) {
- return REDISMODULE_ERR;
- }
- if (RedisModule_CreateCommand(ctx, "mem.alloc", MemAlloc_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- if (RedisModule_CreateCommand(ctx, "mem.free", MemFree_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- if (RedisModule_CreateCommand(ctx, "mem.write", MemWrite_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- if (RedisModule_CreateCommand(ctx, "mem.read", MemRead_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- if (RedisModule_CreateCommand(ctx, "mem.usage", MemUsage_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- /* used for internal aof rewrite */
- if (RedisModule_CreateCommand(ctx, "mem.allocandwrite", MemAllocAndWrite_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) {
- return REDISMODULE_ERR;
- }
- for(int i = 0; i < MAX_DB; i++){
- mem_pool[i] = RedisModule_CreateDict(NULL);
- }
- RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_FlushDB, flushdbCallback);
- RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_SwapDB, swapDbCallback);
-
- return REDISMODULE_OK;
- }
|