summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/adlist.c2
-rw-r--r--src/debug.c9
-rw-r--r--src/module.c68
-rw-r--r--src/modules/hellotype.c13
-rw-r--r--src/rdb.c4
-rw-r--r--src/redismodule.h6
-rw-r--r--src/server.c6
-rw-r--r--src/server.h18
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);
diff --git a/src/rdb.c b/src/rdb.c
index 18acb4195..1341942c4 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -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__)