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,53 +610,61 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) { ...@@ -609,53 +610,61 @@ 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; if (count == 0) {
unsigned int flen, vlen; int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ?
REDIS_AOF_REWRITE_ITEMS_PER_CMD : items;
while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) { if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
if (count == 0) { if (rioWriteBulkString(r,"HMSET",5) == 0) return 0;
int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? if (rioWriteBulkObject(r,key) == 0) return 0;
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 (rioWriteBulkString(r,(char*)field,flen) == 0) return 0;
if (rioWriteBulkString(r,(char*)val,vlen) == 0) return 0;
if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
} }
} else {
dictIterator *di = dictGetIterator(o->ptr);
dictEntry *de;
while((de = dictNext(di)) != NULL) { if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_KEY) == 0) return 0;
robj *field = dictGetKey(de); if (rioWriteHashIteratorCursor(r, hi, REDIS_HASH_VALUE) == 0) return 0;
robj *val = dictGetVal(de); if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
if (count == 0) { hashTypeReleaseIterator(hi);
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);
} }
......
...@@ -636,8 +636,9 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { ...@@ -636,8 +636,9 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"\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. */ /* Too many entries? Use an hash table. */
if (hashlen > server.hash_max_zipmap_entries) if (len > server.hash_max_ziplist_entries)
convertToRealHash(o); hashTypeConvert(o, REDIS_ENCODING_HT);
/* Load every key/value, then set it into the zipmap or hash
* table, as needed. */ /* Load every field and value into the ziplist */
while(hashlen--) { while (o->encoding == REDIS_ENCODING_ZIPLIST && len > 0) {
robj *key, *val; robj *field, *value;
if ((key = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL; len--;
if ((val = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL; /* Load raw strings */
/* If we are using a zipmap and there are too big values field = rdbLoadStringObject(rdb);
* the object is converted to real hash table encoding. */ if (field == NULL) return NULL;
if (o->encoding != REDIS_ENCODING_HT && redisAssert(field->encoding == REDIS_ENCODING_RAW);
((key->encoding == REDIS_ENCODING_RAW && value = rdbLoadStringObject(rdb);
sdslen(key->ptr) > server.hash_max_zipmap_value) || if (value == NULL) return NULL;
(val->encoding == REDIS_ENCODING_RAW && redisAssert(field->encoding == REDIS_ENCODING_RAW);
sdslen(val->ptr) > server.hash_max_zipmap_value)))
/* 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:
o->type = REDIS_HASH; /* Convert to ziplist encoded hash. This must be deprecated
o->encoding = REDIS_ENCODING_ZIPMAP; * when loading dumps created by Redis 2.4 gets deprecated. */
if (zipmapLen(o->ptr) > server.hash_max_zipmap_entries) {
convertToRealHash(o); 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->encoding = REDIS_ENCODING_ZIPLIST;
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.
......
...@@ -211,7 +211,7 @@ struct redisCommand redisCommandTable[] = { ...@@ -211,7 +211,7 @@ struct redisCommand redisCommandTable[] = {
{"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0}, {"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0}, {"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0}, {"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
{"exec",execCommand,1,"wms",0,NULL,0,0,0,0,0}, {"exec",execCommand,1,"s",0,NULL,0,0,0,0,0},
{"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0}, {"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
{"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0}, {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
{"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0}, {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
...@@ -223,13 +223,13 @@ struct redisCommand redisCommandTable[] = { ...@@ -223,13 +223,13 @@ struct redisCommand redisCommandTable[] = {
{"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0}, {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0}, {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
{"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0}, {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"aws",0,NULL,0,0,0,0,0}, {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0}, {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0}, {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
{"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0}, {"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0}, {"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
{"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0}, {"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"rpf",0,NULL,0,0,0,0,0}, {"publish",publishCommand,3,"pf",0,NULL,0,0,0,0,0},
{"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0}, {"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
{"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0}, {"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
{"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0}, {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
...@@ -239,10 +239,11 @@ struct redisCommand redisCommandTable[] = { ...@@ -239,10 +239,11 @@ struct redisCommand redisCommandTable[] = {
{"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0}, {"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0},
{"object",objectCommand,-2,"r",0,NULL,2,2,2,0,0}, {"object",objectCommand,-2,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0}, {"eval",evalCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0}, {"evalsha",evalShaCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0}, {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
{"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0} {"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0},
{"time",timeCommand,1,"rR",0,NULL,0,0,0,0,0}
}; };
/*============================ Utility functions ============================ */ /*============================ Utility functions ============================ */
...@@ -615,6 +616,110 @@ void updateLRUClock(void) { ...@@ -615,6 +616,110 @@ void updateLRUClock(void) {
REDIS_LRU_CLOCK_MAX; REDIS_LRU_CLOCK_MAX;
} }
/* Add a sample to the operations per second array of samples. */
void trackOperationsPerSecond(void) {
long long t = mstime() - server.ops_sec_last_sample_time;
long long ops = server.stat_numcommands - server.ops_sec_last_sample_ops;
long long ops_sec;
ops_sec = t > 0 ? (ops*1000/t) : 0;
server.ops_sec_samples[server.ops_sec_idx] = ops_sec;
server.ops_sec_idx = (server.ops_sec_idx+1) % REDIS_OPS_SEC_SAMPLES;
server.ops_sec_last_sample_time = mstime();
server.ops_sec_last_sample_ops = server.stat_numcommands;
}
/* Return the mean of all the samples. */
long long getOperationsPerSecond(void) {
int j;
long long sum = 0;
for (j = 0; j < REDIS_OPS_SEC_SAMPLES; j++)
sum += server.ops_sec_samples[j];
return sum / REDIS_OPS_SEC_SAMPLES;
}
/* Check for timeouts. Returns non-zero if the client was terminated */
int clientsCronHandleTimeout(redisClient *c) {
time_t now = server.unixtime;
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);
return 1;
} else if (c->flags & REDIS_BLOCKED) {
if (c->bpop.timeout != 0 && c->bpop.timeout < now) {
addReply(c,shared.nullmultibulk);
unblockClientWaitingData(c);
}
}
return 0;
}
/* The client query buffer is an sds.c string that can end with a lot of
* free space not used, this function reclaims space if needed.
*
* The funciton always returns 0 as it never terminates the client. */
int clientsCronResizeQueryBuffer(redisClient *c) {
size_t querybuf_size = sdsAllocSize(c->querybuf);
time_t idletime = server.unixtime - c->lastinteraction;
/* There are two conditions to resize the query buffer:
* 1) Query buffer is > BIG_ARG and too big for latest peak.
* 2) Client is inactive and the buffer is bigger than 1k. */
if (((querybuf_size > REDIS_MBULK_BIG_ARG) &&
(querybuf_size/(c->querybuf_peak+1)) > 2) ||
(querybuf_size > 1024 && idletime > 2))
{
/* Only resize the query buffer if it is actually wasting space. */
if (sdsavail(c->querybuf) > 1024) {
c->querybuf = sdsRemoveFreeSpace(c->querybuf);
}
}
/* Reset the peak again to capture the peak memory usage in the next
* cycle. */
c->querybuf_peak = 0;
return 0;
}
void clientsCron(void) {
/* Make sure to process at least 1/100 of clients per call.
* Since this function is called 10 times per second we are sure that
* in the worst case we process all the clients in 10 seconds.
* In normal conditions (a reasonable number of clients) we process
* all the clients in a shorter time. */
int numclients = listLength(server.clients);
int iterations = numclients/100;
if (iterations < 50)
iterations = (numclients < 50) ? numclients : 50;
while(listLength(server.clients) && iterations--) {
redisClient *c;
listNode *head;
/* Rotate the list, take the current head, process.
* This way if the client must be removed from the list it's the
* first element and we don't incur into O(N) computation. */
listRotate(server.clients);
head = listFirst(server.clients);
c = listNodeValue(head);
/* The following functions do different service checks on the client.
* The protocol is that they return non-zero if the client was
* terminated. */
if (clientsCronHandleTimeout(c)) continue;
if (clientsCronResizeQueryBuffer(c)) continue;
}
}
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
int j, loops = server.cronloops; int j, loops = server.cronloops;
REDIS_NOTUSED(eventLoop); REDIS_NOTUSED(eventLoop);
...@@ -627,6 +732,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { ...@@ -627,6 +732,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* To access a global var is faster than calling time(NULL) */ * To access a global var is faster than calling time(NULL) */
server.unixtime = time(NULL); server.unixtime = time(NULL);
trackOperationsPerSecond();
/* We have just 22 bits per object for LRU information. /* We have just 22 bits per object for LRU information.
* So we use an (eventually wrapping) LRU clock with 10 seconds resolution. * So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
* 2^22 bits with 10 seconds resoluton is more or less 1.5 years. * 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
...@@ -684,9 +791,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { ...@@ -684,9 +791,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
zmalloc_used_memory()); zmalloc_used_memory());
} }
/* Close connections of timedout clients */ /* We need to do a few operations on clients asynchronously. */
if ((server.maxidletime && !(loops % 100)) || server.bpop_blocked_clients) clientsCron();
closeTimedoutClients();
/* Start a scheduled AOF rewrite if this was requested by the user while /* Start a scheduled AOF rewrite if this was requested by the user while
* a BGSAVE was in progress. */ * a BGSAVE was in progress. */
...@@ -832,6 +938,12 @@ void createSharedObjects(void) { ...@@ -832,6 +938,12 @@ void createSharedObjects(void) {
"-LOADING Redis is loading the dataset in memory\r\n")); "-LOADING Redis is loading the dataset in memory\r\n"));
shared.slowscripterr = createObject(REDIS_STRING,sdsnew( shared.slowscripterr = createObject(REDIS_STRING,sdsnew(
"-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\r\n")); "-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\r\n"));
shared.bgsaveerr = createObject(REDIS_STRING,sdsnew(
"-MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.\r\n"));
shared.roslaveerr = createObject(REDIS_STRING,sdsnew(
"-READONLY You can't write against a read only slave.\r\n"));
shared.oomerr = createObject(REDIS_STRING,sdsnew(
"-OOM command not allowed when used memory > 'maxmemory'.\r\n"));
shared.space = createObject(REDIS_STRING,sdsnew(" ")); shared.space = createObject(REDIS_STRING,sdsnew(" "));
shared.colon = createObject(REDIS_STRING,sdsnew(":")); shared.colon = createObject(REDIS_STRING,sdsnew(":"));
shared.plus = createObject(REDIS_STRING,sdsnew("+")); shared.plus = createObject(REDIS_STRING,sdsnew("+"));
...@@ -852,6 +964,8 @@ void createSharedObjects(void) { ...@@ -852,6 +964,8 @@ void createSharedObjects(void) {
shared.psubscribebulk = createStringObject("$10\r\npsubscribe\r\n",17); shared.psubscribebulk = createStringObject("$10\r\npsubscribe\r\n",17);
shared.punsubscribebulk = createStringObject("$12\r\npunsubscribe\r\n",19); shared.punsubscribebulk = createStringObject("$12\r\npunsubscribe\r\n",19);
shared.del = createStringObject("DEL",3); shared.del = createStringObject("DEL",3);
shared.rpop = createStringObject("RPOP",4);
shared.lpop = createStringObject("LPOP",4);
for (j = 0; j < REDIS_SHARED_INTEGERS; j++) { for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {
shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j); shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);
shared.integers[j]->encoding = REDIS_ENCODING_INT; shared.integers[j]->encoding = REDIS_ENCODING_INT;
...@@ -865,6 +979,8 @@ void createSharedObjects(void) { ...@@ -865,6 +979,8 @@ void createSharedObjects(void) {
} }
void initServerConfig() { void initServerConfig() {
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
server.runid[REDIS_RUN_ID_SIZE] = '\0';
server.arch_bits = (sizeof(long) == 8) ? 64 : 32; server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
server.port = REDIS_SERVERPORT; server.port = REDIS_SERVERPORT;
server.bindaddr = NULL; server.bindaddr = NULL;
...@@ -905,8 +1021,8 @@ void initServerConfig() { ...@@ -905,8 +1021,8 @@ void initServerConfig() {
server.maxmemory = 0; server.maxmemory = 0;
server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
server.maxmemory_samples = 3; server.maxmemory_samples = 3;
server.hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES; server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
server.hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE; server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES; server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE; server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES; server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
...@@ -936,6 +1052,7 @@ void initServerConfig() { ...@@ -936,6 +1052,7 @@ void initServerConfig() {
server.repl_state = REDIS_REPL_NONE; server.repl_state = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT; server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = 1; server.repl_serve_stale_data = 1;
server.repl_slave_ro = 1;
server.repl_down_since = -1; server.repl_down_since = -1;
/* Client output buffer limits */ /* Client output buffer limits */
...@@ -962,6 +1079,7 @@ void initServerConfig() { ...@@ -962,6 +1079,7 @@ void initServerConfig() {
populateCommandTable(); populateCommandTable();
server.delCommand = lookupCommandByCString("del"); server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi"); server.multiCommand = lookupCommandByCString("multi");
server.lpushCommand = lookupCommandByCString("lpush");
/* Slow log */ /* Slow log */
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN; server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
...@@ -1083,7 +1201,13 @@ void initServer() { ...@@ -1083,7 +1201,13 @@ void initServer() {
server.stat_peak_memory = 0; server.stat_peak_memory = 0;
server.stat_fork_time = 0; server.stat_fork_time = 0;
server.stat_rejected_conn = 0; server.stat_rejected_conn = 0;
memset(server.ops_sec_samples,0,sizeof(server.ops_sec_samples));
server.ops_sec_idx = 0;
server.ops_sec_last_sample_time = mstime();
server.ops_sec_last_sample_ops = 0;
server.unixtime = time(NULL); server.unixtime = time(NULL);
server.lastbgsave_status = REDIS_OK;
server.stop_writes_on_bgsave_err = 1;
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL); aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR) oom("creating file event"); acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
...@@ -1160,6 +1284,43 @@ void resetCommandTableStats(void) { ...@@ -1160,6 +1284,43 @@ void resetCommandTableStats(void) {
} }
} }
/* ========================== Redis OP Array API ============================ */
void redisOpArrayInit(redisOpArray *oa) {
oa->ops = NULL;
oa->numops = 0;
}
int redisOpArrayAppend(redisOpArray *oa, struct redisCommand *cmd, int dbid,
robj **argv, int argc, int target)
{
redisOp *op;
oa->ops = zrealloc(oa->ops,sizeof(redisOp)*(oa->numops+1));
op = oa->ops+oa->numops;
op->cmd = cmd;
op->dbid = dbid;
op->argv = argv;
op->argc = argc;
op->target = target;
oa->numops++;
return oa->numops;
}
void redisOpArrayFree(redisOpArray *oa) {
while(oa->numops) {
int j;
redisOp *op;
oa->numops--;
op = oa->ops+oa->numops;
for (j = 0; j < op->argc; j++)
decrRefCount(op->argv[j]);
zfree(op->argv);
}
zfree(oa->ops);
}
/* ====================== Commands lookup and execution ===================== */ /* ====================== Commands lookup and execution ===================== */
struct redisCommand *lookupCommand(sds name) { struct redisCommand *lookupCommand(sds name) {
...@@ -1175,10 +1336,42 @@ struct redisCommand *lookupCommandByCString(char *s) { ...@@ -1175,10 +1336,42 @@ struct redisCommand *lookupCommandByCString(char *s) {
return cmd; return cmd;
} }
/* Propagate the specified command (in the context of the specified database id)
* to AOF, Slaves and Monitors.
*
* flags are an xor between:
* + REDIS_PROPAGATE_NONE (no propagation of command at all)
* + REDIS_PROPAGATE_AOF (propagate into the AOF file if is enabled)
* + REDIS_PROPAGATE_REPL (propagate into the replication link)
*/
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int flags)
{
if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)
feedAppendOnlyFile(cmd,dbid,argv,argc);
if (flags & REDIS_PROPAGATE_REPL && listLength(server.slaves))
replicationFeedSlaves(server.slaves,dbid,argv,argc);
}
/* Used inside commands to schedule the propagation of additional commands
* after the current command is propagated to AOF / Replication. */
void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int target)
{
redisOpArrayAppend(&server.also_propagate,cmd,dbid,argv,argc,target);
}
/* Call() is the core of Redis execution of a command */ /* Call() is the core of Redis execution of a command */
void call(redisClient *c, int flags) { void call(redisClient *c, int flags) {
long long dirty, start = ustime(), duration; long long dirty, start = ustime(), duration;
/* Sent the command to clients in MONITOR mode, only if the commands are
* not geneated from reading an AOF. */
if (listLength(server.monitors) && !server.loading)
replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
/* Call the command. */
redisOpArrayInit(&server.also_propagate);
dirty = server.dirty; dirty = server.dirty;
c->cmd->proc(c); c->cmd->proc(c);
dirty = server.dirty-dirty; dirty = server.dirty-dirty;
...@@ -1189,20 +1382,37 @@ void call(redisClient *c, int flags) { ...@@ -1189,20 +1382,37 @@ void call(redisClient *c, int flags) {
if (server.loading && c->flags & REDIS_LUA_CLIENT) if (server.loading && c->flags & REDIS_LUA_CLIENT)
flags &= ~(REDIS_CALL_SLOWLOG | REDIS_CALL_STATS); flags &= ~(REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);
/* Log the command into the Slow log if needed, and populate the
* per-command statistics that we show in INFO commandstats. */
if (flags & REDIS_CALL_SLOWLOG) if (flags & REDIS_CALL_SLOWLOG)
slowlogPushEntryIfNeeded(c->argv,c->argc,duration); slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
if (flags & REDIS_CALL_STATS) { if (flags & REDIS_CALL_STATS) {
c->cmd->microseconds += duration; c->cmd->microseconds += duration;
c->cmd->calls++; c->cmd->calls++;
} }
/* Propagate the command into the AOF and replication link */
if (flags & REDIS_CALL_PROPAGATE) { if (flags & REDIS_CALL_PROPAGATE) {
if (server.aof_state != REDIS_AOF_OFF && dirty > 0) int flags = REDIS_PROPAGATE_NONE;
feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc);
if ((dirty > 0 || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) && if (c->cmd->flags & REDIS_CMD_FORCE_REPLICATION)
listLength(server.slaves)) flags |= REDIS_PROPAGATE_REPL;
replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc); if (dirty)
if (listLength(server.monitors)) flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);
replicationFeedMonitors(server.monitors,c->db->id,c->argv,c->argc); if (flags != REDIS_PROPAGATE_NONE)
propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
}
/* Commands such as LPUSH or BRPOPLPUSH may propagate an additional
* PUSH command. */
if (server.also_propagate.numops) {
int j;
redisOp *rop;
for (j = 0; j < server.also_propagate.numops; j++) {
rop = &server.also_propagate.ops[j];
propagate(rop->cmd, rop->dbid, rop->argv, rop->argc, rop->target);
}
redisOpArrayFree(&server.also_propagate);
} }
server.stat_numcommands++; server.stat_numcommands++;
} }
...@@ -1278,12 +1488,31 @@ int processCommand(redisClient *c) { ...@@ -1278,12 +1488,31 @@ int processCommand(redisClient *c) {
if (server.maxmemory) { if (server.maxmemory) {
int retval = freeMemoryIfNeeded(); int retval = freeMemoryIfNeeded();
if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) { if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {
addReplyError(c, addReply(c, shared.oomerr);
"command not allowed when used memory > 'maxmemory'");
return REDIS_OK; return REDIS_OK;
} }
} }
/* Don't accept write commands if there are problems persisting on disk. */
if (server.stop_writes_on_bgsave_err &&
server.saveparamslen > 0
&& server.lastbgsave_status == REDIS_ERR &&
c->cmd->flags & REDIS_CMD_WRITE)
{
addReply(c, shared.bgsaveerr);
return REDIS_OK;
}
/* Don't accept wirte commands if this is a read only slave. But
* accept write commands if this is our master. */
if (server.masterhost && server.repl_slave_ro &&
!(c->flags & REDIS_MASTER) &&
c->cmd->flags & REDIS_CMD_WRITE)
{
addReply(c, shared.roslaveerr);
return REDIS_OK;
}
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */ /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
&& &&
...@@ -1416,6 +1645,17 @@ void echoCommand(redisClient *c) { ...@@ -1416,6 +1645,17 @@ void echoCommand(redisClient *c) {
addReplyBulk(c,c->argv[1]); addReplyBulk(c,c->argv[1]);
} }
void timeCommand(redisClient *c) {
struct timeval tv;
/* gettimeofday() can only fail if &tv is a bad addresss so we
* don't check for errors. */
gettimeofday(&tv,NULL);
addReplyMultiBulkLen(c,2);
addReplyBulkLongLong(c,tv.tv_sec);
addReplyBulkLongLong(c,tv.tv_usec);
}
/* Convert an amount of bytes into a human readable string in the form /* Convert an amount of bytes into a human readable string in the form
* of 100B, 2G, 100M, 4K, and so forth. */ * of 100B, 2G, 100M, 4K, and so forth. */
void bytesToHuman(char *s, unsigned long long n) { void bytesToHuman(char *s, unsigned long long n) {
...@@ -1470,6 +1710,7 @@ sds genRedisInfoString(char *section) { ...@@ -1470,6 +1710,7 @@ sds genRedisInfoString(char *section) {
"multiplexing_api:%s\r\n" "multiplexing_api:%s\r\n"
"gcc_version:%d.%d.%d\r\n" "gcc_version:%d.%d.%d\r\n"
"process_id:%ld\r\n" "process_id:%ld\r\n"
"run_id:%s\r\n"
"tcp_port:%d\r\n" "tcp_port:%d\r\n"
"uptime_in_seconds:%ld\r\n" "uptime_in_seconds:%ld\r\n"
"uptime_in_days:%ld\r\n" "uptime_in_days:%ld\r\n"
...@@ -1485,6 +1726,7 @@ sds genRedisInfoString(char *section) { ...@@ -1485,6 +1726,7 @@ sds genRedisInfoString(char *section) {
0,0,0, 0,0,0,
#endif #endif
(long) getpid(), (long) getpid(),
server.runid,
server.port, server.port,
uptime, uptime,
uptime/(3600*24), uptime/(3600*24),
...@@ -1544,12 +1786,14 @@ sds genRedisInfoString(char *section) { ...@@ -1544,12 +1786,14 @@ sds genRedisInfoString(char *section) {
"changes_since_last_save:%lld\r\n" "changes_since_last_save:%lld\r\n"
"bgsave_in_progress:%d\r\n" "bgsave_in_progress:%d\r\n"
"last_save_time:%ld\r\n" "last_save_time:%ld\r\n"
"last_bgsave_status:%s\r\n"
"bgrewriteaof_in_progress:%d\r\n", "bgrewriteaof_in_progress:%d\r\n",
server.loading, server.loading,
server.aof_state != REDIS_AOF_OFF, server.aof_state != REDIS_AOF_OFF,
server.dirty, server.dirty,
server.rdb_child_pid != -1, server.rdb_child_pid != -1,
server.lastsave, server.lastsave,
server.lastbgsave_status == REDIS_OK ? "ok" : "err",
server.aof_child_pid != -1); server.aof_child_pid != -1);
if (server.aof_state != REDIS_AOF_OFF) { if (server.aof_state != REDIS_AOF_OFF) {
...@@ -1605,6 +1849,7 @@ sds genRedisInfoString(char *section) { ...@@ -1605,6 +1849,7 @@ sds genRedisInfoString(char *section) {
"# Stats\r\n" "# Stats\r\n"
"total_connections_received:%lld\r\n" "total_connections_received:%lld\r\n"
"total_commands_processed:%lld\r\n" "total_commands_processed:%lld\r\n"
"instantaneous_ops_per_sec:%lld\r\n"
"rejected_connections:%lld\r\n" "rejected_connections:%lld\r\n"
"expired_keys:%lld\r\n" "expired_keys:%lld\r\n"
"evicted_keys:%lld\r\n" "evicted_keys:%lld\r\n"
...@@ -1615,6 +1860,7 @@ sds genRedisInfoString(char *section) { ...@@ -1615,6 +1860,7 @@ sds genRedisInfoString(char *section) {
"latest_fork_usec:%lld\r\n", "latest_fork_usec:%lld\r\n",
server.stat_numconnections, server.stat_numconnections,
server.stat_numcommands, server.stat_numcommands,
getOperationsPerSecond(),
server.stat_rejected_conn, server.stat_rejected_conn,
server.stat_expiredkeys, server.stat_expiredkeys,
server.stat_evictedkeys, server.stat_evictedkeys,
...@@ -1990,8 +2236,8 @@ void daemonize(void) { ...@@ -1990,8 +2236,8 @@ void daemonize(void) {
} }
void version() { void version() {
printf("Redis server version %s (%s:%d)\n", REDIS_VERSION, printf("Redis server v=%s sha=%s:%d malloc=%s\n", REDIS_VERSION,
redisGitSHA1(), atoi(redisGitDirty()) > 0); redisGitSHA1(), atoi(redisGitDirty()) > 0, ZMALLOC_LIB);
exit(0); exit(0);
} }
...@@ -1999,7 +2245,8 @@ void usage() { ...@@ -1999,7 +2245,8 @@ void usage() {
fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n"); fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n");
fprintf(stderr," ./redis-server - (read config from stdin)\n"); fprintf(stderr," ./redis-server - (read config from stdin)\n");
fprintf(stderr," ./redis-server -v or --version\n"); fprintf(stderr," ./redis-server -v or --version\n");
fprintf(stderr," ./redis-server -h or --help\n\n"); fprintf(stderr," ./redis-server -h or --help\n");
fprintf(stderr," ./redis-server --test-memory <megabytes>\n\n");
fprintf(stderr,"Examples:\n"); fprintf(stderr,"Examples:\n");
fprintf(stderr," ./redis-server (run the server with default conf)\n"); fprintf(stderr," ./redis-server (run the server with default conf)\n");
fprintf(stderr," ./redis-server /etc/redis/6379.conf\n"); fprintf(stderr," ./redis-server /etc/redis/6379.conf\n");
...@@ -2055,6 +2302,8 @@ void setupSignalHandlers(void) { ...@@ -2055,6 +2302,8 @@ void setupSignalHandlers(void) {
return; return;
} }
void memtest(size_t megabytes, int passes);
int main(int argc, char **argv) { int main(int argc, char **argv) {
long long start; long long start;
struct timeval tv; struct timeval tv;
...@@ -2076,6 +2325,17 @@ int main(int argc, char **argv) { ...@@ -2076,6 +2325,17 @@ int main(int argc, char **argv) {
strcmp(argv[1], "--version") == 0) version(); strcmp(argv[1], "--version") == 0) version();
if (strcmp(argv[1], "--help") == 0 || if (strcmp(argv[1], "--help") == 0 ||
strcmp(argv[1], "-h") == 0) usage(); strcmp(argv[1], "-h") == 0) usage();
if (strcmp(argv[1], "--test-memory") == 0) {
if (argc == 3) {
memtest(atoi(argv[2]),50);
exit(0);
} else {
fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");
fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");
exit(1);
}
}
/* First argument is the config file name? */ /* First argument is the config file name? */
if (argv[j][0] != '-' || argv[j][1] != '-') if (argv[j][0] != '-' || argv[j][1] != '-')
configfile = argv[j++]; configfile = argv[j++];
......
...@@ -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