Unverified Commit ec2d1807 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge 6.2 RC2

parents b8c67ce4 049f2f08
...@@ -646,13 +646,13 @@ dictEntry *dictGetRandomKey(dict *d) ...@@ -646,13 +646,13 @@ dictEntry *dictGetRandomKey(dict *d)
do { do {
/* We are sure there are no elements in indexes from 0 /* We are sure there are no elements in indexes from 0
* to rehashidx-1 */ * to rehashidx-1 */
h = d->rehashidx + (random() % (dictSlots(d) - d->rehashidx)); h = d->rehashidx + (randomULong() % (dictSlots(d) - d->rehashidx));
he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] : he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
d->ht[0].table[h]; d->ht[0].table[h];
} while(he == NULL); } while(he == NULL);
} else { } else {
do { do {
h = random() & d->ht[0].sizemask; h = randomULong() & d->ht[0].sizemask;
he = d->ht[0].table[h]; he = d->ht[0].table[h];
} while(he == NULL); } while(he == NULL);
} }
...@@ -718,7 +718,7 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) { ...@@ -718,7 +718,7 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
maxsizemask = d->ht[1].sizemask; maxsizemask = d->ht[1].sizemask;
/* Pick a random point inside the larger table. */ /* Pick a random point inside the larger table. */
unsigned long i = random() & maxsizemask; unsigned long i = randomULong() & maxsizemask;
unsigned long emptylen = 0; /* Continuous empty entries so far. */ unsigned long emptylen = 0; /* Continuous empty entries so far. */
while(stored < count && maxsteps--) { while(stored < count && maxsteps--) {
for (j = 0; j < tables; j++) { for (j = 0; j < tables; j++) {
...@@ -743,7 +743,7 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) { ...@@ -743,7 +743,7 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
if (he == NULL) { if (he == NULL) {
emptylen++; emptylen++;
if (emptylen >= 5 && emptylen > count) { if (emptylen >= 5 && emptylen > count) {
i = random() & maxsizemask; i = randomULong() & maxsizemask;
emptylen = 0; emptylen = 0;
} }
} else { } else {
...@@ -1135,10 +1135,10 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) { ...@@ -1135,10 +1135,10 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {
/* Generate human readable stats. */ /* Generate human readable stats. */
l += snprintf(buf+l,bufsize-l, l += snprintf(buf+l,bufsize-l,
"Hash table %d stats (%s):\n" "Hash table %d stats (%s):\n"
" table size: %ld\n" " table size: %lu\n"
" number of elements: %ld\n" " number of elements: %lu\n"
" different slots: %ld\n" " different slots: %lu\n"
" max chain length: %ld\n" " max chain length: %lu\n"
" avg chain length (counted): %.02f\n" " avg chain length (counted): %.02f\n"
" avg chain length (computed): %.02f\n" " avg chain length (computed): %.02f\n"
" Chain length distribution:\n", " Chain length distribution:\n",
...@@ -1215,7 +1215,7 @@ dictType BenchmarkDictType = { ...@@ -1215,7 +1215,7 @@ dictType BenchmarkDictType = {
#define end_benchmark(msg) do { \ #define end_benchmark(msg) do { \
elapsed = timeInMilliseconds()-start; \ elapsed = timeInMilliseconds()-start; \
printf(msg ": %ld items in %lld ms\n", count, elapsed); \ printf(msg ": %ld items in %lld ms\n", count, elapsed); \
} while(0); } while(0)
/* dict-benchmark [count] */ /* dict-benchmark [count] */
int main(int argc, char **argv) { int main(int argc, char **argv) {
...@@ -1270,6 +1270,13 @@ int main(int argc, char **argv) { ...@@ -1270,6 +1270,13 @@ int main(int argc, char **argv) {
} }
end_benchmark("Random access of existing elements"); end_benchmark("Random access of existing elements");
start_benchmark();
for (j = 0; j < count; j++) {
dictEntry *de = dictGetRandomKey(dict);
assert(de != NULL);
}
end_benchmark("Accessing random keys");
start_benchmark(); start_benchmark();
for (j = 0; j < count; j++) { for (j = 0; j < count; j++) {
sds key = sdsfromlonglong(rand() % count); sds key = sdsfromlonglong(rand() % count);
......
...@@ -33,11 +33,14 @@ ...@@ -33,11 +33,14 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <stdint.h>
#ifndef __DICT_H #ifndef __DICT_H
#define __DICT_H #define __DICT_H
#include "mt19937-64.h"
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#define DICT_OK 0 #define DICT_OK 0
#define DICT_ERR 1 #define DICT_ERR 1
...@@ -148,6 +151,13 @@ typedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref); ...@@ -148,6 +151,13 @@ typedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref);
#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used) #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
#define dictIsRehashing(d) ((d)->rehashidx != -1) #define dictIsRehashing(d) ((d)->rehashidx != -1)
/* If our unsigned long type can store a 64 bit number, use a 64 bit PRNG. */
#if ULONG_MAX >= 0xffffffffffffffff
#define randomULong() ((unsigned long) genrand64_int64())
#else
#define randomULong() random()
#endif
/* API */ /* API */
dict *dictCreate(dictType *type, void *privDataPtr); dict *dictCreate(dictType *type, void *privDataPtr);
int dictExpand(dict *d, unsigned long size); int dictExpand(dict *d, unsigned long size);
......
...@@ -462,7 +462,7 @@ static int isSafeToPerformEvictions(void) { ...@@ -462,7 +462,7 @@ static int isSafeToPerformEvictions(void) {
/* When clients are paused the dataset should be static not just from the /* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of * POV of clients not being able to write, but also from the POV of
* expires and evictions of keys not being performed. */ * expires and evictions of keys not being performed. */
if (clientsArePaused()) return 0; if (checkClientPauseTimeoutAndReturnIfPaused()) return 0;
return 1; return 1;
} }
......
...@@ -148,7 +148,7 @@ void activeExpireCycle(int type) { ...@@ -148,7 +148,7 @@ void activeExpireCycle(int type) {
/* When clients are paused the dataset should be static not just from the /* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of * POV of clients not being able to write, but also from the POV of
* expires and evictions of keys not being performed. */ * expires and evictions of keys not being performed. */
if (clientsArePaused()) return; if (checkClientPauseTimeoutAndReturnIfPaused()) return;
if (type == ACTIVE_EXPIRE_CYCLE_FAST) { if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
/* Don't start a fast cycle if the previous cycle did not exit /* Don't start a fast cycle if the previous cycle did not exit
......
...@@ -100,8 +100,8 @@ int extractLongLatOrReply(client *c, robj **argv, double *xy) { ...@@ -100,8 +100,8 @@ int extractLongLatOrReply(client *c, robj **argv, double *xy) {
} }
if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX || if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX ||
xy[1] < GEO_LAT_MIN || xy[1] > GEO_LAT_MAX) { xy[1] < GEO_LAT_MIN || xy[1] > GEO_LAT_MAX) {
addReplySds(c, sdscatprintf(sdsempty(), addReplyErrorFormat(c,
"-ERR invalid longitude,latitude pair %f,%f\r\n",xy[0],xy[1])); "-ERR invalid longitude,latitude pair %f,%f\r\n",xy[0],xy[1]);
return C_ERR; return C_ERR;
} }
return C_OK; return C_OK;
...@@ -249,7 +249,7 @@ int geoAppendIfWithinShape(geoArray *ga, GeoShape *shape, double score, sds memb ...@@ -249,7 +249,7 @@ int geoAppendIfWithinShape(geoArray *ga, GeoShape *shape, double score, sds memb
* using multiple queries to the sorted set, that we later need to sort * using multiple queries to the sorted set, that we later need to sort
* via qsort. Similarly we need to be able to reject points outside the search * via qsort. Similarly we need to be able to reject points outside the search
* radius area ASAP in order to allocate and process more points than needed. */ * radius area ASAP in order to allocate and process more points than needed. */
int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geoArray *ga) { int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geoArray *ga, unsigned long limit) {
/* minex 0 = include min in range; maxex 1 = exclude max in range */ /* minex 0 = include min in range; maxex 1 = exclude max in range */
/* That's: min <= val < max */ /* That's: min <= val < max */
zrangespec range = { .min = min, .max = max, .minex = 0, .maxex = 1 }; zrangespec range = { .min = min, .max = max, .minex = 0, .maxex = 1 };
...@@ -283,6 +283,7 @@ int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geo ...@@ -283,6 +283,7 @@ int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geo
sdsnewlen(vstr,vlen); sdsnewlen(vstr,vlen);
if (geoAppendIfWithinShape(ga,shape,score,member) if (geoAppendIfWithinShape(ga,shape,score,member)
== C_ERR) sdsfree(member); == C_ERR) sdsfree(member);
if (ga->used && limit && ga->used >= limit) break;
zzlNext(zl, &eptr, &sptr); zzlNext(zl, &eptr, &sptr);
} }
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
...@@ -304,6 +305,7 @@ int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geo ...@@ -304,6 +305,7 @@ int geoGetPointsInRange(robj *zobj, double min, double max, GeoShape *shape, geo
ele = sdsdup(ele); ele = sdsdup(ele);
if (geoAppendIfWithinShape(ga,shape,ln->score,ele) if (geoAppendIfWithinShape(ga,shape,ln->score,ele)
== C_ERR) sdsfree(ele); == C_ERR) sdsfree(ele);
if (ga->used && limit && ga->used >= limit) break;
ln = ln->level[0].forward; ln = ln->level[0].forward;
} }
} }
...@@ -342,15 +344,15 @@ void scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bit ...@@ -342,15 +344,15 @@ void scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bit
/* Obtain all members between the min/max of this geohash bounding box. /* Obtain all members between the min/max of this geohash bounding box.
* Populate a geoArray of GeoPoints by calling geoGetPointsInRange(). * Populate a geoArray of GeoPoints by calling geoGetPointsInRange().
* Return the number of points added to the array. */ * Return the number of points added to the array. */
int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, GeoShape *shape) { int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, GeoShape *shape, unsigned long limit) {
GeoHashFix52Bits min, max; GeoHashFix52Bits min, max;
scoresOfGeoHashBox(hash,&min,&max); scoresOfGeoHashBox(hash,&min,&max);
return geoGetPointsInRange(zobj, min, max, shape, ga); return geoGetPointsInRange(zobj, min, max, shape, ga, limit);
} }
/* Search all eight neighbors + self geohash box */ /* Search all eight neighbors + self geohash box */
int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, GeoShape *shape, geoArray *ga) { int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, GeoShape *shape, geoArray *ga, unsigned long limit) {
GeoHashBits neighbors[9]; GeoHashBits neighbors[9];
unsigned int i, count = 0, last_processed = 0; unsigned int i, count = 0, last_processed = 0;
int debugmsg = 0; int debugmsg = 0;
...@@ -401,7 +403,8 @@ int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, GeoShape *shape, geoArray ...@@ -401,7 +403,8 @@ int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, GeoShape *shape, geoArray
D("Skipping processing of %d, same as previous\n",i); D("Skipping processing of %d, same as previous\n",i);
continue; continue;
} }
count += membersOfGeoHashBox(zobj, neighbors[i], ga, shape); if (ga->used && limit && ga->used >= limit) break;
count += membersOfGeoHashBox(zobj, neighbors[i], ga, shape, limit);
last_processed = i; last_processed = i;
} }
return count; return count;
...@@ -428,31 +431,45 @@ static int sort_gp_desc(const void *a, const void *b) { ...@@ -428,31 +431,45 @@ static int sort_gp_desc(const void *a, const void *b) {
* Commands * Commands
* ==================================================================== */ * ==================================================================== */
/* GEOADD key long lat name [long2 lat2 name2 ... longN latN nameN] */ /* GEOADD key [CH] [NX|XX] long lat name [long2 lat2 name2 ... longN latN nameN] */
void geoaddCommand(client *c) { void geoaddCommand(client *c) {
/* Check arguments number for sanity. */ int xx = 0, nx = 0, longidx = 2;
if ((c->argc - 2) % 3 != 0) { int i;
/* Parse options. At the end 'longidx' is set to the argument position
* of the longitude of the first element. */
while (longidx < c->argc) {
char *opt = c->argv[longidx]->ptr;
if (!strcasecmp(opt,"nx")) nx = 1;
else if (!strcasecmp(opt,"xx")) xx = 1;
else if (!strcasecmp(opt,"ch")) {}
else break;
longidx++;
}
if ((c->argc - longidx) % 3 || (xx && nx)) {
/* Need an odd number of arguments if we got this far... */ /* Need an odd number of arguments if we got this far... */
addReplyError(c, "syntax error. Try GEOADD key [x1] [y1] [name1] " addReplyErrorObject(c,shared.syntaxerr);
"[x2] [y2] [name2] ... ");
return; return;
} }
int elements = (c->argc - 2) / 3; /* Set up the vector for calling ZADD. */
int argc = 2+elements*2; /* ZADD key score ele ... */ int elements = (c->argc - longidx) / 3;
int argc = longidx+elements*2; /* ZADD key [CH] [NX|XX] score ele ... */
robj **argv = zcalloc(argc*sizeof(robj*)); robj **argv = zcalloc(argc*sizeof(robj*));
argv[0] = createRawStringObject("zadd",4); argv[0] = createRawStringObject("zadd",4);
argv[1] = c->argv[1]; /* key */ for (i = 1; i < longidx; i++) {
incrRefCount(argv[1]); argv[i] = c->argv[i];
incrRefCount(argv[i]);
}
/* Create the argument vector to call ZADD in order to add all /* Create the argument vector to call ZADD in order to add all
* the score,value pairs to the requested zset, where score is actually * the score,value pairs to the requested zset, where score is actually
* an encoded version of lat,long. */ * an encoded version of lat,long. */
int i;
for (i = 0; i < elements; i++) { for (i = 0; i < elements; i++) {
double xy[2]; double xy[2];
if (extractLongLatOrReply(c, (c->argv+2)+(i*3),xy) == C_ERR) { if (extractLongLatOrReply(c, (c->argv+longidx)+(i*3),xy) == C_ERR) {
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
if (argv[i]) decrRefCount(argv[i]); if (argv[i]) decrRefCount(argv[i]);
zfree(argv); zfree(argv);
...@@ -464,9 +481,9 @@ void geoaddCommand(client *c) { ...@@ -464,9 +481,9 @@ void geoaddCommand(client *c) {
geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash); geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);
GeoHashFix52Bits bits = geohashAlign52Bits(hash); GeoHashFix52Bits bits = geohashAlign52Bits(hash);
robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits)); robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));
robj *val = c->argv[2 + i * 3 + 2]; robj *val = c->argv[longidx + i * 3 + 2];
argv[2+i*2] = score; argv[longidx+i*2] = score;
argv[3+i*2] = val; argv[longidx+1+i*2] = val;
incrRefCount(val); incrRefCount(val);
} }
...@@ -486,12 +503,12 @@ void geoaddCommand(client *c) { ...@@ -486,12 +503,12 @@ void geoaddCommand(client *c) {
#define GEOSEARCHSTORE (1<<4) /* GEOSEARCHSTORE just accept STOREDIST option */ #define GEOSEARCHSTORE (1<<4) /* GEOSEARCHSTORE just accept STOREDIST option */
/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC] /* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
* [COUNT count] [STORE key] [STOREDIST key] * [COUNT count [ANY]] [STORE key] [STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ... * GEORADIUSBYMEMBER key member radius unit ... options ...
* GEOSEARCH key [FROMMEMBER member] [FORMLOG long lat] [BYRADIUS radius unit] * GEOSEARCH key [FROMMEMBER member] [FROMLONLAT long lat] [BYRADIUS radius unit]
* [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT count] [ASC|DESC] * [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT count [ANY]] [ASC|DESC]
* GEOSEARCHSTORE dest_key src_key [FROMMEMBER member] [FORMLOG long lat] [BYRADIUS radius unit] * GEOSEARCHSTORE dest_key src_key [FROMMEMBER member] [FROMLONLAT long lat] [BYRADIUS radius unit]
* [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT count] [ASC|DESC] [STOREDIST] * [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT count [ANY]] [ASC|DESC] [STOREDIST]
* */ * */
void georadiusGeneric(client *c, int srcKeyIndex, int flags) { void georadiusGeneric(client *c, int srcKeyIndex, int flags) {
robj *storekey = NULL; robj *storekey = NULL;
...@@ -536,7 +553,8 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { ...@@ -536,7 +553,8 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) {
int withdist = 0, withhash = 0, withcoords = 0; int withdist = 0, withhash = 0, withcoords = 0;
int frommember = 0, fromloc = 0, byradius = 0, bybox = 0; int frommember = 0, fromloc = 0, byradius = 0, bybox = 0;
int sort = SORT_NONE; int sort = SORT_NONE;
long long count = 0; int any = 0; /* any=1 means a limited search, stop as soon as enough results were found. */
long long count = 0; /* Max number of results to return. 0 means unlimited. */
if (c->argc > base_args) { if (c->argc > base_args) {
int remaining = c->argc - base_args; int remaining = c->argc - base_args;
for (int i = 0; i < remaining; i++) { for (int i = 0; i < remaining; i++) {
...@@ -547,6 +565,8 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { ...@@ -547,6 +565,8 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) {
withhash = 1; withhash = 1;
} else if (!strcasecmp(arg, "withcoord")) { } else if (!strcasecmp(arg, "withcoord")) {
withcoords = 1; withcoords = 1;
} else if (!strcasecmp(arg, "any")) {
any = 1;
} else if (!strcasecmp(arg, "asc")) { } else if (!strcasecmp(arg, "asc")) {
sort = SORT_ASC; sort = SORT_ASC;
} else if (!strcasecmp(arg, "desc")) { } else if (!strcasecmp(arg, "desc")) {
...@@ -620,7 +640,7 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { ...@@ -620,7 +640,7 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) {
bybox = 1; bybox = 1;
i += 3; i += 3;
} else { } else {
addReply(c, shared.syntaxerr); addReplyErrorObject(c,shared.syntaxerr);
return; return;
} }
} }
...@@ -648,16 +668,23 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { ...@@ -648,16 +668,23 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) {
return; return;
} }
/* COUNT without ordering does not make much sense, force ASC if (any && !count) {
* ordering if COUNT was specified but no sorting was requested. */ addReplyErrorFormat(c, "the ANY argument requires COUNT argument");
if (count != 0 && sort == SORT_NONE) sort = SORT_ASC; return;
}
/* COUNT without ordering does not make much sense (we need to
* sort in order to return the closest N entries),
* force ASC ordering if COUNT was specified but no sorting was
* requested. Note that this is not needed for ANY option. */
if (count != 0 && sort == SORT_NONE && !any) sort = SORT_ASC;
/* Get all neighbor geohash boxes for our radius search */ /* Get all neighbor geohash boxes for our radius search */
GeoHashRadius georadius = geohashCalculateAreasByShapeWGS84(&shape); GeoHashRadius georadius = geohashCalculateAreasByShapeWGS84(&shape);
/* Search the zset for all matching points */ /* Search the zset for all matching points */
geoArray *ga = geoArrayCreate(); geoArray *ga = geoArrayCreate();
membersOfAllNeighbors(zobj, georadius, &shape, ga); membersOfAllNeighbors(zobj, georadius, &shape, ga, any ? count : 0);
/* If no matching results, the user gets an empty reply. */ /* If no matching results, the user gets an empty reply. */
if (ga->used == 0 && storekey == NULL) { if (ga->used == 0 && storekey == NULL) {
...@@ -902,7 +929,7 @@ void geodistCommand(client *c) { ...@@ -902,7 +929,7 @@ void geodistCommand(client *c) {
to_meter = extractUnitOrReply(c,c->argv[4]); to_meter = extractUnitOrReply(c,c->argv[4]);
if (to_meter < 0) return; if (to_meter < 0) return;
} else if (c->argc > 5) { } else if (c->argc > 5) {
addReply(c,shared.syntaxerr); addReplyErrorObject(c,shared.syntaxerr);
return; return;
} }
......
...@@ -194,7 +194,7 @@ struct commandHelp { ...@@ -194,7 +194,7 @@ struct commandHelp {
8, 8,
"2.4.0" }, "2.4.0" },
{ "CLIENT PAUSE", { "CLIENT PAUSE",
"timeout", "timeout [WRITE|ALL]",
"Stop processing commands from clients for some time", "Stop processing commands from clients for some time",
8, 8,
"2.9.50" }, "2.9.50" },
...@@ -213,11 +213,21 @@ struct commandHelp { ...@@ -213,11 +213,21 @@ struct commandHelp {
"Enable or disable server assisted client side caching support", "Enable or disable server assisted client side caching support",
8, 8,
"6.0.0" }, "6.0.0" },
{ "CLIENT TRACKINGINFO",
"-",
"Return information about server assisted client side caching for the current connection",
8,
"6.2.0" },
{ "CLIENT UNBLOCK", { "CLIENT UNBLOCK",
"client-id [TIMEOUT|ERROR]", "client-id [TIMEOUT|ERROR]",
"Unblock a client blocked in a blocking command from a different connection", "Unblock a client blocked in a blocking command from a different connection",
8, 8,
"5.0.0" }, "5.0.0" },
{ "CLIENT UNPAUSE",
"-",
"Resume processing of clients that were paused",
8,
"6.2.0" },
{ "CLUSTER ADDSLOTS", { "CLUSTER ADDSLOTS",
"slot [slot ...]", "slot [slot ...]",
"Assign new hash slots to receiving node", "Assign new hash slots to receiving node",
...@@ -459,7 +469,7 @@ struct commandHelp { ...@@ -459,7 +469,7 @@ struct commandHelp {
9, 9,
"1.0.0" }, "1.0.0" },
{ "GEOADD", { "GEOADD",
"key longitude latitude member [longitude latitude member ...]", "key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]",
"Add one or more geospatial items in the geospatial index represented using a sorted set", "Add one or more geospatial items in the geospatial index represented using a sorted set",
13, 13,
"3.2.0" }, "3.2.0" },
...@@ -479,22 +489,22 @@ struct commandHelp { ...@@ -479,22 +489,22 @@ struct commandHelp {
13, 13,
"3.2.0" }, "3.2.0" },
{ "GEORADIUS", { "GEORADIUS",
"key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]", "key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]",
"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point", "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point",
13, 13,
"3.2.0" }, "3.2.0" },
{ "GEORADIUSBYMEMBER", { "GEORADIUSBYMEMBER",
"key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]", "key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]",
"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member", "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member",
13, 13,
"3.2.0" }, "3.2.0" },
{ "GEOSEARCH", { "GEOSEARCH",
"key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count] [WITHCOORD] [WITHDIST] [WITHHASH]", "key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]",
"Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.", "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.",
13, 13,
"6.2" }, "6.2" },
{ "GEOSEARCHSTORE", { "GEOSEARCHSTORE",
"destination source [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count] [WITHCOORD] [WITHDIST] [WITHHASH] [STOREDIST]", "destination source [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH] [STOREDIST]",
"Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.", "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.",
13, 13,
"6.2" }, "6.2" },
...@@ -524,8 +534,8 @@ struct commandHelp { ...@@ -524,8 +534,8 @@ struct commandHelp {
5, 5,
"2.0.0" }, "2.0.0" },
{ "HELLO", { "HELLO",
"protover [AUTH username password] [SETNAME clientname]", "[protover [AUTH username password] [SETNAME clientname]]",
"switch Redis protocol", "Handshake with Redis",
8, 8,
"6.0.0" }, "6.0.0" },
{ "HEXISTS", { "HEXISTS",
...@@ -684,8 +694,8 @@ struct commandHelp { ...@@ -684,8 +694,8 @@ struct commandHelp {
9, 9,
"5.0.0" }, "5.0.0" },
{ "LPOP", { "LPOP",
"key", "key [count]",
"Remove and get the first element in a list", "Remove and get the first elements in a list",
2, 2,
"1.0.0" }, "1.0.0" },
{ "LPOS", { "LPOS",
...@@ -929,8 +939,8 @@ struct commandHelp { ...@@ -929,8 +939,8 @@ struct commandHelp {
9, 9,
"2.8.12" }, "2.8.12" },
{ "RPOP", { "RPOP",
"key", "key [count]",
"Remove and get the last element in a list", "Remove and get the last elements in a list",
2, 2,
"1.0.0" }, "1.0.0" },
{ "RPOPLPUSH", { "RPOPLPUSH",
...@@ -1189,10 +1199,15 @@ struct commandHelp { ...@@ -1189,10 +1199,15 @@ struct commandHelp {
14, 14,
"5.0.0" }, "5.0.0" },
{ "XADD", { "XADD",
"key [MAXLEN [=|~] length] [NOMKSTREAM] *|ID field value [field value ...]", "key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...]",
"Appends a new entry to a stream", "Appends a new entry to a stream",
14, 14,
"5.0.0" }, "5.0.0" },
{ "XAUTOCLAIM",
"key group consumer min-idle-time start [COUNT count] [justid]",
"Changes (or acquires) ownership of messages in a consumer group, as if the messages were delivered to the specified consumer.",
14,
"6.2.0" },
{ "XCLAIM", { "XCLAIM",
"key group consumer min-idle-time ID [ID ...] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid]", "key group consumer min-idle-time ID [ID ...] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid]",
"Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.", "Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.",
...@@ -1244,7 +1259,7 @@ struct commandHelp { ...@@ -1244,7 +1259,7 @@ struct commandHelp {
14, 14,
"5.0.0" }, "5.0.0" },
{ "XTRIM", { "XTRIM",
"key MAXLEN [=|~] length", "key MAXLEN|MINID [=|~] threshold [LIMIT count]",
"Trims the stream to (approximately if '~' is passed) a certain size", "Trims the stream to (approximately if '~' is passed) a certain size",
14, 14,
"5.0.0" }, "5.0.0" },
...@@ -1309,8 +1324,8 @@ struct commandHelp { ...@@ -1309,8 +1324,8 @@ struct commandHelp {
4, 4,
"5.0.0" }, "5.0.0" },
{ "ZRANGE", { "ZRANGE",
"key start stop [WITHSCORES]", "key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]",
"Return a range of members in a sorted set, by index", "Return a range of members in a sorted set",
4, 4,
"1.2.0" }, "1.2.0" },
{ "ZRANGEBYLEX", { "ZRANGEBYLEX",
...@@ -1323,6 +1338,11 @@ struct commandHelp { ...@@ -1323,6 +1338,11 @@ struct commandHelp {
"Return a range of members in a sorted set, by score", "Return a range of members in a sorted set, by score",
4, 4,
"1.0.5" }, "1.0.5" },
{ "ZRANGESTORE",
"dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]",
"Store a range of members from sorted set into another key",
4,
"6.2.0" },
{ "ZRANK", { "ZRANK",
"key member", "key member",
"Determine the index of a member in a sorted set", "Determine the index of a member in a sorted set",
......
...@@ -205,7 +205,7 @@ struct hllhdr { ...@@ -205,7 +205,7 @@ struct hllhdr {
#define HLL_RAW 255 /* Only used internally, never exposed. */ #define HLL_RAW 255 /* Only used internally, never exposed. */
#define HLL_MAX_ENCODING 1 #define HLL_MAX_ENCODING 1
static char *invalid_hll_err = "-INVALIDOBJ Corrupted HLL object detected\r\n"; static char *invalid_hll_err = "-INVALIDOBJ Corrupted HLL object detected";
/* =========================== Low level bit macros ========================= */ /* =========================== Low level bit macros ========================= */
...@@ -1171,9 +1171,8 @@ int isHLLObjectOrReply(client *c, robj *o) { ...@@ -1171,9 +1171,8 @@ int isHLLObjectOrReply(client *c, robj *o) {
return C_OK; return C_OK;
invalid: invalid:
addReplySds(c, addReplyError(c,"-WRONGTYPE Key is not a valid "
sdsnew("-WRONGTYPE Key is not a valid " "HyperLogLog string value.");
"HyperLogLog string value.\r\n"));
return C_ERR; return C_ERR;
} }
...@@ -1203,7 +1202,7 @@ void pfaddCommand(client *c) { ...@@ -1203,7 +1202,7 @@ void pfaddCommand(client *c) {
updated++; updated++;
break; break;
case -1: case -1:
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
} }
...@@ -1211,7 +1210,7 @@ void pfaddCommand(client *c) { ...@@ -1211,7 +1210,7 @@ void pfaddCommand(client *c) {
if (updated) { if (updated) {
signalModifiedKey(c,c->db,c->argv[1]); signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id); notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id);
server.dirty++; server.dirty += updated;
HLL_INVALIDATE_CACHE(hdr); HLL_INVALIDATE_CACHE(hdr);
} }
addReply(c, updated ? shared.cone : shared.czero); addReply(c, updated ? shared.cone : shared.czero);
...@@ -1245,7 +1244,7 @@ void pfcountCommand(client *c) { ...@@ -1245,7 +1244,7 @@ void pfcountCommand(client *c) {
/* Merge with this HLL with our 'max' HLL by setting max[i] /* Merge with this HLL with our 'max' HLL by setting max[i]
* to MAX(max[i],hll[i]). */ * to MAX(max[i],hll[i]). */
if (hllMerge(registers,o) == C_ERR) { if (hllMerge(registers,o) == C_ERR) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
} }
...@@ -1285,7 +1284,7 @@ void pfcountCommand(client *c) { ...@@ -1285,7 +1284,7 @@ void pfcountCommand(client *c) {
/* Recompute it and update the cached value. */ /* Recompute it and update the cached value. */
card = hllCount(hdr,&invalid); card = hllCount(hdr,&invalid);
if (invalid) { if (invalid) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
hdr->card[0] = card & 0xff; hdr->card[0] = card & 0xff;
...@@ -1332,7 +1331,7 @@ void pfmergeCommand(client *c) { ...@@ -1332,7 +1331,7 @@ void pfmergeCommand(client *c) {
/* Merge with this HLL with our 'max' HLL by setting max[i] /* Merge with this HLL with our 'max' HLL by setting max[i]
* to MAX(max[i],hll[i]). */ * to MAX(max[i],hll[i]). */
if (hllMerge(max,o) == C_ERR) { if (hllMerge(max,o) == C_ERR) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
} }
...@@ -1355,7 +1354,7 @@ void pfmergeCommand(client *c) { ...@@ -1355,7 +1354,7 @@ void pfmergeCommand(client *c) {
/* Convert the destination object to dense representation if at least /* Convert the destination object to dense representation if at least
* one of the inputs was dense. */ * one of the inputs was dense. */
if (use_dense && hllSparseToDense(o) == C_ERR) { if (use_dense && hllSparseToDense(o) == C_ERR) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
...@@ -1512,7 +1511,7 @@ void pfdebugCommand(client *c) { ...@@ -1512,7 +1511,7 @@ void pfdebugCommand(client *c) {
if (hdr->encoding == HLL_SPARSE) { if (hdr->encoding == HLL_SPARSE) {
if (hllSparseToDense(o) == C_ERR) { if (hllSparseToDense(o) == C_ERR) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
server.dirty++; /* Force propagation on encoding change. */ server.dirty++; /* Force propagation on encoding change. */
...@@ -1577,7 +1576,7 @@ void pfdebugCommand(client *c) { ...@@ -1577,7 +1576,7 @@ void pfdebugCommand(client *c) {
if (hdr->encoding == HLL_SPARSE) { if (hdr->encoding == HLL_SPARSE) {
if (hllSparseToDense(o) == C_ERR) { if (hllSparseToDense(o) == C_ERR) {
addReplySds(c,sdsnew(invalid_hll_err)); addReplyError(c,invalid_hll_err);
return; return;
} }
conv = 1; conv = 1;
......
...@@ -358,12 +358,6 @@ static long long usec(void) { ...@@ -358,12 +358,6 @@ static long long usec(void) {
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
} }
#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))
static void _assert(char *estr, char *file, int line) {
printf("\n\n=== ASSERTION FAILED ===\n");
printf("==> %s:%d '%s' is not true\n",file,line,estr);
}
static intset *createSet(int bits, int size) { static intset *createSet(int bits, int size) {
uint64_t mask = (1<<bits)-1; uint64_t mask = (1<<bits)-1;
uint64_t value; uint64_t value;
......
...@@ -584,16 +584,6 @@ sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) { ...@@ -584,16 +584,6 @@ sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {
* LATENCY RESET: reset data of a specified event or all the data if no event provided. * LATENCY RESET: reset data of a specified event or all the data if no event provided.
*/ */
void latencyCommand(client *c) { void latencyCommand(client *c) {
const char *help[] = {
"DOCTOR -- Returns a human readable latency analysis report.",
"GRAPH <event> -- Returns an ASCII latency graph for the event class.",
"HISTORY <event> -- Returns time-latency samples for the event class.",
"LATEST -- Returns the latest latency samples for all events.",
"RESET [event ...] -- Resets latency data of one or more event classes.",
" (default: reset all data for all event classes)",
"HELP -- Prints this help.",
NULL
};
struct latencyTimeSeries *ts; struct latencyTimeSeries *ts;
if (!strcasecmp(c->argv[1]->ptr,"history") && c->argc == 3) { if (!strcasecmp(c->argv[1]->ptr,"history") && c->argc == 3) {
...@@ -639,6 +629,20 @@ NULL ...@@ -639,6 +629,20 @@ NULL
addReplyLongLong(c,resets); addReplyLongLong(c,resets);
} }
} else if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) { } else if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) {
const char *help[] = {
"DOCTOR",
" Return a human readable latency analysis report.",
"GRAPH <event>",
" Return an ASCII latency graph for the <event> class.",
"HISTORY <event>",
" Return time-latency samples for the <event> class.",
"LATEST",
" Return the latest latency samples for all events.",
"RESET [<event> ...]",
" Reset latency data of one or more <event> classes.",
" (default: reset all data for all event classes)",
NULL
};
addReplyHelp(c, help); addReplyHelp(c, help);
} else { } else {
addReplySubcommandSyntaxError(c); addReplySubcommandSyntaxError(c);
......
...@@ -6,6 +6,49 @@ ...@@ -6,6 +6,49 @@
static redisAtomic size_t lazyfree_objects = 0; static redisAtomic size_t lazyfree_objects = 0;
static redisAtomic size_t lazyfreed_objects = 0; static redisAtomic size_t lazyfreed_objects = 0;
/* Release objects from the lazyfree thread. It's just decrRefCount()
* updating the count of objects to release. */
void lazyfreeFreeObject(void *args[]) {
robj *o = (robj *) args[0];
decrRefCount(o);
atomicDecr(lazyfree_objects,1);
atomicIncr(lazyfreed_objects,1);
}
/* Release a database from the lazyfree thread. The 'db' pointer is the
* database which was substituted with a fresh one in the main thread
* when the database was logically deleted. */
void lazyfreeFreeDatabase(void *args[]) {
dict *ht1 = (dict *) args[0];
dict *ht2 = (dict *) args[1];
size_t numkeys = dictSize(ht1);
dictRelease(ht1);
dictRelease(ht2);
atomicDecr(lazyfree_objects,numkeys);
atomicIncr(lazyfreed_objects,numkeys);
}
/* Release the skiplist mapping Redis Cluster keys to slots in the
* lazyfree thread. */
void lazyfreeFreeSlotsMap(void *args[]) {
rax *rt = args[0];
size_t len = rt->numele;
raxFree(rt);
atomicDecr(lazyfree_objects,len);
atomicIncr(lazyfreed_objects,len);
}
/* Release the rax mapping Redis Cluster keys to slots in the
* lazyfree thread. */
void lazyFreeTrackingTable(void *args[]) {
rax *rt = args[0];
size_t len = rt->numele;
raxFree(rt);
atomicDecr(lazyfree_objects,len);
atomicIncr(lazyfreed_objects,len);
}
/* Return the number of currently pending objects to free. */ /* Return the number of currently pending objects to free. */
size_t lazyfreeGetPendingObjectsCount(void) { size_t lazyfreeGetPendingObjectsCount(void) {
size_t aux; size_t aux;
...@@ -120,7 +163,7 @@ int dbAsyncDelete(redisDb *db, robj *key) { ...@@ -120,7 +163,7 @@ int dbAsyncDelete(redisDb *db, robj *key) {
* equivalent to just calling decrRefCount(). */ * equivalent to just calling decrRefCount(). */
if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) { if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {
atomicIncr(lazyfree_objects,1); atomicIncr(lazyfree_objects,1);
bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL); bioCreateLazyFreeJob(lazyfreeFreeObject,1, val);
dictSetVal(db->dict,de,NULL); dictSetVal(db->dict,de,NULL);
} }
} }
...@@ -141,7 +184,7 @@ void freeObjAsync(robj *key, robj *obj) { ...@@ -141,7 +184,7 @@ void freeObjAsync(robj *key, robj *obj) {
size_t free_effort = lazyfreeGetFreeEffort(key,obj); size_t free_effort = lazyfreeGetFreeEffort(key,obj);
if (free_effort > LAZYFREE_THRESHOLD && obj->refcount == 1) { if (free_effort > LAZYFREE_THRESHOLD && obj->refcount == 1) {
atomicIncr(lazyfree_objects,1); atomicIncr(lazyfree_objects,1);
bioCreateBackgroundJob(BIO_LAZY_FREE,obj,NULL,NULL); bioCreateLazyFreeJob(lazyfreeFreeObject,1,obj);
} else { } else {
decrRefCount(obj); decrRefCount(obj);
} }
...@@ -155,39 +198,17 @@ void emptyDbAsync(redisDb *db) { ...@@ -155,39 +198,17 @@ void emptyDbAsync(redisDb *db) {
db->dict = dictCreate(&dbDictType,NULL); db->dict = dictCreate(&dbDictType,NULL);
db->expires = dictCreate(&dbExpiresDictType,NULL); db->expires = dictCreate(&dbExpiresDictType,NULL);
atomicIncr(lazyfree_objects,dictSize(oldht1)); atomicIncr(lazyfree_objects,dictSize(oldht1));
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2); bioCreateLazyFreeJob(lazyfreeFreeDatabase,2,oldht1,oldht2);
} }
/* Release the radix tree mapping Redis Cluster keys to slots asynchronously. */ /* Release the radix tree mapping Redis Cluster keys to slots asynchronously. */
void freeSlotsToKeysMapAsync(rax *rt) { void freeSlotsToKeysMapAsync(rax *rt) {
atomicIncr(lazyfree_objects,rt->numele); atomicIncr(lazyfree_objects,rt->numele);
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,rt); bioCreateLazyFreeJob(lazyfreeFreeSlotsMap,1,rt);
}
/* Release objects from the lazyfree thread. It's just decrRefCount()
* updating the count of objects to release. */
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 /* Free an object, if the object is huge enough, free it in async way. */
* database which was substituted with a fresh one in the main thread void freeTrackingRadixTreeAsync(rax *tracking) {
* when the database was logically deleted. */ atomicIncr(lazyfree_objects,tracking->numele);
void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) { bioCreateLazyFreeJob(lazyFreeTrackingTable,1,tracking);
size_t numkeys = dictSize(ht1);
dictRelease(ht1);
dictRelease(ht2);
atomicDecr(lazyfree_objects,numkeys);
atomicIncr(lazyfreed_objects,numkeys);
}
/* Release the radix tree mapping Redis Cluster keys to slots in the
* lazyfree thread. */
void lazyfreeFreeSlotsMapFromBioThread(rax *rt) {
size_t len = rt->numele;
raxFree(rt);
atomicDecr(lazyfree_objects,len);
atomicIncr(lazyfreed_objects,len);
} }
...@@ -747,6 +747,7 @@ int64_t commandFlagsFromString(char *s) { ...@@ -747,6 +747,7 @@ int64_t commandFlagsFromString(char *s) {
else if (!strcasecmp(t,"no-slowlog")) flags |= CMD_SKIP_SLOWLOG; else if (!strcasecmp(t,"no-slowlog")) flags |= CMD_SKIP_SLOWLOG;
else if (!strcasecmp(t,"fast")) flags |= CMD_FAST; else if (!strcasecmp(t,"fast")) flags |= CMD_FAST;
else if (!strcasecmp(t,"no-auth")) flags |= CMD_NO_AUTH; else if (!strcasecmp(t,"no-auth")) flags |= CMD_NO_AUTH;
else if (!strcasecmp(t,"may-replicate")) flags |= CMD_MAY_REPLICATE;
else if (!strcasecmp(t,"getkeys-api")) flags |= CMD_MODULE_GETKEYS; else if (!strcasecmp(t,"getkeys-api")) flags |= CMD_MODULE_GETKEYS;
else if (!strcasecmp(t,"no-cluster")) flags |= CMD_MODULE_NO_CLUSTER; else if (!strcasecmp(t,"no-cluster")) flags |= CMD_MODULE_NO_CLUSTER;
else break; else break;
...@@ -813,6 +814,8 @@ int64_t commandFlagsFromString(char *s) { ...@@ -813,6 +814,8 @@ int64_t commandFlagsFromString(char *s) {
* * **"no-auth"**: This command can be run by an un-authenticated client. * * **"no-auth"**: This command can be run by an un-authenticated client.
* Normally this is used by a command that is used * Normally this is used by a command that is used
* to authenticate a client. * to authenticate a client.
* * **"may-replicate"**: This command may generate replication traffic, even
* though it's not a write command.
*/ */
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) { int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {
int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0; int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;
...@@ -851,6 +854,8 @@ int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc c ...@@ -851,6 +854,8 @@ int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc c
cp->rediscmd->keystep = keystep; cp->rediscmd->keystep = keystep;
cp->rediscmd->microseconds = 0; cp->rediscmd->microseconds = 0;
cp->rediscmd->calls = 0; cp->rediscmd->calls = 0;
cp->rediscmd->rejected_calls = 0;
cp->rediscmd->failed_calls = 0;
dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd); dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd);
dictAdd(server.orig_commands,sdsdup(cmdname),cp->rediscmd); dictAdd(server.orig_commands,sdsdup(cmdname),cp->rediscmd);
cp->rediscmd->id = ACLGetCommandID(cmdname); /* ID used for ACL. */ cp->rediscmd->id = ACLGetCommandID(cmdname); /* ID used for ACL. */
...@@ -1368,18 +1373,6 @@ int RM_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) { ...@@ -1368,18 +1373,6 @@ int RM_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) {
return REDISMODULE_OK; return REDISMODULE_OK;
} }
/* Reply with an error or simple string (status message). Used to implement
* ReplyWithSimpleString() and ReplyWithError().
* The function always returns REDISMODULE_OK. */
int replyWithStatus(RedisModuleCtx *ctx, const char *msg, char *prefix) {
client *c = moduleGetReplyClient(ctx);
if (c == NULL) return REDISMODULE_OK;
addReplyProto(c,prefix,strlen(prefix));
addReplyProto(c,msg,strlen(msg));
addReplyProto(c,"\r\n",2);
return REDISMODULE_OK;
}
/* Reply with the error 'err'. /* Reply with the error 'err'.
* *
* Note that 'err' must contain all the error, including * Note that 'err' must contain all the error, including
...@@ -1395,7 +1388,10 @@ int replyWithStatus(RedisModuleCtx *ctx, const char *msg, char *prefix) { ...@@ -1395,7 +1388,10 @@ int replyWithStatus(RedisModuleCtx *ctx, const char *msg, char *prefix) {
* The function always returns REDISMODULE_OK. * The function always returns REDISMODULE_OK.
*/ */
int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) { int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) {
return replyWithStatus(ctx,err,"-"); client *c = moduleGetReplyClient(ctx);
if (c == NULL) return REDISMODULE_OK;
addReplyErrorFormat(c,"-%s",err);
return REDISMODULE_OK;
} }
/* Reply with a simple string (+... \r\n in RESP protocol). This replies /* Reply with a simple string (+... \r\n in RESP protocol). This replies
...@@ -1404,7 +1400,12 @@ int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) { ...@@ -1404,7 +1400,12 @@ int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) {
* *
* The function always returns REDISMODULE_OK. */ * The function always returns REDISMODULE_OK. */
int RM_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) { int RM_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) {
return replyWithStatus(ctx,msg,"+"); client *c = moduleGetReplyClient(ctx);
if (c == NULL) return REDISMODULE_OK;
addReplyProto(c,"+",1);
addReplyProto(c,msg,strlen(msg));
addReplyProto(c,"\r\n",2);
return REDISMODULE_OK;
} }
/* Reply with an array type of 'len' elements. However 'len' other calls /* Reply with an array type of 'len' elements. However 'len' other calls
...@@ -1629,7 +1630,7 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { ...@@ -1629,7 +1630,7 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) {
ctx->saved_oparray = server.also_propagate; ctx->saved_oparray = server.also_propagate;
redisOpArrayInit(&server.also_propagate); redisOpArrayInit(&server.also_propagate);
} }
execCommandPropagateMulti(ctx->client); execCommandPropagateMulti(ctx->client->db->id);
} }
/* Replicate the specified command and arguments to slaves and AOF, as effect /* Replicate the specified command and arguments to slaves and AOF, as effect
...@@ -2044,7 +2045,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) { ...@@ -2044,7 +2045,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) {
* periodically in timer callbacks or other periodic callbacks. * periodically in timer callbacks or other periodic callbacks.
*/ */
int RM_AvoidReplicaTraffic() { int RM_AvoidReplicaTraffic() {
return clientsArePaused(); return checkClientPauseTimeoutAndReturnIfPaused();
} }
/* Change the currently selected DB. Returns an error if the id /* Change the currently selected DB. Returns an error if the id
...@@ -7067,33 +7068,32 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc ...@@ -7067,33 +7068,32 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
*/ */
int RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) { int RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) {
pid_t childpid; pid_t childpid;
if (hasActiveChildProcess()) {
return -1;
}
openChildInfoPipe();
if ((childpid = redisFork(CHILD_TYPE_MODULE)) == 0) { if ((childpid = redisFork(CHILD_TYPE_MODULE)) == 0) {
/* Child */ /* Child */
redisSetProcTitle("redis-module-fork"); redisSetProcTitle("redis-module-fork");
} else if (childpid == -1) { } else if (childpid == -1) {
closeChildInfoPipe();
serverLog(LL_WARNING,"Can't fork for module: %s", strerror(errno)); serverLog(LL_WARNING,"Can't fork for module: %s", strerror(errno));
} else { } else {
/* Parent */ /* Parent */
server.module_child_pid = childpid;
moduleForkInfo.done_handler = cb; moduleForkInfo.done_handler = cb;
moduleForkInfo.done_handler_user_data = user_data; moduleForkInfo.done_handler_user_data = user_data;
updateDictResizePolicy();
serverLog(LL_VERBOSE, "Module fork started pid: %ld ", (long) childpid); serverLog(LL_VERBOSE, "Module fork started pid: %ld ", (long) childpid);
} }
return childpid; return childpid;
} }
/* The module is advised to call this function from the fork child once in a while,
* so that it can report COW memory to the parent which will be reported in INFO */
void RM_SendChildCOWInfo(void) {
sendChildCOWInfo(CHILD_TYPE_MODULE, 0, "Module fork");
}
/* Call from the child process when you want to terminate it. /* Call from the child process when you want to terminate it.
* retcode will be provided to the done handler executed on the parent process. * retcode will be provided to the done handler executed on the parent process.
*/ */
int RM_ExitFromChild(int retcode) { int RM_ExitFromChild(int retcode) {
sendChildCOWInfo(CHILD_TYPE_MODULE, "Module fork"); sendChildCOWInfo(CHILD_TYPE_MODULE, 1, "Module fork");
exitFromChild(retcode); exitFromChild(retcode);
return REDISMODULE_OK; return REDISMODULE_OK;
} }
...@@ -7103,22 +7103,20 @@ int RM_ExitFromChild(int retcode) { ...@@ -7103,22 +7103,20 @@ int RM_ExitFromChild(int retcode) {
* child or the pid does not match, return C_ERR without doing anything. */ * child or the pid does not match, return C_ERR without doing anything. */
int TerminateModuleForkChild(int child_pid, int wait) { int TerminateModuleForkChild(int child_pid, int wait) {
/* Module child should be active and pid should match. */ /* Module child should be active and pid should match. */
if (server.module_child_pid == -1 || if (server.child_type != CHILD_TYPE_MODULE ||
server.module_child_pid != child_pid) return C_ERR; server.child_pid != child_pid) return C_ERR;
int statloc; int statloc;
serverLog(LL_VERBOSE,"Killing running module fork child: %ld", serverLog(LL_VERBOSE,"Killing running module fork child: %ld",
(long) server.module_child_pid); (long) server.child_pid);
if (kill(server.module_child_pid,SIGUSR1) != -1 && wait) { if (kill(server.child_pid,SIGUSR1) != -1 && wait) {
while(wait4(server.module_child_pid,&statloc,0,NULL) != while(wait4(server.child_pid,&statloc,0,NULL) !=
server.module_child_pid); server.child_pid);
} }
/* Reset the buffer accumulating changes while the child saves. */ /* Reset the buffer accumulating changes while the child saves. */
server.module_child_pid = -1; resetChildState();
moduleForkInfo.done_handler = NULL; moduleForkInfo.done_handler = NULL;
moduleForkInfo.done_handler_user_data = NULL; moduleForkInfo.done_handler_user_data = NULL;
closeChildInfoPipe();
updateDictResizePolicy();
return C_OK; return C_OK;
} }
...@@ -7135,12 +7133,12 @@ int RM_KillForkChild(int child_pid) { ...@@ -7135,12 +7133,12 @@ int RM_KillForkChild(int child_pid) {
void ModuleForkDoneHandler(int exitcode, int bysignal) { void ModuleForkDoneHandler(int exitcode, int bysignal) {
serverLog(LL_NOTICE, serverLog(LL_NOTICE,
"Module fork exited pid: %ld, retcode: %d, bysignal: %d", "Module fork exited pid: %ld, retcode: %d, bysignal: %d",
(long) server.module_child_pid, exitcode, bysignal); (long) server.child_pid, exitcode, bysignal);
if (moduleForkInfo.done_handler) { if (moduleForkInfo.done_handler) {
moduleForkInfo.done_handler(exitcode, bysignal, moduleForkInfo.done_handler(exitcode, bysignal,
moduleForkInfo.done_handler_user_data); moduleForkInfo.done_handler_user_data);
} }
server.module_child_pid = -1;
moduleForkInfo.done_handler = NULL; moduleForkInfo.done_handler = NULL;
moduleForkInfo.done_handler_user_data = NULL; moduleForkInfo.done_handler_user_data = NULL;
} }
...@@ -7938,14 +7936,21 @@ sds genModulesInfoString(sds info) { ...@@ -7938,14 +7936,21 @@ sds genModulesInfoString(sds info) {
/* Redis MODULE command. /* Redis MODULE command.
* *
* MODULE LOAD <path> [args...] */ * MODULE LIST
* MODULE LOAD <path> [args...]
* MODULE UNLOAD <name>
*/
void moduleCommand(client *c) { void moduleCommand(client *c) {
char *subcmd = c->argv[1]->ptr; char *subcmd = c->argv[1]->ptr;
if (c->argc == 2 && !strcasecmp(subcmd,"help")) { if (c->argc == 2 && !strcasecmp(subcmd,"help")) {
const char *help[] = { const char *help[] = {
"LIST -- Return a list of loaded modules.", "LIST",
"LOAD <path> [arg ...] -- Load a module library from <path>.", " Return a list of loaded modules.",
"UNLOAD <name> -- Unload a module.", "LOAD <path> [<arg> ...]",
" Load a module library from <path>, passing to it any optional arguments.",
"UNLOAD <name>",
" Unload a module.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -8594,6 +8599,7 @@ void moduleRegisterCoreAPI(void) { ...@@ -8594,6 +8599,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(CommandFilterArgReplace); REGISTER_API(CommandFilterArgReplace);
REGISTER_API(CommandFilterArgDelete); REGISTER_API(CommandFilterArgDelete);
REGISTER_API(Fork); REGISTER_API(Fork);
REGISTER_API(SendChildCOWInfo);
REGISTER_API(ExitFromChild); REGISTER_API(ExitFromChild);
REGISTER_API(KillForkChild); REGISTER_API(KillForkChild);
REGISTER_API(RegisterInfoFunc); REGISTER_API(RegisterInfoFunc);
......
...@@ -364,7 +364,7 @@ int TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, lon ...@@ -364,7 +364,7 @@ int TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, lon
do { \ do { \
RedisModule_Log(ctx,"warning","Testing %s", name); \ RedisModule_Log(ctx,"warning","Testing %s", name); \
reply = RedisModule_Call(ctx,name,__VA_ARGS__); \ reply = RedisModule_Call(ctx,name,__VA_ARGS__); \
} while (0); } while (0)
/* TEST.IT -- Run all the tests. */ /* TEST.IT -- Run all the tests. */
int TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
......
/*
A C-program for MT19937-64 (2004/9/29 version).
Coded by Takuji Nishimura and Makoto Matsumoto.
This is a 64-bit version of Mersenne Twister pseudorandom number
generator.
Before using, initialize the state by using init_genrand64(seed)
or init_by_array64(init_key, key_length).
Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
References:
T. Nishimura, ``Tables of 64-bit Mersenne Twisters''
ACM Transactions on Modeling and
Computer Simulation 10. (2000) 348--357.
M. Matsumoto and T. Nishimura,
``Mersenne Twister: a 623-dimensionally equidistributed
uniform pseudorandom number generator''
ACM Transactions on Modeling and
Computer Simulation 8. (Jan. 1998) 3--30.
Any feedback is very welcome.
http://www.math.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove spaces)
*/
#include "mt19937-64.h"
#include <stdio.h>
#define NN 312
#define MM 156
#define MATRIX_A 0xB5026F5AA96619E9ULL
#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
#define LM 0x7FFFFFFFULL /* Least significant 31 bits */
/* The array for the state vector */
static unsigned long long mt[NN];
/* mti==NN+1 means mt[NN] is not initialized */
static int mti=NN+1;
/* initializes mt[NN] with a seed */
void init_genrand64(unsigned long long seed)
{
mt[0] = seed;
for (mti=1; mti<NN; mti++)
mt[mti] = (6364136223846793005ULL * (mt[mti-1] ^ (mt[mti-1] >> 62)) + mti);
}
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
void init_by_array64(unsigned long long init_key[],
unsigned long long key_length)
{
unsigned long long i, j, k;
init_genrand64(19650218ULL);
i=1; j=0;
k = (NN>key_length ? NN : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 62)) * 3935559000370003845ULL))
+ init_key[j] + j; /* non linear */
i++; j++;
if (i>=NN) { mt[0] = mt[NN-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=NN-1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 62)) * 2862933555777941757ULL))
- i; /* non linear */
i++;
if (i>=NN) { mt[0] = mt[NN-1]; i=1; }
}
mt[0] = 1ULL << 63; /* MSB is 1; assuring non-zero initial array */
}
/* generates a random number on [0, 2^64-1]-interval */
unsigned long long genrand64_int64(void)
{
int i;
unsigned long long x;
static unsigned long long mag01[2]={0ULL, MATRIX_A};
if (mti >= NN) { /* generate NN words at one time */
/* if init_genrand64() has not been called, */
/* a default initial seed is used */
if (mti == NN+1)
init_genrand64(5489ULL);
for (i=0;i<NN-MM;i++) {
x = (mt[i]&UM)|(mt[i+1]&LM);
mt[i] = mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
}
for (;i<NN-1;i++) {
x = (mt[i]&UM)|(mt[i+1]&LM);
mt[i] = mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
}
x = (mt[NN-1]&UM)|(mt[0]&LM);
mt[NN-1] = mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
mti = 0;
}
x = mt[mti++];
x ^= (x >> 29) & 0x5555555555555555ULL;
x ^= (x << 17) & 0x71D67FFFEDA60000ULL;
x ^= (x << 37) & 0xFFF7EEE000000000ULL;
x ^= (x >> 43);
return x;
}
/* generates a random number on [0, 2^63-1]-interval */
long long genrand64_int63(void)
{
return (long long)(genrand64_int64() >> 1);
}
/* generates a random number on [0,1]-real-interval */
double genrand64_real1(void)
{
return (genrand64_int64() >> 11) * (1.0/9007199254740991.0);
}
/* generates a random number on [0,1)-real-interval */
double genrand64_real2(void)
{
return (genrand64_int64() >> 11) * (1.0/9007199254740992.0);
}
/* generates a random number on (0,1)-real-interval */
double genrand64_real3(void)
{
return ((genrand64_int64() >> 12) + 0.5) * (1.0/4503599627370496.0);
}
#ifdef MT19937_64_MAIN
int main(void)
{
int i;
unsigned long long init[4]={0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL}, length=4;
init_by_array64(init, length);
printf("1000 outputs of genrand64_int64()\n");
for (i=0; i<1000; i++) {
printf("%20llu ", genrand64_int64());
if (i%5==4) printf("\n");
}
printf("\n1000 outputs of genrand64_real2()\n");
for (i=0; i<1000; i++) {
printf("%10.8f ", genrand64_real2());
if (i%5==4) printf("\n");
}
return 0;
}
#endif
/*
A C-program for MT19937-64 (2004/9/29 version).
Coded by Takuji Nishimura and Makoto Matsumoto.
This is a 64-bit version of Mersenne Twister pseudorandom number
generator.
Before using, initialize the state by using init_genrand64(seed)
or init_by_array64(init_key, key_length).
Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
References:
T. Nishimura, ``Tables of 64-bit Mersenne Twisters''
ACM Transactions on Modeling and
Computer Simulation 10. (2000) 348--357.
M. Matsumoto and T. Nishimura,
``Mersenne Twister: a 623-dimensionally equidistributed
uniform pseudorandom number generator''
ACM Transactions on Modeling and
Computer Simulation 8. (Jan. 1998) 3--30.
Any feedback is very welcome.
http://www.math.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove spaces)
*/
#ifndef __MT19937_64_H
#define __MT19937_64_H
/* initializes mt[NN] with a seed */
void init_genrand64(unsigned long long seed);
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
void init_by_array64(unsigned long long init_key[],
unsigned long long key_length);
/* generates a random number on [0, 2^64-1]-interval */
unsigned long long genrand64_int64(void);
/* generates a random number on [0, 2^63-1]-interval */
long long genrand64_int63(void);
/* generates a random number on [0,1]-real-interval */
double genrand64_real1(void);
/* generates a random number on [0,1)-real-interval */
double genrand64_real2(void);
/* generates a random number on (0,1)-real-interval */
double genrand64_real3(void);
/* generates a random number on (0,1]-real-interval */
double genrand64_real4(void);
#endif
...@@ -127,15 +127,15 @@ void beforePropagateMultiOrExec(int multi) { ...@@ -127,15 +127,15 @@ void beforePropagateMultiOrExec(int multi) {
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand /* Send a MULTI command to all the slaves and AOF file. Check the execCommand
* implementation for more information. */ * implementation for more information. */
void execCommandPropagateMulti(client *c) { void execCommandPropagateMulti(int dbid) {
beforePropagateMultiOrExec(1); beforePropagateMultiOrExec(1);
propagate(server.multiCommand,c->db->id,&shared.multi,1, propagate(server.multiCommand,dbid,&shared.multi,1,
PROPAGATE_AOF|PROPAGATE_REPL); PROPAGATE_AOF|PROPAGATE_REPL);
} }
void execCommandPropagateExec(client *c) { void execCommandPropagateExec(int dbid) {
beforePropagateMultiOrExec(0); beforePropagateMultiOrExec(0);
propagate(server.execCommand,c->db->id,&shared.exec,1, propagate(server.execCommand,dbid,&shared.exec,1,
PROPAGATE_AOF|PROPAGATE_REPL); PROPAGATE_AOF|PROPAGATE_REPL);
} }
...@@ -162,7 +162,6 @@ void execCommand(client *c) { ...@@ -162,7 +162,6 @@ void execCommand(client *c) {
robj **orig_argv; robj **orig_argv;
int orig_argc; int orig_argc;
struct redisCommand *orig_cmd; struct redisCommand *orig_cmd;
int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */
int was_master = server.masterhost == NULL; int was_master = server.masterhost == NULL;
if (!(c->flags & CLIENT_MULTI)) { if (!(c->flags & CLIENT_MULTI)) {
...@@ -202,19 +201,6 @@ void execCommand(client *c) { ...@@ -202,19 +201,6 @@ void execCommand(client *c) {
c->argv = c->mstate.commands[j].argv; c->argv = c->mstate.commands[j].argv;
c->cmd = c->mstate.commands[j].cmd; c->cmd = c->mstate.commands[j].cmd;
/* Propagate a MULTI request once we encounter the first command which
* is not readonly nor an administrative one.
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* and atomicity guarantees. */
if (!must_propagate &&
!server.loading &&
!(c->cmd->flags & (CMD_READONLY|CMD_ADMIN)))
{
execCommandPropagateMulti(c);
must_propagate = 1;
}
/* ACL permissions are also checked at the time of execution in case /* ACL permissions are also checked at the time of execution in case
* they were changed after the commands were ququed. */ * they were changed after the commands were ququed. */
int acl_errpos; int acl_errpos;
...@@ -265,7 +251,7 @@ void execCommand(client *c) { ...@@ -265,7 +251,7 @@ void execCommand(client *c) {
/* Make sure the EXEC command will be propagated as well if MULTI /* Make sure the EXEC command will be propagated as well if MULTI
* was already propagated. */ * was already propagated. */
if (must_propagate) { if (server.propagate_in_transaction) {
int is_master = server.masterhost == NULL; int is_master = server.masterhost == NULL;
server.dirty++; server.dirty++;
beforePropagateMultiOrExec(0); beforePropagateMultiOrExec(0);
...@@ -388,31 +374,36 @@ void touchWatchedKey(redisDb *db, robj *key) { ...@@ -388,31 +374,36 @@ void touchWatchedKey(redisDb *db, robj *key) {
} }
} }
/* On FLUSHDB or FLUSHALL all the watched keys that are present before the /* Set CLIENT_DIRTY_CAS to all clients of DB when DB is dirty.
* flush but will be deleted as effect of the flushing operation should * It may happen in the following situations:
* be touched. "dbid" is the DB that's getting the flush. -1 if it is * FLUSHDB, FLUSHALL, SWAPDB
* a FLUSHALL operation (all the DBs flushed). */ *
void touchWatchedKeysOnFlush(int dbid) { * replaced_with: for SWAPDB, the WATCH should be invalidated if
listIter li1, li2; * the key exists in either of them, and skipped only if it
* doesn't exist in both. */
void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with) {
listIter li;
listNode *ln; listNode *ln;
dictEntry *de;
/* For every client, check all the waited keys */ if (dictSize(emptied->watched_keys) == 0) return;
listRewind(server.clients,&li1);
while((ln = listNext(&li1))) { dictIterator *di = dictGetSafeIterator(emptied->watched_keys);
while((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
list *clients = dictGetVal(de);
if (!clients) continue;
listRewind(clients,&li);
while((ln = listNext(&li))) {
client *c = listNodeValue(ln); client *c = listNodeValue(ln);
listRewind(c->watched_keys,&li2); if (dictFind(emptied->dict, key->ptr)) {
while((ln = listNext(&li2))) { c->flags |= CLIENT_DIRTY_CAS;
watchedKey *wk = listNodeValue(ln); } else if (replaced_with && dictFind(replaced_with->dict, key->ptr)) {
/* For every watched key matching the specified DB, if the
* key exists, mark the client as dirty, as the key will be
* removed. */
if (dbid == -1 || wk->db->id == dbid) {
if (dictFind(wk->db->dict, wk->key->ptr) != NULL)
c->flags |= CLIENT_DIRTY_CAS; c->flags |= CLIENT_DIRTY_CAS;
} }
} }
} }
dictReleaseIterator(di);
} }
void watchCommand(client *c) { void watchCommand(client *c) {
......
This diff is collapsed.
...@@ -404,7 +404,7 @@ robj *resetRefCount(robj *obj) { ...@@ -404,7 +404,7 @@ robj *resetRefCount(robj *obj) {
int checkType(client *c, robj *o, int type) { int checkType(client *c, robj *o, int type) {
/* A NULL is considered an empty key */ /* A NULL is considered an empty key */
if (o && o->type != type) { if (o && o->type != type) {
addReply(c,shared.wrongtypeerr); addReplyErrorObject(c,shared.wrongtypeerr);
return 1; return 1;
} }
return 0; return 0;
...@@ -1256,10 +1256,18 @@ void objectCommand(client *c) { ...@@ -1256,10 +1256,18 @@ void objectCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.", "ENCODING <key>",
"FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.", " Return the kind of internal representation used in order to store the value",
"IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.", " associated with a <key>.",
"REFCOUNT <key> -- Return the number of references of the value associated with the specified key.", "FREQ <key>",
" Return the access frequency index of the <key>. The returned integer is",
" proportional to the logarithm of the recent access frequency of the key.",
"IDLETIME <key>",
" Return the idle time of the <key>, that is the approximated number of",
" seconds elapsed since the last access to the key.",
"REFCOUNT <key>",
" Return the number of references of the value associated with the specified",
" <key>.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -1303,11 +1311,17 @@ NULL ...@@ -1303,11 +1311,17 @@ NULL
void memoryCommand(client *c) { void memoryCommand(client *c) {
if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) { if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) {
const char *help[] = { const char *help[] = {
"DOCTOR - Return memory problems reports.", "DOCTOR",
"MALLOC-STATS -- Return internal statistics report from the memory allocator.", " Return memory problems reports.",
"PURGE -- Attempt to purge dirty pages for reclamation by the allocator.", "MALLOC-STATS"
"STATS -- Return information about the memory usage of the server.", " Return internal statistics report from the memory allocator.",
"USAGE <key> [SAMPLES <count>] -- Return memory in bytes used by <key> and its value. Nested values are sampled up to <count> times (default: 5).", "PURGE",
" Attempt to purge dirty pages for reclamation by the allocator.",
"STATS",
" Return information about the memory usage of the server.",
"USAGE <key> [SAMPLES <count>]",
" Return memory in bytes used by <key> and its value. Nested values are",
" sampled up to <count> times (default: 5).",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -1321,13 +1335,13 @@ NULL ...@@ -1321,13 +1335,13 @@ NULL
if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL) if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL)
== C_ERR) return; == C_ERR) return;
if (samples < 0) { if (samples < 0) {
addReply(c,shared.syntaxerr); addReplyErrorObject(c,shared.syntaxerr);
return; return;
} }
if (samples == 0) samples = LLONG_MAX; if (samples == 0) samples = LLONG_MAX;
j++; /* skip option argument. */ j++; /* skip option argument. */
} else { } else {
addReply(c,shared.syntaxerr); addReplyErrorObject(c,shared.syntaxerr);
return; return;
} }
} }
...@@ -1452,6 +1466,6 @@ NULL ...@@ -1452,6 +1466,6 @@ NULL
else else
addReplyError(c, "Error purging dirty pages"); addReplyError(c, "Error purging dirty pages");
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try MEMORY HELP", (char*)c->argv[1]->ptr); addReplySubcommandSyntaxError(c);
} }
} }
...@@ -455,9 +455,13 @@ void publishCommand(client *c) { ...@@ -455,9 +455,13 @@ void publishCommand(client *c) {
void pubsubCommand(client *c) { void pubsubCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"CHANNELS [<pattern>] -- Return the currently active channels matching a pattern (default: all).", "CHANNELS [<pattern>]",
"NUMPAT -- Return number of subscriptions to patterns.", " Return the currently active channels matching a <pattern> (default: '*').",
"NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).", "NUMPAT",
" Return number of subscriptions to patterns.",
"NUMSUB [<channel> ...]",
" Return the number of subscribers for the specified channels, excluding",
" pattern subscriptions(default: no channels).",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
......
...@@ -66,10 +66,10 @@ static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536}; ...@@ -66,10 +66,10 @@ static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536};
#else #else
#define D(...) \ #define D(...) \
do { \ do { \
printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ printf("%s:%s:%d:\t", __FILE__, __func__, __LINE__); \
printf(__VA_ARGS__); \ printf(__VA_ARGS__); \
printf("\n"); \ printf("\n"); \
} while (0); } while (0)
#endif #endif
/* Bookmarks forward declarations */ /* Bookmarks forward declarations */
...@@ -1508,15 +1508,6 @@ void quicklistBookmarksClear(quicklist *ql) { ...@@ -1508,15 +1508,6 @@ void quicklistBookmarksClear(quicklist *ql) {
#include <stdint.h> #include <stdint.h>
#include <sys/time.h> #include <sys/time.h>
#define assert(_e) \
do { \
if (!(_e)) { \
printf("\n\n=== ASSERTION FAILED ===\n"); \
printf("==> %s:%d '%s' is not true\n", __FILE__, __LINE__, #_e); \
err++; \
} \
} while (0)
#define yell(str, ...) printf("ERROR! " str "\n\n", __VA_ARGS__) #define yell(str, ...) printf("ERROR! " str "\n\n", __VA_ARGS__)
#define OK printf("\tOK\n") #define OK printf("\tOK\n")
...@@ -1529,7 +1520,7 @@ void quicklistBookmarksClear(quicklist *ql) { ...@@ -1529,7 +1520,7 @@ void quicklistBookmarksClear(quicklist *ql) {
#define ERR(x, ...) \ #define ERR(x, ...) \
do { \ do { \
printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ printf("%s:%s:%d:\t", __FILE__, __func__, __LINE__); \
printf("ERROR! " x "\n", __VA_ARGS__); \ printf("ERROR! " x "\n", __VA_ARGS__); \
err++; \ err++; \
} while (0) } while (0)
...@@ -1614,7 +1605,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count, ...@@ -1614,7 +1605,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,
ql_info(ql); ql_info(ql);
if (len != ql->len) { if (len != ql->len) {
yell("quicklist length wrong: expected %d, got %u", len, ql->len); yell("quicklist length wrong: expected %d, got %lu", len, ql->len);
errors++; errors++;
} }
...@@ -1670,7 +1661,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count, ...@@ -1670,7 +1661,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,
if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {
yell("Incorrect compression: node %d is " yell("Incorrect compression: node %d is "
"compressed at depth %d ((%u, %u); total " "compressed at depth %d ((%u, %u); total "
"nodes: %u; size: %u; recompress: %d)", "nodes: %lu; size: %u; recompress: %d)",
at, ql->compress, low_raw, high_raw, ql->len, node->sz, at, ql->compress, low_raw, high_raw, ql->len, node->sz,
node->recompress); node->recompress);
errors++; errors++;
...@@ -1680,7 +1671,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count, ...@@ -1680,7 +1671,7 @@ static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,
!node->attempted_compress) { !node->attempted_compress) {
yell("Incorrect non-compression: node %d is NOT " yell("Incorrect non-compression: node %d is NOT "
"compressed at depth %d ((%u, %u); total " "compressed at depth %d ((%u, %u); total "
"nodes: %u; size: %u; recompress: %d; attempted: %d)", "nodes: %lu; size: %u; recompress: %d; attempted: %d)",
at, ql->compress, low_raw, high_raw, ql->len, node->sz, at, ql->compress, low_raw, high_raw, ql->len, node->sz,
node->recompress, node->attempted_compress); node->recompress, node->attempted_compress);
errors++; errors++;
...@@ -2706,7 +2697,7 @@ int quicklistTest(int argc, char *argv[]) { ...@@ -2706,7 +2697,7 @@ int quicklistTest(int argc, char *argv[]) {
if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {
ERR("Incorrect compression: node %d is " ERR("Incorrect compression: node %d is "
"compressed at depth %d ((%u, %u); total " "compressed at depth %d ((%u, %u); total "
"nodes: %u; size: %u)", "nodes: %lu; size: %u)",
at, depth, low_raw, high_raw, ql->len, at, depth, low_raw, high_raw, ql->len,
node->sz); node->sz);
} }
...@@ -2714,7 +2705,7 @@ int quicklistTest(int argc, char *argv[]) { ...@@ -2714,7 +2705,7 @@ int quicklistTest(int argc, char *argv[]) {
if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) { if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) {
ERR("Incorrect non-compression: node %d is NOT " ERR("Incorrect non-compression: node %d is NOT "
"compressed at depth %d ((%u, %u); total " "compressed at depth %d ((%u, %u); total "
"nodes: %u; size: %u; attempted: %d)", "nodes: %lu; size: %u; attempted: %d)",
at, depth, low_raw, high_raw, ql->len, at, depth, low_raw, high_raw, ql->len,
node->sz, node->attempted_compress); node->sz, node->attempted_compress);
} }
......
...@@ -61,7 +61,7 @@ void raxDebugShowNode(const char *msg, raxNode *n); ...@@ -61,7 +61,7 @@ void raxDebugShowNode(const char *msg, raxNode *n);
#ifdef RAX_DEBUG_MSG #ifdef RAX_DEBUG_MSG
#define debugf(...) \ #define debugf(...) \
if (raxDebugMsg) { \ if (raxDebugMsg) { \
printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ printf("%s:%s:%d:\t", __FILE__, __func__, __LINE__); \
printf(__VA_ARGS__); \ printf(__VA_ARGS__); \
fflush(stdout); \ fflush(stdout); \
} }
...@@ -1892,7 +1892,7 @@ void raxShow(rax *rax) { ...@@ -1892,7 +1892,7 @@ void raxShow(rax *rax) {
/* Used by debugnode() macro to show info about a given node. */ /* Used by debugnode() macro to show info about a given node. */
void raxDebugShowNode(const char *msg, raxNode *n) { void raxDebugShowNode(const char *msg, raxNode *n) {
if (raxDebugMsg == 0) return; if (raxDebugMsg == 0) return;
printf("%s: %p [%.*s] key:%d size:%d children:", printf("%s: %p [%.*s] key:%u size:%u children:",
msg, (void*)n, (int)n->size, (char*)n->data, n->iskey, n->size); msg, (void*)n, (int)n->size, (char*)n->data, n->iskey, n->size);
int numcld = n->iscompr ? 1 : n->size; int numcld = n->iscompr ? 1 : n->size;
raxNode **cldptr = raxNodeLastChildPtr(n) - (numcld-1); raxNode **cldptr = raxNodeLastChildPtr(n) - (numcld-1);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment