diff options
author | antirez <antirez@gmail.com> | 2017-07-06 10:29:19 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2017-07-06 11:04:46 +0200 |
commit | 51ffd062d37b370884e21cc70b20264ff4060dc8 (patch) | |
tree | e75ac9bcc39685c0d939a1c1e2f1df22807ebcd0 | |
parent | f9fac7f7770f056790b628e47af91ed3e814246c (diff) | |
download | redis-51ffd062d37b370884e21cc70b20264ff4060dc8.tar.gz |
Modules: DEBUG DIGEST interface.
-rw-r--r-- | src/debug.c | 9 | ||||
-rw-r--r-- | src/module.c | 63 | ||||
-rw-r--r-- | src/modules/hellotype.c | 13 | ||||
-rw-r--r-- | src/redismodule.h | 6 | ||||
-rw-r--r-- | src/server.h | 18 |
5 files changed, 108 insertions, 1 deletions
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 e377d2712..a85307ccd 100644 --- a/src/module.c +++ b/src/module.c @@ -3058,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 * -------------------------------------------------------------------------- */ @@ -3818,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); 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.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__) |