diff options
-rw-r--r-- | src/adlist.c | 2 | ||||
-rw-r--r-- | src/debug.c | 9 | ||||
-rw-r--r-- | src/module.c | 68 | ||||
-rw-r--r-- | src/modules/hellotype.c | 13 | ||||
-rw-r--r-- | src/rdb.c | 4 | ||||
-rw-r--r-- | src/redismodule.h | 6 | ||||
-rw-r--r-- | src/server.c | 6 | ||||
-rw-r--r-- | src/server.h | 18 |
8 files changed, 123 insertions, 3 deletions
diff --git a/src/adlist.c b/src/adlist.c index f0a261b61..e87d25cee 100644 --- a/src/adlist.c +++ b/src/adlist.c @@ -357,6 +357,6 @@ void listJoin(list *l, list *o) { l->len += o->len; /* Setup other as an empty list. */ - o->head = l->tail = NULL; + o->head = o->tail = NULL; o->len = 0; } diff --git a/src/debug.c b/src/debug.c index a4caa49f2..d6e12ec2a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -239,6 +239,15 @@ void computeDatasetDigest(unsigned char *final) { xorDigest(digest,eledigest,20); } hashTypeReleaseIterator(hi); + } else if (o->type == OBJ_MODULE) { + RedisModuleDigest md; + moduleValue *mv = o->ptr; + moduleType *mt = mv->type; + moduleInitDigestContext(md); + if (mt->digest) { + mt->digest(&md,mv->value); + xorDigest(digest,md.x,sizeof(md.x)); + } } else { serverPanic("Unknown object type"); } diff --git a/src/module.c b/src/module.c index ac00867e4..a85307ccd 100644 --- a/src/module.c +++ b/src/module.c @@ -1163,7 +1163,12 @@ int RM_ReplyWithDouble(RedisModuleCtx *ctx, double d) { * in the context of a command execution. EXEC will be handled by the * RedisModuleCommandDispatcher() function. */ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { + /* If we already emitted MULTI return ASAP. */ if (ctx->flags & REDISMODULE_CTX_MULTI_EMITTED) return; + /* If this is a thread safe context, we do not want to wrap commands + * executed into MUTLI/EXEC, they are executed as single commands + * from an external client in essence. */ + if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) return; execCommandPropagateMulti(ctx->client); ctx->flags |= REDISMODULE_CTX_MULTI_EMITTED; } @@ -3053,6 +3058,66 @@ loaderr: } /* -------------------------------------------------------------------------- + * Key digest API (DEBUG DIGEST interface for modules types) + * -------------------------------------------------------------------------- */ + +/* Add a new element to the digest. This function can be called multiple times + * one element after the other, for all the elements that constitute a given + * data structure. The function call must be followed by the call to + * `RedisModule_DigestEndSequence` eventually, when all the elements that are + * always in a given order are added. See the Redis Modules data types + * documentation for more info. However this is a quick example that uses Redis + * data types as an example. + * + * To add a sequence of unordered elements (for example in the case of a Redis + * Set), the pattern to use is: + * + * foreach element { + * AddElement(element); + * EndSequence(); + * } + * + * Because Sets are not ordered, so every element added has a position that + * does not depend from the other. However if instead our elements are + * ordered in pairs, like field-value pairs of an Hash, then one should + * use: + * + * foreach key,value { + * AddElement(key); + * AddElement(value); + * EndSquence(); + * } + * + * Because the key and value will be always in the above order, while instead + * the single key-value pairs, can appear in any position into a Redis hash. + * + * A list of ordered elements would be implemented with: + * + * foreach element { + * AddElement(element); + * } + * EndSequence(); + * + */ +void RM_DigestAddStringBuffer(RedisModuleDigest *md, unsigned char *ele, size_t len) { + mixDigest(md->o,ele,len); +} + +/* Like `RedisModule_DigestAddStringBuffer()` but takes a long long as input + * that gets converted into a string before adding it to the digest. */ +void RM_DigestAddLongLong(RedisModuleDigest *md, long long ll) { + char buf[LONG_STR_SIZE]; + size_t len = ll2string(buf,sizeof(buf),ll); + mixDigest(md->o,buf,len); +} + +/* See the doucmnetation for `RedisModule_DigestAddElement()`. */ +void RM_DigestEndSequence(RedisModuleDigest *md) { + xorDigest(md->x,md->o,sizeof(md->o)); + memset(md->o,0,sizeof(md->o)); +} + +/* -------------------------------------------------------------------------- * AOF API for modules data types * -------------------------------------------------------------------------- */ @@ -3813,4 +3878,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(FreeThreadSafeContext); REGISTER_API(ThreadSafeContextLock); REGISTER_API(ThreadSafeContextUnlock); + REGISTER_API(DigestAddStringBuffer); + REGISTER_API(DigestAddLongLong); + REGISTER_API(DigestEndSequence); } diff --git a/src/modules/hellotype.c b/src/modules/hellotype.c index 027155d45..ba634c4a1 100644 --- a/src/modules/hellotype.c +++ b/src/modules/hellotype.c @@ -238,6 +238,16 @@ void HelloTypeFree(void *value) { HelloTypeReleaseObject(value); } +void HelloTypeDigest(RedisModuleDigest *md, void *value) { + struct HelloTypeObject *hto = value; + struct HelloTypeNode *node = hto->head; + while(node) { + RedisModule_DigestAddLongLong(md,node->value); + node = node->next; + } + RedisModule_DigestEndSequence(md); +} + /* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { @@ -253,7 +263,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) .rdb_save = HelloTypeRdbSave, .aof_rewrite = HelloTypeAofRewrite, .mem_usage = HelloTypeMemUsage, - .free = HelloTypeFree + .free = HelloTypeFree, + .digest = HelloTypeDigest }; HelloType = RedisModule_CreateDataType(ctx,"hellotype",0,&tm); @@ -1415,6 +1415,10 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { /* Call the rdb_load method of the module providing the 10 bit * encoding version in the lower 10 bits of the module ID. */ void *ptr = mt->rdb_load(&io,moduleid&1023); + if (io.ctx) { + moduleFreeContext(io.ctx); + zfree(io.ctx); + } /* Module v2 serialization has an EOF mark at the end. */ if (io.ver == 2) { diff --git a/src/redismodule.h b/src/redismodule.h index 2f2e3c923..dd14c5f4e 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -218,6 +218,9 @@ RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModu void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); +void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); +void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); +void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); /* This is included inline inside each Redis module. */ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); @@ -330,6 +333,9 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(FreeThreadSafeContext); REDISMODULE_GET_API(ThreadSafeContextLock); REDISMODULE_GET_API(ThreadSafeContextUnlock); + REDISMODULE_GET_API(DigestAddStringBuffer); + REDISMODULE_GET_API(DigestAddLongLong); + REDISMODULE_GET_API(DigestEndSequence); RedisModule_SetModuleAttribs(ctx,name,ver,apiver); return REDISMODULE_OK; diff --git a/src/server.c b/src/server.c index a75581b97..57ee43e38 100644 --- a/src/server.c +++ b/src/server.c @@ -1555,7 +1555,11 @@ int restartServer(int flags, mstime_t delay) { /* Close all file descriptors, with the exception of stdin, stdout, strerr * which are useful if we restart a Redis server which is not daemonized. */ - for (j = 3; j < (int)server.maxclients + 1024; j++) close(j); + for (j = 3; j < (int)server.maxclients + 1024; j++) { + /* Test the descriptor validity before closing it, otherwise + * Valgrind issues a warning on close(). */ + if (fcntl(j,F_GETFD) != -1) close(j); + } /* Execute the server with the original command line. */ if (delay) usleep(delay*1000); diff --git a/src/server.h b/src/server.h index 64451f5c6..88690ace4 100644 --- a/src/server.h +++ b/src/server.h @@ -546,6 +546,22 @@ typedef struct RedisModuleIO { iovar.ctx = NULL; \ } while(0); +/* This is a structure used to export DEBUG DIGEST capabilities to Redis + * modules. We want to capture both the ordered and unordered elements of + * a data structure, so that a digest can be created in a way that correctly + * reflects the values. See the DEBUG DIGEST command implementation for more + * background. */ +typedef struct RedisModuleDigest { + unsigned char o[20]; /* Ordered elements. */ + unsigned char x[20]; /* Xored elements. */ +} RedisModuleDigest; + +/* Just start with a digest composed of all zero bytes. */ +#define moduleInitDigestContext(mdvar) do { \ + memset(mdvar.o,0,sizeof(mdvar.o)); \ + memset(mdvar.x,0,sizeof(mdvar.x)); \ +} while(0); + /* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */ @@ -1993,6 +2009,8 @@ void disableWatchdog(void); void watchdogScheduleSignal(int period); void serverLogHexDump(int level, char *descr, void *value, size_t len); int memtest_preserving_test(unsigned long *m, size_t bytes, int passes); +void mixDigest(unsigned char *digest, void *ptr, size_t len); +void xorDigest(unsigned char *digest, void *ptr, size_t len); #define redisDebug(fmt, ...) \ printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__) |