Commit 0f999784 authored by antirez's avatar antirez
Browse files

Merge remote-tracking branch 'origin/unstable' into unstable

parents 74f10793 1b247d13
...@@ -22,6 +22,12 @@ but it is better to keep them in mind: ...@@ -22,6 +22,12 @@ but it is better to keep them in mind:
starting with '#'. All the major clients should be already fixed to work starting with '#'. All the major clients should be already fixed to work
with the new INFO format. with the new INFO format.
Also the following redis.conf and CONFIG GET / SET parameters changed name:
* hash-max-zipmap-entries, now replaced by hash-max-ziplist-entries
* hash-max-zipmap-value, now replaced by hash-max-ziplist-value
* glueoutputbuf was no completely removed as it does not make sense
--------- ---------
CHANGELOG CHANGELOG
--------- ---------
......
...@@ -93,6 +93,21 @@ save 900 1 ...@@ -93,6 +93,21 @@ save 900 1
save 300 10 save 300 10
save 60 10000 save 60 10000
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in an hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# distater will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usually even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes
# Compress string objects using LZF when dump .rdb databases? # Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win. # For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but # If you want to save some CPU in the saving child set it to 'no' but
...@@ -141,6 +156,22 @@ dir ./ ...@@ -141,6 +156,22 @@ dir ./
# #
slave-serve-stale-data yes slave-serve-stale-data yes
# You can configure a slave instance to accept writes or not. Writing against
# a slave instance may be useful to store some ephemeral data (because data
# written on a slave will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a
# misconfiguration.
#
# Since Redis 2.6 by default slaves are read-only.
#
# Note: read only slaves are not designed to be exposed to untrusted clients
# on the internet. It's just a protection layer against misuse of the instance.
# Still a read only slave exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extend you can improve
# security of read only slaves using 'rename-command' to shadow all the
# administrative / dangerous commands.
slave-read-only yes
# Slaves send PINGs to server in a predefined interval. It's possible to change # Slaves send PINGs to server in a predefined interval. It's possible to change
# this interval with the repl_ping_slave_period option. The default value is 10 # this interval with the repl_ping_slave_period option. The default value is 10
# seconds. # seconds.
...@@ -231,7 +262,7 @@ slave-serve-stale-data yes ...@@ -231,7 +262,7 @@ slave-serve-stale-data yes
# volatile-lru -> remove the key with an expire set using an LRU algorithm # volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm # allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set # volatile-random -> remove a random key with an expire set
# allkeys->random -> remove a random key, any key # allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL) # volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations # noeviction -> don't expire at all, just return an error on write operations
# #
...@@ -406,12 +437,11 @@ slowlog-max-len 1024 ...@@ -406,12 +437,11 @@ slowlog-max-len 1024
############################### ADVANCED CONFIG ############################### ############################### ADVANCED CONFIG ###############################
# Hashes are encoded in a special way (much more memory efficient) when they # Hashes are encoded using a memory efficient data structure when they have a
# have at max a given number of elements, and the biggest element does not # small number of entries, and the biggest entry does not exceed a given
# exceed a given threshold. You can configure this limits with the following # threshold. These thresholds can be configured using the following directives.
# configuration directives. hash-max-ziplist-entries 512
hash-max-zipmap-entries 512 hash-max-ziplist-value 64
hash-max-zipmap-value 64
# Similarly to hashes, small lists are also encoded in a special way in order # Similarly to hashes, small lists are also encoded in a special way in order
# to save a lot of space. The special representation is only used when # to save a lot of space. The special representation is only used when
......
...@@ -73,7 +73,7 @@ QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR ...@@ -73,7 +73,7 @@ QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR); QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
endif endif
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
...@@ -98,35 +98,39 @@ ae_kqueue.o: ae_kqueue.c ...@@ -98,35 +98,39 @@ ae_kqueue.o: ae_kqueue.c
ae_select.o: ae_select.c ae_select.o: ae_select.c
anet.o: anet.c fmacros.h anet.h anet.o: anet.c fmacros.h anet.h
aof.o: aof.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ aof.o: aof.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
bio.o: bio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ bio.o: bio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h bio.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \ cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h sha1.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h
dict.o: dict.c fmacros.h dict.h zmalloc.h dict.o: dict.c fmacros.h dict.h zmalloc.h
endianconv.o: endianconv.c endianconv.o: endianconv.c
intset.o: intset.c intset.h zmalloc.h endianconv.h intset.o: intset.c intset.h zmalloc.h endianconv.h
lzf_c.o: lzf_c.c lzfP.h lzf_c.o: lzf_c.c lzfP.h
lzf_d.o: lzf_d.c lzfP.h lzf_d.o: lzf_d.c lzfP.h
multi.o: multi.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ multi.o: multi.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \ networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
pqsort.o: pqsort.c pqsort.o: pqsort.c
pubsub.o: pubsub.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ pubsub.o: pubsub.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
rand.o: rand.c
rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h lzf.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h lzf.h \
zipmap.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \ redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h ../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h
redis-check-aof.o: redis-check-aof.c fmacros.h config.h redis-check-aof.o: redis-check-aof.c fmacros.h config.h
...@@ -134,34 +138,37 @@ redis-check-dump.o: redis-check-dump.c lzf.h ...@@ -134,34 +138,37 @@ redis-check-dump.o: redis-check-dump.c lzf.h
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \ redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
redis.o: redis.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ redis.o: redis.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h \ zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h \
bio.h asciilogo.h slowlog.h bio.h asciilogo.h
release.o: release.c release.h release.o: release.c release.h
replication.o: replication.c redis.h fmacros.h config.h ae.h sds.h dict.h \ replication.o: replication.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
rio.o: rio.c fmacros.h rio.h sds.h util.h
scripting.o: scripting.c redis.h fmacros.h config.h ae.h sds.h dict.h \ scripting.o: scripting.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \ adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
sha1.h rand.h rio.h sha1.h rand.h
rio.o: rio.c sds.h
sds.o: sds.c sds.h zmalloc.h sds.o: sds.c sds.h zmalloc.h
sha1.o: sha1.c sha1.h config.h sha1.o: sha1.c sha1.h config.h
slowlog.o: slowlog.c redis.h fmacros.h config.h ae.h sds.h dict.h \ slowlog.o: slowlog.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \ adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
slowlog.h rio.h slowlog.h
sort.o: sort.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ sort.o: sort.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h pqsort.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h \
pqsort.h
syncio.o: syncio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ syncio.o: syncio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
t_hash.o: t_hash.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_hash.o: t_hash.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
t_set.o: t_set.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_set.o: t_set.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \ t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
util.o: util.c fmacros.h util.h util.o: util.c fmacros.h util.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h
zipmap.o: zipmap.c zmalloc.h endianconv.h zipmap.o: zipmap.c zmalloc.h endianconv.h
......
...@@ -323,3 +323,19 @@ listNode *listIndex(list *list, long index) { ...@@ -323,3 +323,19 @@ listNode *listIndex(list *list, long index) {
} }
return n; return n;
} }
/* Rotate the list removing the tail node and inserting it to the head. */
void listRotate(list *list) {
listNode *tail = list->tail;
if (listLength(list) <= 1) return;
/* Detatch current tail */
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
list->head->prev = tail;
tail->prev = NULL;
tail->next = list->head;
list->head = tail;
}
...@@ -84,6 +84,7 @@ listNode *listSearchKey(list *list, void *key); ...@@ -84,6 +84,7 @@ listNode *listSearchKey(list *list, void *key);
listNode *listIndex(list *list, long index); listNode *listIndex(list *list, long index);
void listRewind(list *list, listIter *li); void listRewind(list *list, listIter *li);
void listRewindTail(list *list, listIter *li); void listRewindTail(list *list, listIter *li);
void listRotate(list *list);
/* Directions for iterators */ /* Directions for iterators */
#define AL_START_HEAD 0 #define AL_START_HEAD 0
......
...@@ -353,7 +353,12 @@ int anetPeerToString(int fd, char *ip, int *port) { ...@@ -353,7 +353,12 @@ int anetPeerToString(int fd, char *ip, int *port) {
struct sockaddr_in sa; struct sockaddr_in sa;
socklen_t salen = sizeof(sa); socklen_t salen = sizeof(sa);
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) return -1; if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) {
*port = 0;
ip[0] = '?';
ip[1] = '\0';
return -1;
}
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
if (port) *port = ntohs(sa.sin_port); if (port) *port = ntohs(sa.sin_port);
return 0; return 0;
......
...@@ -287,6 +287,7 @@ struct redisClient *createFakeClient(void) { ...@@ -287,6 +287,7 @@ struct redisClient *createFakeClient(void) {
selectDb(c,0); selectDb(c,0);
c->fd = -1; c->fd = -1;
c->querybuf = sdsempty(); c->querybuf = sdsempty();
c->querybuf_peak = 0;
c->argc = 0; c->argc = 0;
c->argv = NULL; c->argv = NULL;
c->bufpos = 0; c->bufpos = 0;
...@@ -609,17 +610,44 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) { ...@@ -609,17 +610,44 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
return 1; return 1;
} }
/* Write either the key or the value of the currently selected item of an hash.
* The 'hi' argument passes a valid Redis hash iterator.
* The 'what' filed specifies if to write a key or a value and can be
* either REDIS_HASH_KEY or REDIS_HASH_VALUE.
*
* The function returns 0 on error, non-zero on success. */
static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {
if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
if (vstr) {
return rioWriteBulkString(r, (char*)vstr, vlen);
} else {
return rioWriteBulkLongLong(r, vll);
}
} else if (hi->encoding == REDIS_ENCODING_HT) {
robj *value;
hashTypeCurrentFromHashTable(hi, what, &value);
return rioWriteBulkObject(r, value);
}
redisPanic("Unknown hash encoding");
return 0;
}
/* Emit the commands needed to rebuild a hash object. /* Emit the commands needed to rebuild a hash object.
* The function returns 0 on error, 1 on success. */ * The function returns 0 on error, 1 on success. */
int rewriteHashObject(rio *r, robj *key, robj *o) { int rewriteHashObject(rio *r, robj *key, robj *o) {
hashTypeIterator *hi;
long long count = 0, items = hashTypeLength(o); long long count = 0, items = hashTypeLength(o);
if (o->encoding == REDIS_ENCODING_ZIPMAP) { hi = hashTypeInitIterator(o);
unsigned char *p = zipmapRewind(o->ptr); while (hashTypeNext(hi) != REDIS_ERR) {
unsigned char *field, *val;
unsigned int flen, vlen;
while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
if (count == 0) { if (count == 0) {
int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?
REDIS_AOF_REWRITE_ITEMS_PER_CMD : items; REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;
...@@ -628,34 +656,15 @@ int rewriteHashObject(rio *r, robj *key, robj *o) { ...@@ -628,34 +656,15 @@ int rewriteHashObject(rio *r, robj *key, robj *o) {
if (rioWriteBulkString(r,"HMSET",5) == 0) return 0; if (rioWriteBulkString(r,"HMSET",5) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0; if (rioWriteBulkObject(r,key) == 0) return 0;
} }
if (rioWriteBulkString(r,(char*)field,flen) == 0) return 0;
if (rioWriteBulkString(r,(char*)val,vlen) == 0) return 0; if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_KEY) == 0) return 0;
if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_VALUE) == 0) return 0;
if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0; if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--; items--;
} }
} else {
dictIterator *di = dictGetIterator(o->ptr);
dictEntry *de;
while((de = dictNext(di)) != NULL) { hashTypeReleaseIterator(hi);
robj *field = dictGetKey(de);
robj *val = dictGetVal(de);
if (count == 0) {
int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?
REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
if (rioWriteBulkString(r,"HMSET",5) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
}
if (rioWriteBulkObject(r,field) == 0) return 0;
if (rioWriteBulkObject(r,val) == 0) return 0;
if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
dictReleaseIterator(di);
}
return 1; return 1;
} }
......
...@@ -19,20 +19,6 @@ int clusterAddSlot(clusterNode *n, int slot); ...@@ -19,20 +19,6 @@ int clusterAddSlot(clusterNode *n, int slot);
* Initialization * Initialization
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
void clusterGetRandomName(char *p) {
FILE *fp = fopen("/dev/urandom","r");
char *charset = "0123456789abcdef";
int j;
if (fp == NULL || fread(p,REDIS_CLUSTER_NAMELEN,1,fp) == 0) {
for (j = 0; j < REDIS_CLUSTER_NAMELEN; j++)
p[j] = rand();
}
for (j = 0; j < REDIS_CLUSTER_NAMELEN; j++)
p[j] = charset[p[j] & 0x0F];
fclose(fp);
}
int clusterLoadConfig(char *filename) { int clusterLoadConfig(char *filename) {
FILE *fp = fopen(filename,"r"); FILE *fp = fopen(filename,"r");
char *line; char *line;
...@@ -304,7 +290,7 @@ clusterNode *createClusterNode(char *nodename, int flags) { ...@@ -304,7 +290,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
if (nodename) if (nodename)
memcpy(node->name, nodename, REDIS_CLUSTER_NAMELEN); memcpy(node->name, nodename, REDIS_CLUSTER_NAMELEN);
else else
clusterGetRandomName(node->name); getRandomHexChars(node->name, REDIS_CLUSTER_NAMELEN);
node->flags = flags; node->flags = flags;
memset(node->slots,0,sizeof(node->slots)); memset(node->slots,0,sizeof(node->slots));
node->numslaves = 0; node->numslaves = 0;
......
...@@ -202,8 +202,10 @@ void loadServerConfigFromString(char *config) { ...@@ -202,8 +202,10 @@ void loadServerConfigFromString(char *config) {
if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) { if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr; err = "argument must be 'yes' or 'no'"; goto loaderr;
} }
} else if (!strcasecmp(argv[0],"glueoutputbuf")) { } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) {
redisLog(REDIS_WARNING, "Deprecated configuration directive: \"%s\"", argv[0]); if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) { } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
if ((server.rdb_compression = yesnotoi(argv[1])) == -1) { if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr; err = "argument must be 'yes' or 'no'"; goto loaderr;
...@@ -262,10 +264,10 @@ void loadServerConfigFromString(char *config) { ...@@ -262,10 +264,10 @@ void loadServerConfigFromString(char *config) {
} else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) { } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
zfree(server.rdb_filename); zfree(server.rdb_filename);
server.rdb_filename = zstrdup(argv[1]); server.rdb_filename = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2) { } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
server.hash_max_zipmap_entries = memtoll(argv[1], NULL); server.hash_max_ziplist_entries = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2) { } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
server.hash_max_zipmap_value = memtoll(argv[1], NULL); server.hash_max_ziplist_value = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){ } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
server.list_max_ziplist_entries = memtoll(argv[1], NULL); server.list_max_ziplist_entries = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) { } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
...@@ -336,6 +338,11 @@ void loadServerConfigFromString(char *config) { ...@@ -336,6 +338,11 @@ void loadServerConfigFromString(char *config) {
server.client_obuf_limits[class].hard_limit_bytes = hard; server.client_obuf_limits[class].hard_limit_bytes = hard;
server.client_obuf_limits[class].soft_limit_bytes = soft; server.client_obuf_limits[class].soft_limit_bytes = soft;
server.client_obuf_limits[class].soft_limit_seconds = soft_seconds; server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
} else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") &&
argc == 2) {
if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else { } else {
err = "Bad directive or wrong number of arguments"; goto loaderr; err = "Bad directive or wrong number of arguments"; goto loaderr;
} }
...@@ -511,17 +518,22 @@ void configSetCommand(redisClient *c) { ...@@ -511,17 +518,22 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt; if (yn == -1) goto badfmt;
server.repl_serve_stale_data = yn; server.repl_serve_stale_data = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.repl_slave_ro = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"dir")) { } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
if (chdir((char*)o->ptr) == -1) { if (chdir((char*)o->ptr) == -1) {
addReplyErrorFormat(c,"Changing directory: %s", strerror(errno)); addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
return; return;
} }
} else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-entries")) { } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.hash_max_zipmap_entries = ll; server.hash_max_ziplist_entries = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-value")) { } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.hash_max_zipmap_value = ll; server.hash_max_ziplist_value = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) { } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.list_max_ziplist_entries = ll; server.list_max_ziplist_entries = ll;
...@@ -604,7 +616,17 @@ void configSetCommand(redisClient *c) { ...@@ -604,7 +616,17 @@ void configSetCommand(redisClient *c) {
server.client_obuf_limits[class].soft_limit_seconds = soft_seconds; server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
} }
sdsfreesplitres(v,vlen); sdsfreesplitres(v,vlen);
} else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.stop_writes_on_bgsave_err = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
server.repl_ping_slave_period = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
server.repl_timeout = ll;
} else { } else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr); (char*)c->argv[2]->ptr);
...@@ -619,6 +641,31 @@ badfmt: /* Bad format errors */ ...@@ -619,6 +641,31 @@ badfmt: /* Bad format errors */
(char*)c->argv[2]->ptr); (char*)c->argv[2]->ptr);
} }
#define config_get_string_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,_var ? _var : ""); \
matches++; \
} \
} while(0);
#define config_get_bool_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,_var ? "yes" : "no"); \
matches++; \
} \
} while(0);
#define config_get_numerical_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
ll2string(buf,sizeof(buf),_var); \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,buf); \
matches++; \
} \
} while(0);
void configGetCommand(redisClient *c) { void configGetCommand(redisClient *c) {
robj *o = c->argv[2]; robj *o = c->argv[2];
void *replylen = addDeferredMultiBulkLength(c); void *replylen = addDeferredMultiBulkLength(c);
...@@ -627,6 +674,68 @@ void configGetCommand(redisClient *c) { ...@@ -627,6 +674,68 @@ void configGetCommand(redisClient *c) {
int matches = 0; int matches = 0;
redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW); redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
/* String values */
config_get_string_field("dbfilename",server.rdb_filename);
config_get_string_field("requirepass",server.requirepass);
config_get_string_field("masterauth",server.requirepass);
config_get_string_field("bind",server.bindaddr);
config_get_string_field("unixsocket",server.unixsocket);
config_get_string_field("logfile",server.logfile);
config_get_string_field("pidfile",server.pidfile);
/* Numerical values */
config_get_numerical_field("maxmemory",server.maxmemory);
config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
config_get_numerical_field("timeout",server.maxidletime);
config_get_numerical_field("auto-aof-rewrite-percentage",
server.aof_rewrite_perc);
config_get_numerical_field("auto-aof-rewrite-min-size",
server.aof_rewrite_min_size);
config_get_numerical_field("hash-max-ziplist-entries",
server.hash_max_ziplist_entries);
config_get_numerical_field("hash-max-ziplist-value",
server.hash_max_ziplist_value);
config_get_numerical_field("list-max-ziplist-entries",
server.list_max_ziplist_entries);
config_get_numerical_field("list-max-ziplist-value",
server.list_max_ziplist_value);
config_get_numerical_field("set-max-intset-entries",
server.set_max_intset_entries);
config_get_numerical_field("zset-max-ziplist-entries",
server.zset_max_ziplist_entries);
config_get_numerical_field("zset-max-ziplist-value",
server.zset_max_ziplist_value);
config_get_numerical_field("lua-time-limit",server.lua_time_limit);
config_get_numerical_field("slowlog-log-slower-than",
server.slowlog_log_slower_than);
config_get_numerical_field("slowlog-max-len",
server.slowlog_max_len);
config_get_numerical_field("port",server.port);
config_get_numerical_field("databases",server.dbnum);
config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
config_get_numerical_field("repl-timeout",server.repl_timeout);
config_get_numerical_field("maxclients",server.maxclients);
/* Bool (yes/no) values */
config_get_bool_field("no-appendfsync-on-rewrite",
server.aof_no_fsync_on_rewrite);
config_get_bool_field("slave-serve-stale-data",
server.repl_serve_stale_data);
config_get_bool_field("slave-read-only",
server.repl_slave_ro);
config_get_bool_field("stop-writes-on-bgsave-error",
server.stop_writes_on_bgsave_err);
config_get_bool_field("daemonize", server.daemonize);
config_get_bool_field("rdbcompression", server.rdb_compression);
config_get_bool_field("activerehashing", server.activerehashing);
/* Everything we can't handle with macros follows. */
if (stringmatch(pattern,"appendonly",0)) {
addReplyBulkCString(c,"appendonly");
addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
matches++;
}
if (stringmatch(pattern,"dir",0)) { if (stringmatch(pattern,"dir",0)) {
char buf[1024]; char buf[1024];
...@@ -637,27 +746,6 @@ void configGetCommand(redisClient *c) { ...@@ -637,27 +746,6 @@ void configGetCommand(redisClient *c) {
addReplyBulkCString(c,buf); addReplyBulkCString(c,buf);
matches++; matches++;
} }
if (stringmatch(pattern,"dbfilename",0)) {
addReplyBulkCString(c,"dbfilename");
addReplyBulkCString(c,server.rdb_filename);
matches++;
}
if (stringmatch(pattern,"requirepass",0)) {
addReplyBulkCString(c,"requirepass");
addReplyBulkCString(c,server.requirepass);
matches++;
}
if (stringmatch(pattern,"masterauth",0)) {
addReplyBulkCString(c,"masterauth");
addReplyBulkCString(c,server.masterauth);
matches++;
}
if (stringmatch(pattern,"maxmemory",0)) {
ll2string(buf,sizeof(buf),server.maxmemory);
addReplyBulkCString(c,"maxmemory");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"maxmemory-policy",0)) { if (stringmatch(pattern,"maxmemory-policy",0)) {
char *s; char *s;
...@@ -674,28 +762,6 @@ void configGetCommand(redisClient *c) { ...@@ -674,28 +762,6 @@ void configGetCommand(redisClient *c) {
addReplyBulkCString(c,s); addReplyBulkCString(c,s);
matches++; matches++;
} }
if (stringmatch(pattern,"maxmemory-samples",0)) {
ll2string(buf,sizeof(buf),server.maxmemory_samples);
addReplyBulkCString(c,"maxmemory-samples");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"timeout",0)) {
ll2string(buf,sizeof(buf),server.maxidletime);
addReplyBulkCString(c,"timeout");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"appendonly",0)) {
addReplyBulkCString(c,"appendonly");
addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
matches++;
}
if (stringmatch(pattern,"no-appendfsync-on-rewrite",0)) {
addReplyBulkCString(c,"no-appendfsync-on-rewrite");
addReplyBulkCString(c,server.aof_no_fsync_on_rewrite ? "yes" : "no");
matches++;
}
if (stringmatch(pattern,"appendfsync",0)) { if (stringmatch(pattern,"appendfsync",0)) {
char *policy; char *policy;
...@@ -725,71 +791,6 @@ void configGetCommand(redisClient *c) { ...@@ -725,71 +791,6 @@ void configGetCommand(redisClient *c) {
sdsfree(buf); sdsfree(buf);
matches++; matches++;
} }
if (stringmatch(pattern,"auto-aof-rewrite-percentage",0)) {
addReplyBulkCString(c,"auto-aof-rewrite-percentage");
addReplyBulkLongLong(c,server.aof_rewrite_perc);
matches++;
}
if (stringmatch(pattern,"auto-aof-rewrite-min-size",0)) {
addReplyBulkCString(c,"auto-aof-rewrite-min-size");
addReplyBulkLongLong(c,server.aof_rewrite_min_size);
matches++;
}
if (stringmatch(pattern,"slave-serve-stale-data",0)) {
addReplyBulkCString(c,"slave-serve-stale-data");
addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no");
matches++;
}
if (stringmatch(pattern,"hash-max-zipmap-entries",0)) {
addReplyBulkCString(c,"hash-max-zipmap-entries");
addReplyBulkLongLong(c,server.hash_max_zipmap_entries);
matches++;
}
if (stringmatch(pattern,"hash-max-zipmap-value",0)) {
addReplyBulkCString(c,"hash-max-zipmap-value");
addReplyBulkLongLong(c,server.hash_max_zipmap_value);
matches++;
}
if (stringmatch(pattern,"list-max-ziplist-entries",0)) {
addReplyBulkCString(c,"list-max-ziplist-entries");
addReplyBulkLongLong(c,server.list_max_ziplist_entries);
matches++;
}
if (stringmatch(pattern,"list-max-ziplist-value",0)) {
addReplyBulkCString(c,"list-max-ziplist-value");
addReplyBulkLongLong(c,server.list_max_ziplist_value);
matches++;
}
if (stringmatch(pattern,"set-max-intset-entries",0)) {
addReplyBulkCString(c,"set-max-intset-entries");
addReplyBulkLongLong(c,server.set_max_intset_entries);
matches++;
}
if (stringmatch(pattern,"zset-max-ziplist-entries",0)) {
addReplyBulkCString(c,"zset-max-ziplist-entries");
addReplyBulkLongLong(c,server.zset_max_ziplist_entries);
matches++;
}
if (stringmatch(pattern,"zset-max-ziplist-value",0)) {
addReplyBulkCString(c,"zset-max-ziplist-value");
addReplyBulkLongLong(c,server.zset_max_ziplist_value);
matches++;
}
if (stringmatch(pattern,"lua-time-limit",0)) {
addReplyBulkCString(c,"lua-time-limit");
addReplyBulkLongLong(c,server.lua_time_limit);
matches++;
}
if (stringmatch(pattern,"slowlog-log-slower-than",0)) {
addReplyBulkCString(c,"slowlog-log-slower-than");
addReplyBulkLongLong(c,server.slowlog_log_slower_than);
matches++;
}
if (stringmatch(pattern,"slowlog-max-len",0)) {
addReplyBulkCString(c,"slowlog-max-len");
addReplyBulkLongLong(c,server.slowlog_max_len);
matches++;
}
if (stringmatch(pattern,"loglevel",0)) { if (stringmatch(pattern,"loglevel",0)) {
char *s; char *s;
...@@ -822,6 +823,25 @@ void configGetCommand(redisClient *c) { ...@@ -822,6 +823,25 @@ void configGetCommand(redisClient *c) {
sdsfree(buf); sdsfree(buf);
matches++; matches++;
} }
if (stringmatch(pattern,"unixsocketperm",0)) {
char buf[32];
snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
addReplyBulkCString(c,"unixsocketperm");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"slaveof",0)) {
char buf[256];
addReplyBulkCString(c,"slaveof");
if (server.masterhost)
snprintf(buf,sizeof(buf),"%s %d",
server.masterhost, server.masterport);
else
buf[0] = '\0';
addReplyBulkCString(c,buf);
matches++;
}
setDeferredMultiBulkLength(c,replylen,matches*2); setDeferredMultiBulkLength(c,replylen,matches*2);
} }
......
...@@ -638,6 +638,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { ...@@ -638,6 +638,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n" "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash opening an issue on github:\n\n" " Please report the crash opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n" " http://github.com/antirez/redis/issues\n\n"
" Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n"
); );
/* free(messages); Don't call free() with possibly corrupted memory. */ /* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize) unlink(server.pidfile); if (server.daemonize) unlink(server.pidfile);
......
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#if (ULONG_MAX == 4294967295UL)
#define MEMTEST_32BIT
#elif (ULONG_MAX == 18446744073709551615ULL)
#define MEMTEST_64BIT
#else
#error "ULONG_MAX value not supported."
#endif
#ifdef MEMTEST_32BIT
#define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL
#define ULONG_ZEROONE 0x5555555555555555UL
#else
#define ULONG_ONEZERO 0xaaaaaaaaUL
#define ULONG_ZEROONE 0x55555555UL
#endif
static struct winsize ws;
size_t progress_printed; /* Printed chars in screen-wide progress bar. */
size_t progress_full; /* How many chars to write to fill the progress bar. */
void memtest_progress_start(char *title, int pass) {
int j;
printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */
/* Fill with dots. */
for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf(".");
printf("Please keep the test running several minutes per GB of memory.\n");
printf("Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/");
printf("\x1b[H\x1b[2K"); /* Cursor home, clear current line. */
printf("%s [%d]\n", title, pass); /* Print title. */
progress_printed = 0;
progress_full = ws.ws_col*(ws.ws_row-3);
fflush(stdout);
}
void memtest_progress_end(void) {
printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */
}
void memtest_progress_step(size_t curr, size_t size, char c) {
size_t chars = (curr*progress_full)/size, j;
for (j = 0; j < chars-progress_printed; j++) {
printf("%c",c);
progress_printed++;
}
fflush(stdout);
}
/* Test that addressing is fine. Every location is populated with its own
* address, and finally verified. This test is very fast but may detect
* ASAP big issues with the memory subsystem. */
void memtest_addressing(unsigned long *l, size_t bytes) {
unsigned long words = bytes/sizeof(unsigned long);
unsigned long j, *p;
/* Fill */
p = l;
for (j = 0; j < words; j++) {
*p = (unsigned long)p;
p++;
if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A');
}
/* Test */
p = l;
for (j = 0; j < words; j++) {
if (*p != (unsigned long)p) {
printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n",
(void*) p, *p);
exit(1);
}
p++;
if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A');
}
}
/* Fill words stepping a single page at every write, so we continue to
* touch all the pages in the smallest amount of time reducing the
* effectiveness of caches, and making it hard for the OS to transfer
* pages on the swap. */
void memtest_fill_random(unsigned long *l, size_t bytes) {
unsigned long step = 4096/sizeof(unsigned long);
unsigned long words = bytes/sizeof(unsigned long)/2;
unsigned long iwords = words/step; /* words per iteration */
unsigned long off, w, *l1, *l2;
assert((bytes & 4095) == 0);
for (off = 0; off < step; off++) {
l1 = l+off;
l2 = l1+words;
for (w = 0; w < iwords; w++) {
#ifdef MEMTEST_32BIT
*l1 = *l2 = ((unsigned long) (rand()&0xffff)) |
(((unsigned long) (rand()&0xffff)) << 16);
#else
*l1 = *l2 = ((unsigned long) (rand()&0xffff)) |
(((unsigned long) (rand()&0xffff)) << 16) |
(((unsigned long) (rand()&0xffff)) << 32) |
(((unsigned long) (rand()&0xffff)) << 48);
#endif
l1 += step;
l2 += step;
if ((w & 0xffff) == 0)
memtest_progress_step(w+iwords*off,words,'R');
}
}
}
/* Like memtest_fill_random() but uses the two specified values to fill
* memory, in an alternated way (v1|v2|v1|v2|...) */
void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1,
unsigned long v2, char sym)
{
unsigned long step = 4096/sizeof(unsigned long);
unsigned long words = bytes/sizeof(unsigned long)/2;
unsigned long iwords = words/step; /* words per iteration */
unsigned long off, w, *l1, *l2, v;
assert((bytes & 4095) == 0);
for (off = 0; off < step; off++) {
l1 = l+off;
l2 = l1+words;
v = (off & 1) ? v2 : v1;
for (w = 0; w < iwords; w++) {
#ifdef MEMTEST_32BIT
*l1 = *l2 = ((unsigned long) (rand()&0xffff)) |
(((unsigned long) (rand()&0xffff)) << 16);
#else
*l1 = *l2 = ((unsigned long) (rand()&0xffff)) |
(((unsigned long) (rand()&0xffff)) << 16) |
(((unsigned long) (rand()&0xffff)) << 32) |
(((unsigned long) (rand()&0xffff)) << 48);
#endif
l1 += step;
l2 += step;
if ((w & 0xffff) == 0)
memtest_progress_step(w+iwords*off,words,sym);
}
}
}
void memtest_compare(unsigned long *l, size_t bytes) {
unsigned long words = bytes/sizeof(unsigned long)/2;
unsigned long w, *l1, *l2;
assert((bytes & 4095) == 0);
l1 = l;
l2 = l1+words;
for (w = 0; w < words; w++) {
if (*l1 != *l2) {
printf("\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\n",
(void*)l1, (void*)l2, *l1, *l2);
exit(1);
}
l1 ++;
l2 ++;
if ((w & 0xffff) == 0) memtest_progress_step(w,words,'=');
}
}
void memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) {
int j;
for (j = 0; j < times; j++) {
memtest_progress_start("Compare",pass);
memtest_compare(m,bytes);
memtest_progress_end();
}
}
void memtest_test(size_t megabytes, int passes) {
size_t bytes = megabytes*1024*1024;
unsigned long *m = malloc(bytes);
int pass = 0;
if (m == NULL) {
fprintf(stderr,"Unable to allocate %zu megabytes: %s",
megabytes, strerror(errno));
exit(1);
}
while (pass != passes) {
pass++;
memtest_progress_start("Addressing test",pass);
memtest_addressing(m,bytes);
memtest_progress_end();
memtest_progress_start("Random fill",pass);
memtest_fill_random(m,bytes);
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
memtest_progress_start("Solid fill",pass);
memtest_fill_value(m,bytes,0,(unsigned long)-1,'S');
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
memtest_progress_start("Checkerboard fill",pass);
memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C');
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
}
}
void memtest(size_t megabytes, int passes) {
if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
ws.ws_col = 80;
ws.ws_row = 20;
}
memtest_test(megabytes,passes);
printf("\nYour memory passed this test.\n");
printf("Please if you are still in doubt use the following two tools:\n");
printf("1) memtest86: http://www.memtest86.com/\n");
printf("2) memtester: http://pyropus.ca/software/memtester/\n");
exit(0);
}
...@@ -40,6 +40,13 @@ void queueMultiCommand(redisClient *c) { ...@@ -40,6 +40,13 @@ void queueMultiCommand(redisClient *c) {
c->mstate.count++; c->mstate.count++;
} }
void discardTransaction(redisClient *c) {
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS);;
unwatchAllKeys(c);
}
void multiCommand(redisClient *c) { void multiCommand(redisClient *c) {
if (c->flags & REDIS_MULTI) { if (c->flags & REDIS_MULTI) {
addReplyError(c,"MULTI calls can not be nested"); addReplyError(c,"MULTI calls can not be nested");
...@@ -54,11 +61,7 @@ void discardCommand(redisClient *c) { ...@@ -54,11 +61,7 @@ void discardCommand(redisClient *c) {
addReplyError(c,"DISCARD without MULTI"); addReplyError(c,"DISCARD without MULTI");
return; return;
} }
discardTransaction(c);
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS);;
unwatchAllKeys(c);
addReply(c,shared.ok); addReply(c,shared.ok);
} }
......
...@@ -22,7 +22,6 @@ int listMatchObjects(void *a, void *b) { ...@@ -22,7 +22,6 @@ int listMatchObjects(void *a, void *b) {
redisClient *createClient(int fd) { redisClient *createClient(int fd) {
redisClient *c = zmalloc(sizeof(redisClient)); redisClient *c = zmalloc(sizeof(redisClient));
c->bufpos = 0;
/* passing -1 as fd it is possible to create a non connected client. /* passing -1 as fd it is possible to create a non connected client.
* This is useful since all the Redis commands needs to be executed * This is useful since all the Redis commands needs to be executed
...@@ -42,7 +41,9 @@ redisClient *createClient(int fd) { ...@@ -42,7 +41,9 @@ redisClient *createClient(int fd) {
selectDb(c,0); selectDb(c,0);
c->fd = fd; c->fd = fd;
c->bufpos = 0;
c->querybuf = sdsempty(); c->querybuf = sdsempty();
c->querybuf_peak = 0;
c->reqtype = 0; c->reqtype = 0;
c->argc = 0; c->argc = 0;
c->argv = NULL; c->argv = NULL;
...@@ -51,7 +52,7 @@ redisClient *createClient(int fd) { ...@@ -51,7 +52,7 @@ redisClient *createClient(int fd) {
c->bulklen = -1; c->bulklen = -1;
c->sentlen = 0; c->sentlen = 0;
c->flags = 0; c->flags = 0;
c->lastinteraction = time(NULL); c->ctime = c->lastinteraction = time(NULL);
c->authenticated = 0; c->authenticated = 0;
c->replstate = REDIS_REPL_NONE; c->replstate = REDIS_REPL_NONE;
c->reply = listCreate(); c->reply = listCreate();
...@@ -751,34 +752,6 @@ void resetClient(redisClient *c) { ...@@ -751,34 +752,6 @@ void resetClient(redisClient *c) {
if (!(c->flags & REDIS_MULTI)) c->flags &= (~REDIS_ASKING); if (!(c->flags & REDIS_MULTI)) c->flags &= (~REDIS_ASKING);
} }
void closeTimedoutClients(void) {
redisClient *c;
listNode *ln;
time_t now = time(NULL);
listIter li;
listRewind(server.clients,&li);
while ((ln = listNext(&li)) != NULL) {
c = listNodeValue(ln);
if (server.maxidletime &&
!(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
!(c->flags & REDIS_MASTER) && /* no timeout for masters */
!(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */
dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */
listLength(c->pubsub_patterns) == 0 &&
(now - c->lastinteraction > server.maxidletime))
{
redisLog(REDIS_VERBOSE,"Closing idle client");
freeClient(c);
} else if (c->flags & REDIS_BLOCKED) {
if (c->bpop.timeout != 0 && c->bpop.timeout < now) {
addReply(c,shared.nullmultibulk);
unblockClientWaitingData(c);
}
}
}
}
int processInlineBuffer(redisClient *c) { int processInlineBuffer(redisClient *c) {
char *newline = strstr(c->querybuf,"\r\n"); char *newline = strstr(c->querybuf,"\r\n");
int argc, j; int argc, j;
...@@ -1026,6 +999,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1026,6 +999,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
} }
qblen = sdslen(c->querybuf); qblen = sdslen(c->querybuf);
if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
c->querybuf = sdsMakeRoomFor(c->querybuf, readlen); c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
nread = read(fd, c->querybuf+qblen, readlen); nread = read(fd, c->querybuf+qblen, readlen);
if (nread == -1) { if (nread == -1) {
...@@ -1087,11 +1061,7 @@ sds getClientInfoString(redisClient *client) { ...@@ -1087,11 +1061,7 @@ sds getClientInfoString(redisClient *client) {
time_t now = time(NULL); time_t now = time(NULL);
int emask; int emask;
if (anetPeerToString(client->fd,ip,&port) == -1) { anetPeerToString(client->fd,ip,&port);
ip[0] = '?';
ip[1] = '\0';
port = 0;
}
p = flags; p = flags;
if (client->flags & REDIS_SLAVE) { if (client->flags & REDIS_SLAVE) {
if (client->flags & REDIS_MONITOR) if (client->flags & REDIS_MONITOR)
...@@ -1115,14 +1085,16 @@ sds getClientInfoString(redisClient *client) { ...@@ -1115,14 +1085,16 @@ sds getClientInfoString(redisClient *client) {
if (emask & AE_WRITABLE) *p++ = 'w'; if (emask & AE_WRITABLE) *p++ = 'w';
*p = '\0'; *p = '\0';
return sdscatprintf(sdsempty(), return sdscatprintf(sdsempty(),
"addr=%s:%d fd=%d idle=%ld flags=%s db=%d sub=%d psub=%d qbuf=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s", "addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
ip,port,client->fd, ip,port,client->fd,
(long)(now - client->ctime),
(long)(now - client->lastinteraction), (long)(now - client->lastinteraction),
flags, flags,
client->db->id, client->db->id,
(int) dictSize(client->pubsub_channels), (int) dictSize(client->pubsub_channels),
(int) listLength(client->pubsub_patterns), (int) listLength(client->pubsub_patterns),
(unsigned long) sdslen(client->querybuf), (unsigned long) sdslen(client->querybuf),
(unsigned long) sdsavail(client->querybuf),
(unsigned long) client->bufpos, (unsigned long) client->bufpos,
(unsigned long) listLength(client->reply), (unsigned long) listLength(client->reply),
getClientOutputBufferMemoryUsage(client), getClientOutputBufferMemoryUsage(client),
......
...@@ -56,7 +56,16 @@ robj *createStringObjectFromLongDouble(long double value) { ...@@ -56,7 +56,16 @@ robj *createStringObjectFromLongDouble(long double value) {
* that is "non surprising" for the user (that is, most small decimal * that is "non surprising" for the user (that is, most small decimal
* numbers will be represented in a way that when converted back into * numbers will be represented in a way that when converted back into
* a string are exactly the same as what the user typed.) */ * a string are exactly the same as what the user typed.) */
len = snprintf(buf,sizeof(buf),"%.17Lg", value); len = snprintf(buf,sizeof(buf),"%.17Lf", value);
/* Now remove trailing zeroes after the '.' */
if (strchr(buf,'.') != NULL) {
char *p = buf+len-1;
while(*p == '0') {
p--;
len--;
}
if (*p == '.') len--;
}
return createStringObject(buf,len); return createStringObject(buf,len);
} }
...@@ -95,12 +104,9 @@ robj *createIntsetObject(void) { ...@@ -95,12 +104,9 @@ robj *createIntsetObject(void) {
} }
robj *createHashObject(void) { robj *createHashObject(void) {
/* All the Hashes start as zipmaps. Will be automatically converted unsigned char *zl = ziplistNew();
* into hash tables if there are enough elements or big elements robj *o = createObject(REDIS_HASH, zl);
* inside. */ o->encoding = REDIS_ENCODING_ZIPLIST;
unsigned char *zm = zipmapNew();
robj *o = createObject(REDIS_HASH,zm);
o->encoding = REDIS_ENCODING_ZIPMAP;
return o; return o;
} }
...@@ -176,7 +182,7 @@ void freeHashObject(robj *o) { ...@@ -176,7 +182,7 @@ void freeHashObject(robj *o) {
case REDIS_ENCODING_HT: case REDIS_ENCODING_HT:
dictRelease((dict*) o->ptr); dictRelease((dict*) o->ptr);
break; break;
case REDIS_ENCODING_ZIPMAP: case REDIS_ENCODING_ZIPLIST:
zfree(o->ptr); zfree(o->ptr);
break; break;
default: default:
...@@ -492,7 +498,6 @@ char *strEncoding(int encoding) { ...@@ -492,7 +498,6 @@ char *strEncoding(int encoding) {
case REDIS_ENCODING_RAW: return "raw"; case REDIS_ENCODING_RAW: return "raw";
case REDIS_ENCODING_INT: return "int"; case REDIS_ENCODING_INT: return "int";
case REDIS_ENCODING_HT: return "hashtable"; case REDIS_ENCODING_HT: return "hashtable";
case REDIS_ENCODING_ZIPMAP: return "zipmap";
case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
case REDIS_ENCODING_ZIPLIST: return "ziplist"; case REDIS_ENCODING_ZIPLIST: return "ziplist";
case REDIS_ENCODING_INTSET: return "intset"; case REDIS_ENCODING_INTSET: return "intset";
......
#include "redis.h" #include "redis.h"
#include "lzf.h" /* LZF compression library */ #include "lzf.h" /* LZF compression library */
#include "zipmap.h"
#include <math.h> #include <math.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -424,8 +425,8 @@ int rdbSaveObjectType(rio *rdb, robj *o) { ...@@ -424,8 +425,8 @@ int rdbSaveObjectType(rio *rdb, robj *o) {
else else
redisPanic("Unknown sorted set encoding"); redisPanic("Unknown sorted set encoding");
case REDIS_HASH: case REDIS_HASH:
if (o->encoding == REDIS_ENCODING_ZIPMAP) if (o->encoding == REDIS_ENCODING_ZIPLIST)
return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH_ZIPMAP); return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH_ZIPLIST);
else if (o->encoding == REDIS_ENCODING_HT) else if (o->encoding == REDIS_ENCODING_HT)
return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH); return rdbSaveType(rdb,REDIS_RDB_TYPE_HASH);
else else
...@@ -530,12 +531,13 @@ int rdbSaveObject(rio *rdb, robj *o) { ...@@ -530,12 +531,13 @@ int rdbSaveObject(rio *rdb, robj *o) {
} }
} else if (o->type == REDIS_HASH) { } else if (o->type == REDIS_HASH) {
/* Save a hash value */ /* Save a hash value */
if (o->encoding == REDIS_ENCODING_ZIPMAP) { if (o->encoding == REDIS_ENCODING_ZIPLIST) {
size_t l = zipmapBlobLen((unsigned char*)o->ptr); size_t l = ziplistBlobLen((unsigned char*)o->ptr);
if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1; if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;
nwritten += n; nwritten += n;
} else {
} else if (o->encoding == REDIS_ENCODING_HT) {
dictIterator *di = dictGetIterator(o->ptr); dictIterator *di = dictGetIterator(o->ptr);
dictEntry *de; dictEntry *de;
...@@ -552,7 +554,11 @@ int rdbSaveObject(rio *rdb, robj *o) { ...@@ -552,7 +554,11 @@ int rdbSaveObject(rio *rdb, robj *o) {
nwritten += n; nwritten += n;
} }
dictReleaseIterator(di); dictReleaseIterator(di);
} else {
redisPanic("Unknown hash encoding");
} }
} else { } else {
redisPanic("Unknown object type"); redisPanic("Unknown object type");
} }
...@@ -610,7 +616,7 @@ int rdbSave(char *filename) { ...@@ -610,7 +616,7 @@ int rdbSave(char *filename) {
} }
rioInitWithFile(&rdb,fp); rioInitWithFile(&rdb,fp);
if (rdbWriteRaw(&rdb,"REDIS0003",9) == -1) goto werr; if (rdbWriteRaw(&rdb,"REDIS0004",9) == -1) goto werr;
for (j = 0; j < server.dbnum; j++) { for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j; redisDb *db = server.db+j;
...@@ -656,6 +662,7 @@ int rdbSave(char *filename) { ...@@ -656,6 +662,7 @@ int rdbSave(char *filename) {
redisLog(REDIS_NOTICE,"DB saved on disk"); redisLog(REDIS_NOTICE,"DB saved on disk");
server.dirty = 0; server.dirty = 0;
server.lastsave = time(NULL); server.lastsave = time(NULL);
server.lastbgsave_status = REDIS_OK;
return REDIS_OK; return REDIS_OK;
werr: werr:
...@@ -824,55 +831,74 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -824,55 +831,74 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
maxelelen <= server.zset_max_ziplist_value) maxelelen <= server.zset_max_ziplist_value)
zsetConvert(o,REDIS_ENCODING_ZIPLIST); zsetConvert(o,REDIS_ENCODING_ZIPLIST);
} else if (rdbtype == REDIS_RDB_TYPE_HASH) { } else if (rdbtype == REDIS_RDB_TYPE_HASH) {
size_t hashlen; size_t len;
int ret;
len = rdbLoadLen(rdb, NULL);
if (len == REDIS_RDB_LENERR) return NULL;
if ((hashlen = rdbLoadLen(rdb,NULL)) == REDIS_RDB_LENERR) return NULL;
o = createHashObject(); o = createHashObject();
/* Too many entries? Use an hash table. */
if (hashlen > server.hash_max_zipmap_entries)
convertToRealHash(o);
/* Load every key/value, then set it into the zipmap or hash
* table, as needed. */
while(hashlen--) {
robj *key, *val;
if ((key = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL; /* Too many entries? Use an hash table. */
if ((val = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL; if (len > server.hash_max_ziplist_entries)
/* If we are using a zipmap and there are too big values hashTypeConvert(o, REDIS_ENCODING_HT);
* the object is converted to real hash table encoding. */
if (o->encoding != REDIS_ENCODING_HT && /* Load every field and value into the ziplist */
((key->encoding == REDIS_ENCODING_RAW && while (o->encoding == REDIS_ENCODING_ZIPLIST && len > 0) {
sdslen(key->ptr) > server.hash_max_zipmap_value) || robj *field, *value;
(val->encoding == REDIS_ENCODING_RAW &&
sdslen(val->ptr) > server.hash_max_zipmap_value))) len--;
/* Load raw strings */
field = rdbLoadStringObject(rdb);
if (field == NULL) return NULL;
redisAssert(field->encoding == REDIS_ENCODING_RAW);
value = rdbLoadStringObject(rdb);
if (value == NULL) return NULL;
redisAssert(field->encoding == REDIS_ENCODING_RAW);
/* Add pair to ziplist */
o->ptr = ziplistPush(o->ptr, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
o->ptr = ziplistPush(o->ptr, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
/* Convert to hash table if size threshold is exceeded */
if (sdslen(field->ptr) > server.hash_max_ziplist_value ||
sdslen(value->ptr) > server.hash_max_ziplist_value)
{ {
convertToRealHash(o); decrRefCount(field);
decrRefCount(value);
hashTypeConvert(o, REDIS_ENCODING_HT);
break;
}
decrRefCount(field);
decrRefCount(value);
} }
if (o->encoding == REDIS_ENCODING_ZIPMAP) { /* Load remaining fields and values into the hash table */
unsigned char *zm = o->ptr; while (o->encoding == REDIS_ENCODING_HT && len > 0) {
robj *deckey, *decval; robj *field, *value;
/* We need raw string objects to add them to the zipmap */ len--;
deckey = getDecodedObject(key); /* Load encoded strings */
decval = getDecodedObject(val); field = rdbLoadEncodedStringObject(rdb);
zm = zipmapSet(zm,deckey->ptr,sdslen(deckey->ptr), if (field == NULL) return NULL;
decval->ptr,sdslen(decval->ptr),NULL); value = rdbLoadEncodedStringObject(rdb);
o->ptr = zm; if (value == NULL) return NULL;
decrRefCount(deckey);
decrRefCount(decval); field = tryObjectEncoding(field);
decrRefCount(key); value = tryObjectEncoding(value);
decrRefCount(val);
} else { /* Add pair to hash table */
key = tryObjectEncoding(key); ret = dictAdd((dict*)o->ptr, field, value);
val = tryObjectEncoding(val); redisAssert(ret == REDIS_OK);
dictAdd((dict*)o->ptr,key,val);
}
} }
/* All pairs should be read by now */
redisAssert(len == 0);
} else if (rdbtype == REDIS_RDB_TYPE_HASH_ZIPMAP || } else if (rdbtype == REDIS_RDB_TYPE_HASH_ZIPMAP ||
rdbtype == REDIS_RDB_TYPE_LIST_ZIPLIST || rdbtype == REDIS_RDB_TYPE_LIST_ZIPLIST ||
rdbtype == REDIS_RDB_TYPE_SET_INTSET || rdbtype == REDIS_RDB_TYPE_SET_INTSET ||
rdbtype == REDIS_RDB_TYPE_ZSET_ZIPLIST) rdbtype == REDIS_RDB_TYPE_ZSET_ZIPLIST ||
rdbtype == REDIS_RDB_TYPE_HASH_ZIPLIST)
{ {
robj *aux = rdbLoadStringObject(rdb); robj *aux = rdbLoadStringObject(rdb);
...@@ -890,10 +916,33 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -890,10 +916,33 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
* converted. */ * converted. */
switch(rdbtype) { switch(rdbtype) {
case REDIS_RDB_TYPE_HASH_ZIPMAP: case REDIS_RDB_TYPE_HASH_ZIPMAP:
/* Convert to ziplist encoded hash. This must be deprecated
* when loading dumps created by Redis 2.4 gets deprecated. */
{
unsigned char *zl = ziplistNew();
unsigned char *zi = zipmapRewind(o->ptr);
unsigned char *fstr, *vstr;
unsigned int flen, vlen;
unsigned int maxlen = 0;
while ((zi = zipmapNext(zi, &fstr, &flen, &vstr, &vlen)) != NULL) {
if (flen > maxlen) maxlen = flen;
if (vlen > maxlen) maxlen = vlen;
zl = ziplistPush(zl, fstr, flen, ZIPLIST_TAIL);
zl = ziplistPush(zl, vstr, vlen, ZIPLIST_TAIL);
}
zfree(o->ptr);
o->ptr = zl;
o->type = REDIS_HASH; o->type = REDIS_HASH;
o->encoding = REDIS_ENCODING_ZIPMAP; o->encoding = REDIS_ENCODING_ZIPLIST;
if (zipmapLen(o->ptr) > server.hash_max_zipmap_entries)
convertToRealHash(o); if (hashTypeLength(o) > server.hash_max_ziplist_entries ||
maxlen > server.hash_max_ziplist_value)
{
hashTypeConvert(o, REDIS_ENCODING_HT);
}
}
break; break;
case REDIS_RDB_TYPE_LIST_ZIPLIST: case REDIS_RDB_TYPE_LIST_ZIPLIST:
o->type = REDIS_LIST; o->type = REDIS_LIST;
...@@ -913,6 +962,12 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -913,6 +962,12 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
if (zsetLength(o) > server.zset_max_ziplist_entries) if (zsetLength(o) > server.zset_max_ziplist_entries)
zsetConvert(o,REDIS_ENCODING_SKIPLIST); zsetConvert(o,REDIS_ENCODING_SKIPLIST);
break; break;
case REDIS_RDB_TYPE_HASH_ZIPLIST:
o->type = REDIS_HASH;
o->encoding = REDIS_ENCODING_ZIPLIST;
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
hashTypeConvert(o, REDIS_ENCODING_HT);
break;
default: default:
redisPanic("Unknown encoding"); redisPanic("Unknown encoding");
break; break;
...@@ -973,7 +1028,7 @@ int rdbLoad(char *filename) { ...@@ -973,7 +1028,7 @@ int rdbLoad(char *filename) {
return REDIS_ERR; return REDIS_ERR;
} }
rdbver = atoi(buf+5); rdbver = atoi(buf+5);
if (rdbver < 1 || rdbver > 3) { if (rdbver < 1 || rdbver > 4) {
fclose(fp); fclose(fp);
redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver); redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver);
errno = EINVAL; errno = EINVAL;
...@@ -1061,12 +1116,15 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) { ...@@ -1061,12 +1116,15 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
"Background saving terminated with success"); "Background saving terminated with success");
server.dirty = server.dirty - server.dirty_before_bgsave; server.dirty = server.dirty - server.dirty_before_bgsave;
server.lastsave = time(NULL); server.lastsave = time(NULL);
server.lastbgsave_status = REDIS_OK;
} else if (!bysignal && exitcode != 0) { } else if (!bysignal && exitcode != 0) {
redisLog(REDIS_WARNING, "Background saving error"); redisLog(REDIS_WARNING, "Background saving error");
server.lastbgsave_status = REDIS_ERR;
} else { } else {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"Background saving terminated by signal %d", bysignal); "Background saving terminated by signal %d", bysignal);
rdbRemoveTempFile(server.rdb_child_pid); rdbRemoveTempFile(server.rdb_child_pid);
server.lastbgsave_status = REDIS_ERR;
} }
server.rdb_child_pid = -1; server.rdb_child_pid = -1;
/* Possibly there are slaves waiting for a BGSAVE in order to be served /* Possibly there are slaves waiting for a BGSAVE in order to be served
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#define REDIS_RDB_TYPE_LIST_ZIPLIST 10 #define REDIS_RDB_TYPE_LIST_ZIPLIST 10
#define REDIS_RDB_TYPE_SET_INTSET 11 #define REDIS_RDB_TYPE_SET_INTSET 11
#define REDIS_RDB_TYPE_ZSET_ZIPLIST 12 #define REDIS_RDB_TYPE_ZSET_ZIPLIST 12
#define REDIS_RDB_TYPE_HASH_ZIPLIST 13
/* Test if a type is an object type. */ /* Test if a type is an object type. */
#define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 12)) #define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 12))
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define REDIS_LIST_ZIPLIST 10 #define REDIS_LIST_ZIPLIST 10
#define REDIS_SET_INTSET 11 #define REDIS_SET_INTSET 11
#define REDIS_ZSET_ZIPLIST 12 #define REDIS_ZSET_ZIPLIST 12
#define REDIS_HASH_ZIPLIST 13
/* Objects encoding. Some kind of objects like Strings and Hashes can be /* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object * internally represented in multiple ways. The 'encoding' field of the object
...@@ -136,7 +137,7 @@ int processHeader() { ...@@ -136,7 +137,7 @@ int processHeader() {
} }
dump_version = (int)strtol(buf + 5, NULL, 10); dump_version = (int)strtol(buf + 5, NULL, 10);
if (dump_version < 1 || dump_version > 2) { if (dump_version < 1 || dump_version > 4) {
ERROR("Unknown RDB format version: %d\n", dump_version); ERROR("Unknown RDB format version: %d\n", dump_version);
} }
return 1; return 1;
...@@ -384,6 +385,7 @@ int loadPair(entry *e) { ...@@ -384,6 +385,7 @@ int loadPair(entry *e) {
case REDIS_LIST_ZIPLIST: case REDIS_LIST_ZIPLIST:
case REDIS_SET_INTSET: case REDIS_SET_INTSET:
case REDIS_ZSET_ZIPLIST: case REDIS_ZSET_ZIPLIST:
case REDIS_HASH_ZIPLIST:
if (!processStringObject(NULL)) { if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading entry value"); SHIFT_ERROR(offset, "Error reading entry value");
return 0; return 0;
......
...@@ -49,6 +49,10 @@ ...@@ -49,6 +49,10 @@
#define REDIS_NOTUSED(V) ((void) V) #define REDIS_NOTUSED(V) ((void) V)
#define OUTPUT_STANDARD 0
#define OUTPUT_RAW 1
#define OUTPUT_CSV 2
static redisContext *context; static redisContext *context;
static struct config { static struct config {
char *hostip; char *hostip;
...@@ -64,9 +68,10 @@ static struct config { ...@@ -64,9 +68,10 @@ static struct config {
int latency_mode; int latency_mode;
int cluster_mode; int cluster_mode;
int cluster_reissue_command; int cluster_reissue_command;
int slave_mode;
int stdinarg; /* get last arg from stdin. (-x option) */ int stdinarg; /* get last arg from stdin. (-x option) */
char *auth; char *auth;
int raw_output; /* output mode per command */ int output; /* output mode, see OUTPUT_* defines */
sds mb_delim; sds mb_delim;
char prompt[128]; char prompt[128];
char *eval; char *eval;
...@@ -434,10 +439,46 @@ static sds cliFormatReplyRaw(redisReply *r) { ...@@ -434,10 +439,46 @@ static sds cliFormatReplyRaw(redisReply *r) {
return out; return out;
} }
static sds cliFormatReplyCSV(redisReply *r) {
unsigned int i;
sds out = sdsempty();
switch (r->type) {
case REDIS_REPLY_ERROR:
out = sdscat(out,"ERROR,");
out = sdscatrepr(out,r->str,strlen(r->str));
break;
case REDIS_REPLY_STATUS:
out = sdscatrepr(out,r->str,r->len);
break;
case REDIS_REPLY_INTEGER:
out = sdscatprintf(out,"%lld",r->integer);
break;
case REDIS_REPLY_STRING:
out = sdscatrepr(out,r->str,r->len);
break;
case REDIS_REPLY_NIL:
out = sdscat(out,"NIL\n");
break;
case REDIS_REPLY_ARRAY:
for (i = 0; i < r->elements; i++) {
sds tmp = cliFormatReplyCSV(r->element[i]);
out = sdscatlen(out,tmp,sdslen(tmp));
if (i != r->elements-1) out = sdscat(out,",");
sdsfree(tmp);
}
break;
default:
fprintf(stderr,"Unknown reply type: %d\n", r->type);
exit(1);
}
return out;
}
static int cliReadReply(int output_raw_strings) { static int cliReadReply(int output_raw_strings) {
void *_reply; void *_reply;
redisReply *reply; redisReply *reply;
sds out; sds out = NULL;
int output = 1; int output = 1;
if (redisGetReply(context,&_reply) != REDIS_OK) { if (redisGetReply(context,&_reply) != REDIS_OK) {
...@@ -457,7 +498,8 @@ static int cliReadReply(int output_raw_strings) { ...@@ -457,7 +498,8 @@ static int cliReadReply(int output_raw_strings) {
reply = (redisReply*)_reply; reply = (redisReply*)_reply;
/* Check if we need to connect to a different node and reissue the request. */ /* Check if we need to connect to a different node and reissue the
* request. */
if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR && if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&
(!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK"))) (!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK")))
{ {
...@@ -489,11 +531,14 @@ static int cliReadReply(int output_raw_strings) { ...@@ -489,11 +531,14 @@ static int cliReadReply(int output_raw_strings) {
if (output_raw_strings) { if (output_raw_strings) {
out = cliFormatReplyRaw(reply); out = cliFormatReplyRaw(reply);
} else { } else {
if (config.raw_output) { if (config.output == OUTPUT_RAW) {
out = cliFormatReplyRaw(reply); out = cliFormatReplyRaw(reply);
out = sdscat(out,"\n"); out = sdscat(out,"\n");
} else { } else if (config.output == OUTPUT_STANDARD) {
out = cliFormatReplyTTY(reply,""); out = cliFormatReplyTTY(reply,"");
} else if (config.output == OUTPUT_CSV) {
out = cliFormatReplyCSV(reply);
out = sdscat(out,"\n");
} }
} }
fwrite(out,sdslen(out),1,stdout); fwrite(out,sdslen(out),1,stdout);
...@@ -545,7 +590,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) { ...@@ -545,7 +590,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
} }
if (config.pubsub_mode) { if (config.pubsub_mode) {
if (!config.raw_output) if (config.output != OUTPUT_RAW)
printf("Reading messages... (press Ctrl-C to quit)\n"); printf("Reading messages... (press Ctrl-C to quit)\n");
while (1) { while (1) {
if (cliReadReply(output_raw) != REDIS_OK) exit(1); if (cliReadReply(output_raw) != REDIS_OK) exit(1);
...@@ -603,9 +648,13 @@ static int parseOptions(int argc, char **argv) { ...@@ -603,9 +648,13 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"-a") && !lastarg) { } else if (!strcmp(argv[i],"-a") && !lastarg) {
config.auth = argv[++i]; config.auth = argv[++i];
} else if (!strcmp(argv[i],"--raw")) { } else if (!strcmp(argv[i],"--raw")) {
config.raw_output = 1; config.output = OUTPUT_RAW;
} else if (!strcmp(argv[i],"--csv")) {
config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) { } else if (!strcmp(argv[i],"--latency")) {
config.latency_mode = 1; config.latency_mode = 1;
} else if (!strcmp(argv[i],"--slave")) {
config.slave_mode = 1;
} else if (!strcmp(argv[i],"--eval") && !lastarg) { } else if (!strcmp(argv[i],"--eval") && !lastarg) {
config.eval = argv[++i]; config.eval = argv[++i];
} else if (!strcmp(argv[i],"-c")) { } else if (!strcmp(argv[i],"-c")) {
...@@ -661,6 +710,7 @@ static void usage() { ...@@ -661,6 +710,7 @@ static void usage() {
" -c Enable cluster mode (follow -ASK and -MOVED redirections)\n" " -c Enable cluster mode (follow -ASK and -MOVED redirections)\n"
" --raw Use raw formatting for replies (default when STDOUT is not a tty)\n" " --raw Use raw formatting for replies (default when STDOUT is not a tty)\n"
" --latency Enter a special mode continuously sampling latency.\n" " --latency Enter a special mode continuously sampling latency.\n"
" --slave Simulate a slave showing commands received from the master.\n"
" --eval <file> Send an EVAL command using the Lua script at <file>.\n" " --eval <file> Send an EVAL command using the Lua script at <file>.\n"
" --help Output this help and exit\n" " --help Output this help and exit\n"
" --version Output version and exit\n" " --version Output version and exit\n"
...@@ -866,6 +916,54 @@ static void latencyMode(void) { ...@@ -866,6 +916,54 @@ static void latencyMode(void) {
} }
} }
static void slaveMode(void) {
/* To start we need to send the SYNC command and return the payload.
* The hiredis client lib does not understand this part of the protocol
* and we don't want to mess with its buffers, so everything is performed
* using direct low-level I/O. */
int fd = context->fd;
char buf[1024], *p;
ssize_t nread;
unsigned long long payload;
/* Send the SYNC command. */
if (write(fd,"SYNC\r\n",6) != 6) {
fprintf(stderr,"Error writing to master\n");
exit(1);
}
/* Read $<payload>\r\n, making sure to read just up to "\n" */
p = buf;
while(1) {
nread = read(fd,p,1);
if (nread <= 0) {
fprintf(stderr,"Error reading bulk length while SYNCing\n");
exit(1);
}
if (*p == '\n') break;
p++;
}
*p = '\0';
payload = strtoull(buf+1,NULL,10);
fprintf(stderr,"SYNC with master, discarding %lld bytes of bulk tranfer...\n",
payload);
/* Discard the payload. */
while(payload) {
nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"Error reading RDB payload while SYNCing\n");
exit(1);
}
payload -= nread;
}
fprintf(stderr,"SYNC done. Logging commands from master.\n");
/* Now we can use the hiredis to read the incoming protocol. */
config.output = OUTPUT_CSV;
while (cliReadReply(0) == REDIS_OK);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
int firstarg; int firstarg;
...@@ -884,7 +982,10 @@ int main(int argc, char **argv) { ...@@ -884,7 +982,10 @@ int main(int argc, char **argv) {
config.stdinarg = 0; config.stdinarg = 0;
config.auth = NULL; config.auth = NULL;
config.eval = NULL; config.eval = NULL;
config.raw_output = !isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL); if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
config.output = OUTPUT_RAW;
else
config.output = OUTPUT_STANDARD;
config.mb_delim = sdsnew("\n"); config.mb_delim = sdsnew("\n");
cliInitHelp(); cliInitHelp();
...@@ -898,6 +999,12 @@ int main(int argc, char **argv) { ...@@ -898,6 +999,12 @@ int main(int argc, char **argv) {
latencyMode(); latencyMode();
} }
/* Start in slave mode if appropriate */
if (config.slave_mode) {
cliConnect(0);
slaveMode();
}
/* Start interactive mode when no command is provided */ /* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) { if (argc == 0 && !config.eval) {
/* Note that in repl mode we don't abort on connection error. /* Note that in repl mode we don't abort on connection error.
......
This diff is collapsed.
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include "adlist.h" /* Linked lists */ #include "adlist.h" /* Linked lists */
#include "zmalloc.h" /* total memory usage aware version of malloc/free */ #include "zmalloc.h" /* total memory usage aware version of malloc/free */
#include "anet.h" /* Networking the easy way */ #include "anet.h" /* Networking the easy way */
#include "zipmap.h" /* Compact string -> string data structure */
#include "ziplist.h" /* Compact list data structure */ #include "ziplist.h" /* Compact list data structure */
#include "intset.h" /* Compact integer set structure */ #include "intset.h" /* Compact integer set structure */
#include "version.h" /* Version macro */ #include "version.h" /* Version macro */
...@@ -58,6 +57,9 @@ ...@@ -58,6 +57,9 @@
#define REDIS_REPL_TIMEOUT 60 #define REDIS_REPL_TIMEOUT 60
#define REDIS_REPL_PING_SLAVE_PERIOD 10 #define REDIS_REPL_PING_SLAVE_PERIOD 10
#define REDIS_RUN_ID_SIZE 40
#define REDIS_OPS_SEC_SAMPLES 16
/* Protocol and I/O related defines */ /* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */ #define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
#define REDIS_IOBUF_LEN (1024*16) /* Generic I/O buffer size */ #define REDIS_IOBUF_LEN (1024*16) /* Generic I/O buffer size */
...@@ -205,8 +207,8 @@ ...@@ -205,8 +207,8 @@
#define AOF_FSYNC_EVERYSEC 2 #define AOF_FSYNC_EVERYSEC 2
/* Zip structure related defaults */ /* Zip structure related defaults */
#define REDIS_HASH_MAX_ZIPMAP_ENTRIES 512 #define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512
#define REDIS_HASH_MAX_ZIPMAP_VALUE 64 #define REDIS_HASH_MAX_ZIPLIST_VALUE 64
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512 #define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512
#define REDIS_LIST_MAX_ZIPLIST_VALUE 64 #define REDIS_LIST_MAX_ZIPLIST_VALUE 64
#define REDIS_SET_MAX_INTSET_ENTRIES 512 #define REDIS_SET_MAX_INTSET_ENTRIES 512
...@@ -245,6 +247,11 @@ ...@@ -245,6 +247,11 @@
#define REDIS_CALL_PROPAGATE 4 #define REDIS_CALL_PROPAGATE 4
#define REDIS_CALL_FULL (REDIS_CALL_SLOWLOG | REDIS_CALL_STATS | REDIS_CALL_PROPAGATE) #define REDIS_CALL_FULL (REDIS_CALL_SLOWLOG | REDIS_CALL_STATS | REDIS_CALL_PROPAGATE)
/* Command propagation flags, see propagate() function */
#define REDIS_PROPAGATE_NONE 0
#define REDIS_PROPAGATE_AOF 1
#define REDIS_PROPAGATE_REPL 2
/* We can print the stacktrace, so our assert is defined this way: */ /* We can print the stacktrace, so our assert is defined this way: */
#define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1))) #define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1))) #define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
...@@ -316,6 +323,7 @@ typedef struct redisClient { ...@@ -316,6 +323,7 @@ typedef struct redisClient {
redisDb *db; redisDb *db;
int dictid; int dictid;
sds querybuf; sds querybuf;
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */
int argc; int argc;
robj **argv; robj **argv;
struct redisCommand *cmd, *lastcmd; struct redisCommand *cmd, *lastcmd;
...@@ -325,6 +333,7 @@ typedef struct redisClient { ...@@ -325,6 +333,7 @@ typedef struct redisClient {
list *reply; list *reply;
unsigned long reply_bytes; /* Tot bytes of objects in reply list */ unsigned long reply_bytes; /* Tot bytes of objects in reply list */
int sentlen; int sentlen;
time_t ctime; /* Client creation time */
time_t lastinteraction; /* time of the last interaction, used for timeout */ time_t lastinteraction; /* time of the last interaction, used for timeout */
time_t obuf_soft_limit_reached_time; time_t obuf_soft_limit_reached_time;
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */ int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */
...@@ -356,11 +365,11 @@ struct sharedObjectsStruct { ...@@ -356,11 +365,11 @@ struct sharedObjectsStruct {
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space, robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
*colon, *nullbulk, *nullmultibulk, *queued, *colon, *nullbulk, *nullmultibulk, *queued,
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr, *emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *plus, *outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
*select0, *select1, *select2, *select3, *select4, *roslaveerr, *oomerr, *plus, *select0, *select1, *select2, *select3,
*select5, *select6, *select7, *select8, *select9, *select4, *select5, *select6, *select7, *select8, *select9,
*messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk,
*psubscribebulk, *punsubscribebulk, *del, *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,
*integers[REDIS_SHARED_INTEGERS], *integers[REDIS_SHARED_INTEGERS],
*mbulkhdr[REDIS_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */ *mbulkhdr[REDIS_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */
*bulkhdr[REDIS_SHARED_BULKHDR_LEN]; /* "$<value>\r\n" */ *bulkhdr[REDIS_SHARED_BULKHDR_LEN]; /* "$<value>\r\n" */
...@@ -394,6 +403,30 @@ typedef struct clientBufferLimitsConfig { ...@@ -394,6 +403,30 @@ typedef struct clientBufferLimitsConfig {
time_t soft_limit_seconds; time_t soft_limit_seconds;
} clientBufferLimitsConfig; } clientBufferLimitsConfig;
/* The redisOp structure defines a Redis Operation, that is an instance of
* a command with an argument vector, database ID, propagation target
* (REDIS_PROPAGATE_*), and command pointer.
*
* Currently only used to additionally propagate more commands to AOF/Replication
* after the propagation of the executed command. */
typedef struct redisOp {
robj **argv;
int argc, dbid, target;
struct redisCommand *cmd;
} redisOp;
/* Defines an array of Redis operations. There is an API to add to this
* structure in a easy way.
*
* redisOpArrayInit();
* redisOpArrayAppend();
* redisOpArrayFree();
*/
typedef struct redisOpArray {
redisOp *ops;
int numops;
} redisOpArray;
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* Redis cluster data structures * Redis cluster data structures
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
...@@ -538,6 +571,8 @@ struct redisServer { ...@@ -538,6 +571,8 @@ struct redisServer {
char *requirepass; /* Pass for AUTH command, or NULL */ char *requirepass; /* Pass for AUTH command, or NULL */
char *pidfile; /* PID file path */ char *pidfile; /* PID file path */
int arch_bits; /* 32 or 64 depending on sizeof(long) */ int arch_bits; /* 32 or 64 depending on sizeof(long) */
int cronloops; /* Number of times the cron function run */
char runid[REDIS_RUN_ID_SIZE+1]; /* ID always different at every exec. */
/* Networking */ /* Networking */
int port; /* TCP listening port */ int port; /* TCP listening port */
char *bindaddr; /* Bind address or NULL */ char *bindaddr; /* Bind address or NULL */
...@@ -557,9 +592,7 @@ struct redisServer { ...@@ -557,9 +592,7 @@ struct redisServer {
off_t loading_loaded_bytes; off_t loading_loaded_bytes;
time_t loading_start_time; time_t loading_start_time;
/* Fast pointers to often looked up command */ /* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand; struct redisCommand *delCommand, *multiCommand, *lpushCommand;
int cronloops; /* Number of times the cron function run */
time_t lastsave; /* Unix time of last save succeeede */
/* Fields used only for stats */ /* Fields used only for stats */
time_t stat_starttime; /* Server start time */ time_t stat_starttime; /* Server start time */
long long stat_numcommands; /* Number of processed commands */ long long stat_numcommands; /* Number of processed commands */
...@@ -575,6 +608,12 @@ struct redisServer { ...@@ -575,6 +608,12 @@ struct redisServer {
long long slowlog_entry_id; /* SLOWLOG current entry ID */ long long slowlog_entry_id; /* SLOWLOG current entry ID */
long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */ long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */ unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */
/* The following two are used to track instantaneous "load" in terms
* of operations per second. */
long long ops_sec_last_sample_time; /* Timestamp of last sample (in ms) */
long long ops_sec_last_sample_ops; /* numcommands in last sample */
long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES];
int ops_sec_idx;
/* Configuration */ /* Configuration */
int verbosity; /* Loglevel in redis.conf */ int verbosity; /* Loglevel in redis.conf */
int maxidletime; /* Client timeout in seconds */ int maxidletime; /* Client timeout in seconds */
...@@ -607,6 +646,11 @@ struct redisServer { ...@@ -607,6 +646,11 @@ struct redisServer {
int saveparamslen; /* Number of saving points */ int saveparamslen; /* Number of saving points */
char *rdb_filename; /* Name of RDB file */ char *rdb_filename; /* Name of RDB file */
int rdb_compression; /* Use compression in RDB? */ int rdb_compression; /* Use compression in RDB? */
time_t lastsave; /* Unix time of last save succeeede */
int lastbgsave_status; /* REDIS_OK or REDIS_ERR */
int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */
/* Propagation of commands in AOF / replication */
redisOpArray also_propagate; /* Additional command to propagate. */
/* Logging */ /* Logging */
char *logfile; /* Path of log file */ char *logfile; /* Path of log file */
int syslog_enabled; /* Is syslog enabled? */ int syslog_enabled; /* Is syslog enabled? */
...@@ -627,6 +671,7 @@ struct redisServer { ...@@ -627,6 +671,7 @@ struct redisServer {
char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */ char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */
time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */ time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */
int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_serve_stale_data; /* Serve stale data when link is down? */
int repl_slave_ro; /* Slave is read only? */
time_t repl_down_since; /* Unix time at which link with master went down */ time_t repl_down_since; /* Unix time at which link with master went down */
/* Limits */ /* Limits */
unsigned int maxclients; /* Max number of simultaneous clients */ unsigned int maxclients; /* Max number of simultaneous clients */
...@@ -643,8 +688,8 @@ struct redisServer { ...@@ -643,8 +688,8 @@ struct redisServer {
int sort_alpha; int sort_alpha;
int sort_bypattern; int sort_bypattern;
/* Zip structure config, see redis.conf for more information */ /* Zip structure config, see redis.conf for more information */
size_t hash_max_zipmap_entries; size_t hash_max_ziplist_entries;
size_t hash_max_zipmap_value; size_t hash_max_ziplist_value;
size_t list_max_ziplist_entries; size_t list_max_ziplist_entries;
size_t list_max_ziplist_value; size_t list_max_ziplist_value;
size_t set_max_intset_entries; size_t set_max_intset_entries;
...@@ -748,10 +793,10 @@ typedef struct { ...@@ -748,10 +793,10 @@ typedef struct {
* not both are required, store pointers in the iterator to avoid * not both are required, store pointers in the iterator to avoid
* unnecessary memory allocation for fields/values. */ * unnecessary memory allocation for fields/values. */
typedef struct { typedef struct {
robj *subject;
int encoding; int encoding;
unsigned char *zi;
unsigned char *zk, *zv; unsigned char *fptr, *vptr;
unsigned int zklen, zvlen;
dictIterator *di; dictIterator *di;
dictEntry *de; dictEntry *de;
...@@ -780,6 +825,7 @@ dictType hashDictType; ...@@ -780,6 +825,7 @@ dictType hashDictType;
/* Utils */ /* Utils */
long long ustime(void); long long ustime(void);
long long mstime(void); long long mstime(void);
void getRandomHexChars(char *p, unsigned int len);
/* networking.c -- Networking and Client related operations */ /* networking.c -- Networking and Client related operations */
redisClient *createClient(int fd); redisClient *createClient(int fd);
...@@ -856,6 +902,7 @@ void freeClientMultiState(redisClient *c); ...@@ -856,6 +902,7 @@ void freeClientMultiState(redisClient *c);
void queueMultiCommand(redisClient *c); void queueMultiCommand(redisClient *c);
void touchWatchedKey(redisDb *db, robj *key); void touchWatchedKey(redisDb *db, robj *key);
void touchWatchedKeysOnFlush(int dbid); void touchWatchedKeysOnFlush(int dbid);
void discardTransaction(redisClient *c);
/* Redis object implementation */ /* Redis object implementation */
void decrRefCount(void *o); void decrRefCount(void *o);
...@@ -901,7 +948,7 @@ int syncReadLine(int fd, char *ptr, ssize_t size, int timeout); ...@@ -901,7 +948,7 @@ int syncReadLine(int fd, char *ptr, ssize_t size, int timeout);
/* Replication */ /* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc); void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc); void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc);
void updateSlavesWaitingBgsave(int bgsaveerr); void updateSlavesWaitingBgsave(int bgsaveerr);
void replicationCron(void); void replicationCron(void);
...@@ -950,6 +997,8 @@ void setupSignalHandlers(void); ...@@ -950,6 +997,8 @@ void setupSignalHandlers(void);
struct redisCommand *lookupCommand(sds name); struct redisCommand *lookupCommand(sds name);
struct redisCommand *lookupCommandByCString(char *s); struct redisCommand *lookupCommandByCString(char *s);
void call(redisClient *c, int flags); void call(redisClient *c, int flags);
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags);
void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int target);
int prepareForShutdown(); int prepareForShutdown();
void redisLog(int level, const char *fmt, ...); void redisLog(int level, const char *fmt, ...);
void redisLogRaw(int level, const char *msg); void redisLogRaw(int level, const char *msg);
...@@ -974,10 +1023,9 @@ unsigned long setTypeSize(robj *subject); ...@@ -974,10 +1023,9 @@ unsigned long setTypeSize(robj *subject);
void setTypeConvert(robj *subject, int enc); void setTypeConvert(robj *subject, int enc);
/* Hash data type */ /* Hash data type */
void convertToRealHash(robj *o); void hashTypeConvert(robj *o, int enc);
void hashTypeTryConversion(robj *subject, robj **argv, int start, int end); void hashTypeTryConversion(robj *subject, robj **argv, int start, int end);
void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2); void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2);
int hashTypeGet(robj *o, robj *key, robj **objval, unsigned char **v, unsigned int *vlen);
robj *hashTypeGetObject(robj *o, robj *key); robj *hashTypeGetObject(robj *o, robj *key);
int hashTypeExists(robj *o, robj *key); int hashTypeExists(robj *o, robj *key);
int hashTypeSet(robj *o, robj *key, robj *value); int hashTypeSet(robj *o, robj *key, robj *value);
...@@ -986,7 +1034,11 @@ unsigned long hashTypeLength(robj *o); ...@@ -986,7 +1034,11 @@ unsigned long hashTypeLength(robj *o);
hashTypeIterator *hashTypeInitIterator(robj *subject); hashTypeIterator *hashTypeInitIterator(robj *subject);
void hashTypeReleaseIterator(hashTypeIterator *hi); void hashTypeReleaseIterator(hashTypeIterator *hi);
int hashTypeNext(hashTypeIterator *hi); int hashTypeNext(hashTypeIterator *hi);
int hashTypeCurrent(hashTypeIterator *hi, int what, robj **objval, unsigned char **v, unsigned int *vlen); void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
unsigned char **vstr,
unsigned int *vlen,
long long *vll);
void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst);
robj *hashTypeCurrentObject(hashTypeIterator *hi, int what); robj *hashTypeCurrentObject(hashTypeIterator *hi, int what);
robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key); robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key);
...@@ -1185,6 +1237,7 @@ void clientCommand(redisClient *c); ...@@ -1185,6 +1237,7 @@ void clientCommand(redisClient *c);
void evalCommand(redisClient *c); void evalCommand(redisClient *c);
void evalShaCommand(redisClient *c); void evalShaCommand(redisClient *c);
void scriptCommand(redisClient *c); void scriptCommand(redisClient *c);
void timeCommand(redisClient *c);
#if defined(__GNUC__) #if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated)); void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
......
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