提交 97c4b7b8 编写于 作者: A Alexis Campailla

Merge 2.8 branch from antirez\redis into 2.8

......@@ -14,6 +14,29 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
--------------------------------------------------------------------------------
--[ Redis 2.8.20 ] Release date: 5 May 2015
Upgrade urgency: LOW for Redis, MODERATE for Sentinel.
* [FIX] Sentinel memory leak due to hiredis fixed. (Salvatore Sanfilippo)
* [FIX] Sentinel memory leak on duplicated instance. (Charsyam)
* [FIX] Redis crash on Lua reaching output buffer limits. (Yossi Gottlieb)
* [FIX] Sentinel flushes config on +slave events. (Bill Anderson)
* [FIX] Fixes to diskless replication. (Oran Agra)
* [FIX] Redis (non clustered & clustered) replication bug involving blocking
operations: see issue #2473. (Salvatore Sanfilippo)
* [FIX] Config: missing activerehashing option support in CONFIG SET added.
(Salvatore Sanfilippo, thx to Bill Anderson)
* [FIX] AOF bug unlikely to happen in practice and mostly harmless: child
process segfaults when parent is not reachable via pipe. (Sun He)
* [FIX] Scripting engine now reports an error when misused with Lua debug
hooks, instead of crashing. (Salvatore Sanfilippo)
* [FIX] INFO loading stats: three fixes.
* [FIX] Fixed memory leaks in rdbSaveToSlavesSockets(). (Alon Diamant)
* [NEW] Redis-cli --latency-dist backported from unstable.
(Salvatore Sanfilippo)
--[ Redis 2.8.19 ] Release date: 16 Dec 2014
# UPGRADE URGENCY: LOW for both Redis and Sentinel. This release mostly
......
Copyright (c) 2006-2014, Salvatore Sanfilippo
Copyright (c) 2006-2015, Salvatore Sanfilippo
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
......
......@@ -26,6 +26,25 @@ After building Redis is a good idea to test it, using:
% make test
Fixing build problems with dependencies or cached build options
—--------
Redis has some dependencies which are included into the "deps" directory.
"make" does not rebuild dependencies automatically, even if something in the
source code of dependencies is changes.
When you update the source code with `git pull` or when code inside the
dependencies tree is modified in any other way, make sure to use the following
command in order to really clean everything and rebuild from scratch:
make distclean
This will clean: jemalloc, lua, hiredis, linenoise.
Also if you force certain build options like 32bit target, no C compiler
optimizations (for debugging purposes), and other similar build time options,
those options are cached indefinitely until you issue a "make distclean"
command.
Fixing problems building 32 bit binaries
---------
......
MSOpenTech Redis 2.8.19 Release Notes
MSOpenTech Redis 2.8.20 Release Notes
=====================================
Welcome to the binary release of Redis from Microsoft Open Technologies, Inc.
......@@ -13,10 +13,10 @@ Porting Goals
Our goal is to provide a version of Redis that runs on Windows with a performance essentially equal to the performance of Redis on an equivalent UNIX machine.
What is new with the 2.8.19 release
What is new with the 2.8.20 release
-----------------------------------
Our last official release was 2.8.12. We have merged in the changes up to 2.8.19. Please see the [release notes for the UNIX 2.8 branch](http://download.redis.io/redis-stable/00-RELEASENOTES) to understand how this impacts Redis functionality.
Our last official release was 2.8.12. We have merged in the changes up to 2.8.20. Please see the [release notes for the UNIX 2.8 branch](http://download.redis.io/redis-stable/00-RELEASENOTES) to understand how this impacts Redis functionality.
### Network layer changes
......
......@@ -487,6 +487,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
c->err = REDIS_ERR_OTHER;
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
c->reader->fn->freeObject(reply);
__redisAsyncDisconnect(ac);
return;
}
......
......@@ -3,7 +3,7 @@
<metadata>
<id>redis-64</id>
<title>redis-64</title>
<version>2.8.19</version>
<version>2.8.20</version>
<authors>Jonathan Pickett</authors>
<owners>Microsoft Open Technologies, Inc.</owners>
<summary>Redis is a very popular open-source, networked, in-memory, key-value data store known for high performance, flexibility, a rich set of data structures, and a simple straightforward API.</summary>
......@@ -14,7 +14,7 @@
<licenseUrl>https://github.com/MSOpenTech/redis/blob/2.8/license.txt</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<iconUrl>http://redis.io/images/redis.png</iconUrl>
<releaseNotes>Includes the changes from Redis 2.8.12 -> 2.8.19. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.</releaseNotes>
<releaseNotes>Includes the changes from Redis 2.8.12 -> 2.8.20. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.</releaseNotes>
</metadata>
<files>
<file src="..\signed_binaries\*.*" target=".\" />
......
......@@ -3,7 +3,7 @@
<metadata>
<id>redis-64</id>
<title>redis-64</title>
<version>2.8.19</version>
<version>2.8.20</version>
<authors>Jonathan Pickett</authors>
<owners>Microsoft Open Technologies, Inc.</owners>
<summary>Redis is a very popular open-source, networked, in-memory, key-value data store known for high performance, flexibility, a rich set of data structures, and a simple straightforward API.</summary>
......@@ -14,7 +14,7 @@
<licenseUrl>https://github.com/MSOpenTech/redis/blob/2.8/license.txt</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<iconUrl>http://redis.io/images/redis.png</iconUrl>
<releaseNotes>Includes the changes from Redis 2.8.12 -> 2.8.19. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.</releaseNotes>
<releaseNotes>Includes the changes from Redis 2.8.12 -> 2.8.20. Please see the release notes for the UNIX 2.8 branch to understand how this impacts Redis functionality.</releaseNotes>
</metadata>
<files>
<file src="..\signed_binaries\*.*" target=".\" />
......
......@@ -58,7 +58,7 @@ ifeq ($(uname_S),SunOS)
# SunOS
INSTALL=cp -pf
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt
else
ifeq ($(uname_S),Darwin)
# Darwin (nothing to do)
......@@ -251,4 +251,4 @@ install: all
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
@ln -sf $(INSTALL_BIN)/$(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
......@@ -1073,6 +1073,7 @@ int rewriteAppendOnlyFile(char *filename) {
}
}
dictReleaseIterator(di);
di = NULL;
}
/* Make sure data will not remain on the OS's output buffers */
......
......@@ -818,6 +818,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.repl_slave_ro = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"activerehashing")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.activerehashing = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
if (chdir((char*)o->ptr) == -1) {
addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
......
......@@ -247,7 +247,7 @@ sds createLatencyReport(void) {
dictEntry *de;
int eventnum = 0;
di = dictGetIterator(server.latency_events);
di = dictGetSafeIterator(server.latency_events);
while((de = dictNext(di)) != NULL) {
char *event = dictGetKey(de);
struct latencyTimeSeries *ts = dictGetVal(de);
......
......@@ -140,7 +140,7 @@ int prepareClientToWrite(redisClient *c) {
if (c->fd <= 0) return REDIS_ERR; /* Fake client */
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||
c->replstate == REDIS_REPL_ONLINE) &&
c->replstate == REDIS_REPL_ONLINE) && !c->repl_put_online_on_ack &&
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
return REDIS_OK;
......@@ -782,7 +782,7 @@ void freeClient(redisClient *c) {
* a context where calling freeClient() is not possible, because the client
* should be valid for the continuation of the flow of the program. */
void freeClientAsync(redisClient *c) {
if (c->flags & REDIS_CLOSE_ASAP) return;
if (c->flags & REDIS_CLOSE_ASAP || c->flags & REDIS_LUA_CLIENT) return;
c->flags |= REDIS_CLOSE_ASAP;
listAddNodeTail(server.clients_to_close,c);
}
......@@ -1095,7 +1095,7 @@ int processInlineBuffer(redisClient *c) {
/* Helper function. Trims query buffer to make the function that processes
* multi bulk requests idempotent. */
static void setProtocolError(redisClient *c, int pos) {
if (server.verbosity >= REDIS_VERBOSE) {
if (server.verbosity <= REDIS_VERBOSE) {
sds client = catClientInfoString(sdsempty(),c);
redisLog(REDIS_VERBOSE,
"Protocol error from client: %s", client);
......
......@@ -1137,8 +1137,9 @@ void startLoading(FILE *fp) {
/* Load the DB */
server.loading = 1;
server.loading_start_time = time(NULL);
server.loading_loaded_bytes = 0;
if (fstat(fileno(fp), &sb) == -1) {
server.loading_total_bytes = 1; /* just to avoid division by zero */
server.loading_total_bytes = 0;
} else {
server.loading_total_bytes = sb.st_size;
}
......@@ -1543,7 +1544,9 @@ int rdbSaveToSlavesSockets(void) {
{
retval = REDIS_ERR;
}
zfree(msg);
}
zfree(clientids);
exitFromChild((retval == REDIS_OK) ? 0 : 1);
} else {
#else // #ifndef _WIN32
......
......@@ -64,6 +64,7 @@
#endif
#include <limits.h>
#include <math.h>
#ifdef _WIN32
#include <fcntl.h>
......@@ -90,6 +91,8 @@
#define OUTPUT_CSV 2
#define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
#define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
#define REDIS_CLI_HISTFILE_ENV "REDISCLI_HISTFILE"
#define REDIS_CLI_HISTFILE_DEFAULT ".rediscli_history"
static redisContext *context;
static struct config {
......@@ -104,7 +107,10 @@ static struct config {
int monitor_mode;
int pubsub_mode;
int latency_mode;
int latency_dist_mode;
int latency_history;
int lru_test_mode;
long long lru_test_sample_size;
int cluster_mode;
int cluster_reissue_command;
int slave_mode;
......@@ -172,6 +178,39 @@ static void cliRefreshPrompt(void) {
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
}
static sds getHistoryPath() {
char *path = NULL;
sds historyPath = NULL;
/* check the env for a histfile override */
path = getenv(REDIS_CLI_HISTFILE_ENV);
if (path != NULL && *path != '\0') {
if (!strcmp("/dev/null", path)) {
return NULL;
}
/* if the env is set, return it */
historyPath = sdscatprintf(sdsempty(), "%s", path);
} else {
#ifdef _WIN32
char *home = getenv("USERPROFILE");
#else
char *home = getenv("HOME");
#endif
if (home != NULL && *home != '\0') {
/* otherwise, return the default */
#ifdef _WIN32
historyPath = sdscatprintf(sdsempty(), "%s\\%s", home, REDIS_CLI_HISTFILE_DEFAULT);
#else
historyPath = sdscatprintf(sdsempty(), "%s/%s", home, REDIS_CLI_HISTFILE_DEFAULT);
#endif
}
}
return historyPath;
}
/*------------------------------------------------------------------------------
* Help functions
*--------------------------------------------------------------------------- */
......@@ -527,7 +566,7 @@ static sds cliFormatReplyCSV(redisReply *r) {
out = sdscatrepr(out,r->str,r->len);
break;
case REDIS_REPLY_NIL:
out = sdscat(out,"NIL\n");
out = sdscat(out,"NIL");
break;
case REDIS_REPLY_ARRAY:
for (i = 0; i < r->elements; i++) {
......@@ -710,16 +749,17 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
return REDIS_OK;
}
/* Send the INFO command, reconnecting the link if needed. */
static redisReply *reconnectingInfo(void) {
redisContext *c = context;
/* Send a command reconnecting the link if needed. */
static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {
redisReply *reply = NULL;
int tries = 0;
va_list ap;
assert(!c->err);
while(reply == NULL) {
while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {
printf("Reconnecting (%d)...\r", ++tries);
printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
printf("Reconnecting... %d\r", ++tries);
fflush(stdout);
redisFree(c);
......@@ -727,12 +767,15 @@ static redisReply *reconnectingInfo(void) {
usleep(1000000);
}
reply = redisCommand(c,"INFO");
va_start(ap,fmt);
reply = redisvCommand(c,fmt,ap);
va_end(ap);
if (c->err && !(c->err & (REDIS_ERR_IO | REDIS_ERR_EOF))) {
fprintf(stderr, "Error: %s\n", c->errstr);
exit(1);
} else if (tries > 0) {
printf("\n");
printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
}
}
......@@ -780,9 +823,14 @@ static int parseOptions(int argc, char **argv) {
config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) {
config.latency_mode = 1;
} else if (!strcmp(argv[i],"--latency-dist")) {
config.latency_dist_mode = 1;
} else if (!strcmp(argv[i],"--latency-history")) {
config.latency_mode = 1;
config.latency_history = 1;
} else if (!strcmp(argv[i],"--lru-test") && !lastarg) {
config.lru_test_mode = 1;
config.lru_test_sample_size = strtoll(argv[++i],NULL,10);
} else if (!strcmp(argv[i],"--slave")) {
config.slave_mode = 1;
} else if (!strcmp(argv[i],"--stat")) {
......@@ -872,6 +920,9 @@ static void usage(void) {
" --latency Enter a special mode continuously sampling latency.\n"
" --latency-history Like --latency but tracking latency changes over time.\n"
" Default time interval is 15 sec. Change it using -i.\n"
" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n"
" Default time interval is 1 sec. Change it using -i.\n"
" --lru-test <keys> Simulate a cache workload with an 80-20 distribution.\n"
" --slave Simulate a slave showing commands received from the master.\n"
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
" --pipe Transfer raw Redis protocol from stdin to server.\n"
......@@ -929,19 +980,11 @@ static void repl(void) {
/* Only use history when stdin is a tty. */
if (isatty(fileno(stdin))) {
history = 1;
#ifdef _WIN32
if (getenv("USERPROFILE") != NULL) {
historyfile = sdscatprintf(sdsempty(),"%s\\.rediscli_history",getenv("USERPROFILE"));
linenoiseHistoryLoad(historyfile);
}
#else
if (getenv("HOME") != NULL) {
historyfile = sdscatprintf(sdsempty(),"%s/.rediscli_history",getenv("HOME"));
historyfile = getHistoryPath();
if (historyfile != NULL) {
history = 1;
linenoiseHistoryLoad(historyfile);
}
#endif
}
cliRefreshPrompt();
......@@ -1087,7 +1130,7 @@ static void latencyMode(void) {
if (!context) exit(1);
while(1) {
start = mstime();
reply = redisCommand(context,"PING");
reply = reconnectingRedisCommand(context,"PING");
if (reply == NULL) {
fprintf(stderr,"\nI/O error\n");
exit(1);
......@@ -1117,6 +1160,155 @@ static void latencyMode(void) {
}
}
/*------------------------------------------------------------------------------
* Latency distribution mode -- requires 256 colors xterm
*--------------------------------------------------------------------------- */
#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
#define LATENCY_DIST_MIN_GRAY 233 /* Less than that is too hard to see gray. */
#define LATENCY_DIST_MAX_GRAY 255
#define LATENCY_DIST_GRAYS (LATENCY_DIST_MAX_GRAY-LATENCY_DIST_MIN_GRAY+1)
/* Gray palette. */
int spectrum_palette_size = 24;
int spectrum_palette[] = {0, 233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
/* Structure to store samples distribution. */
struct distsamples {
long long max; /* Max latency to fit into this interval (usec). */
long long count; /* Number of samples in this interval. */
int character; /* Associated character in visualization. */
};
/* Helper function for latencyDistMode(). Performs the spectrum visualization
* of the collected samples targeting an xterm 256 terminal.
*
* Takes an array of distsamples structures, ordered from smaller to bigger
* 'max' value. Last sample max must be 0, to mean that it olds all the
* samples greater than the previous one, and is also the stop sentinel.
*
* "tot' is the total number of samples in the different buckets, so it
* is the SUM(samples[i].conut) for i to 0 up to the max sample.
*
* As a side effect the function sets all the buckets count to 0. */
void showLatencyDistSamples(struct distsamples *samples, long long tot) {
int j;
/* We convert samples into a index inside the palette
* proportional to the percentage a given bucket represents.
* This way intensity of the different parts of the spectrum
* don't change relative to the number of requests, which avoids to
* pollute the visualization with non-latency related info. */
printf("\033[38;5;0m"); /* Set foreground color to black. */
for (j = 0; ; j++) {
int coloridx =
ceil((float) samples[j].count / tot * (spectrum_palette_size-1));
int color = spectrum_palette[coloridx];
printf("\033[48;5;%dm%c", (int)color, samples[j].character);
samples[j].count = 0;
if (samples[j].max == 0) break; /* Last sample. */
}
printf("\033[0m\n");
fflush(stdout);
}
/* Show the legend: different buckets values and colors meaning, so
* that the spectrum is more easily readable. */
void showLatencyDistLegend(void) {
int j;
printf("---------------------------------------------\n");
printf(". - * # .01 .125 .25 .5 milliseconds\n");
printf("1,2,3,...,9 from 1 to 9 milliseconds\n");
printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n");
printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n");
printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n");
printf("From 0 to 100%%: ");
for (j = 0; j < spectrum_palette_size; j++) {
printf("\033[48;5;%dm ", spectrum_palette[j]);
}
printf("\033[0m\n");
printf("---------------------------------------------\n");
}
static void latencyDistMode(void) {
redisReply *reply;
long long start, latency, count = 0;
long long history_interval =
config.interval ? config.interval/1000 :
LATENCY_DIST_DEFAULT_INTERVAL;
long long history_start = ustime();
int j, outputs = 0;
struct distsamples samples[] = {
/* We use a mostly logarithmic scale, with certain linear intervals
* which are more interesting than others, like 1-10 milliseconds
* range. */
{10,0,'.'}, /* 0.01 ms */
{125,0,'-'}, /* 0.125 ms */
{250,0,'*'}, /* 0.25 ms */
{500,0,'#'}, /* 0.5 ms */
{1000,0,'1'}, /* 1 ms */
{2000,0,'2'}, /* 2 ms */
{3000,0,'3'}, /* 3 ms */
{4000,0,'4'}, /* 4 ms */
{5000,0,'5'}, /* 5 ms */
{6000,0,'6'}, /* 6 ms */
{7000,0,'7'}, /* 7 ms */
{8000,0,'8'}, /* 8 ms */
{9000,0,'9'}, /* 9 ms */
{10000,0,'A'}, /* 10 ms */
{20000,0,'B'}, /* 20 ms */
{30000,0,'C'}, /* 30 ms */
{40000,0,'D'}, /* 40 ms */
{50000,0,'E'}, /* 50 ms */
{100000,0,'F'}, /* 0.1 s */
{200000,0,'G'}, /* 0.2 s */
{300000,0,'H'}, /* 0.3 s */
{400000,0,'I'}, /* 0.4 s */
{500000,0,'J'}, /* 0.5 s */
{1000000,0,'K'}, /* 1 s */
{2000000,0,'L'}, /* 2 s */
{4000000,0,'M'}, /* 4 s */
{8000000,0,'N'}, /* 8 s */
{16000000,0,'O'}, /* 16 s */
{30000000,0,'P'}, /* 30 s */
{60000000,0,'Q'}, /* 1 minute */
{0,0,'?'}, /* > 1 minute */
};
if (!context) exit(1);
while(1) {
start = ustime();
reply = reconnectingRedisCommand(context,"PING");
if (reply == NULL) {
fprintf(stderr,"\nI/O error\n");
exit(1);
}
latency = ustime()-start;
freeReplyObject(reply);
count++;
/* Populate the relevant bucket. */
for (j = 0; ; j++) {
if (samples[j].max == 0 || latency <= samples[j].max) {
samples[j].count++;
break;
}
}
/* From time to time show the spectrum. */
if (count && (ustime()-history_start)/1000 > history_interval) {
if ((outputs++ % 20) == 0)
showLatencyDistLegend();
showLatencyDistSamples(samples,count);
history_start = ustime();
count = 0;
}
usleep(LATENCY_SAMPLE_RATE * 1000);
}
}
/*------------------------------------------------------------------------------
* Slave mode
*--------------------------------------------------------------------------- */
......@@ -1740,7 +1932,7 @@ static void statMode(void) {
char buf[64];
int j;
reply = reconnectingInfo();
reply = reconnectingRedisCommand(context,"INFO");
if (reply->type == REDIS_REPLY_ERROR) {
printf("ERROR: %s\n", reply->str);
exit(1);
......@@ -1846,6 +2038,94 @@ static void scanMode(void) {
exit(0);
}
/*------------------------------------------------------------------------------
* LRU test mode
*--------------------------------------------------------------------------- */
/* Return an integer from min to max (both inclusive) using a power-law
* distribution, depending on the value of alpha: the greater the alpha
* the more bias towards lower values.
*
* With alpha = 6.2 the output follows the 80-20 rule where 20% of
* the returned numbers will account for 80% of the frequency. */
long long powerLawRand(long long min, long long max, double alpha) {
double pl, r;
max += 1;
r = ((double)rand()) / RAND_MAX;
pl = pow(
((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),
(1.0/(alpha+1)));
return (max-1-(long long)pl)+min;
}
/* Generates a key name among a set of lru_test_sample_size keys, using
* an 80-20 distribution. */
void LRUTestGenKey(char *buf, size_t buflen) {
snprintf(buf, buflen, "lru:%lld\n",
powerLawRand(1, config.lru_test_sample_size, 6.2));
}
#define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */
#define LRU_CYCLE_PIPELINE_SIZE 250
static void LRUTestMode(void) {
redisReply *reply;
char key[128];
long long start_cycle;
int j;
srand(time(NULL)^getpid());
while(1) {
/* Perform cycles of 1 second with 50% writes and 50% reads.
* We use pipelining batching writes / reads N times per cycle in order
* to fill the target instance easily. */
start_cycle = mstime();
long long hits = 0, misses = 0;
while(mstime() - start_cycle < 1000) {
/* Write cycle. */
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
LRUTestGenKey(key,sizeof(key));
redisAppendCommand(context, "SET %s val",key);
}
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)
redisGetReply(context, (void**)&reply);
/* Read cycle. */
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
LRUTestGenKey(key,sizeof(key));
redisAppendCommand(context, "GET %s",key);
}
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
if (redisGetReply(context, (void**)&reply) == REDIS_OK) {
switch(reply->type) {
case REDIS_REPLY_ERROR:
printf("%s\n", reply->str);
break;
case REDIS_REPLY_NIL:
misses++;
break;
default:
hits++;
break;
}
}
}
if (context->err) {
fprintf(stderr,"I/O error during LRU test\n");
exit(1);
}
}
/* Print stats. */
printf(
"%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
hits+misses,
hits, (double)hits/(hits+misses)*100,
misses, (double)misses/(hits+misses)*100);
}
exit(0);
}
/*------------------------------------------------------------------------------
* Intrisic latency mode.
*
......@@ -1937,7 +2217,10 @@ int main(int argc, char **argv) {
config.monitor_mode = 0;
config.pubsub_mode = 0;
config.latency_mode = 0;
config.latency_dist_mode = 0;
config.latency_history = 0;
config.lru_test_mode = 0;
config.lru_test_sample_size = 0;
config.cluster_mode = 0;
config.slave_mode = 0;
config.getrdb_mode = 0;
......@@ -1971,6 +2254,12 @@ int main(int argc, char **argv) {
latencyMode();
}
/* Latency distribution mode */
if (config.latency_dist_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
latencyDistMode();
}
/* Slave mode */
if (config.slave_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
......@@ -2008,6 +2297,12 @@ int main(int argc, char **argv) {
scanMode();
}
/* LRU test mode */
if (config.lru_test_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
LRUTestMode();
}
/* Intrinsic latency mode */
if (config.intrinsic_latency_mode) intrinsicLatencyMode();
......
......@@ -1540,33 +1540,33 @@ void adjustOpenFilesLimit(void) {
/* Set the max number of files if the current limit is not enough
* for our needs. */
if (oldlimit < maxfiles) {
rlim_t f;
rlim_t bestlimit;
int setrlimit_error = 0;
/* Try to set the file limit to match 'maxfiles' or at least
* to the higher value supported less than maxfiles. */
f = maxfiles;
while(f > oldlimit) {
bestlimit = maxfiles;
while(bestlimit > oldlimit) {
rlim_t decr_step = 16;
limit.rlim_cur = f;
limit.rlim_max = f;
limit.rlim_cur = bestlimit;
limit.rlim_max = bestlimit;
if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
setrlimit_error = errno;
/* We failed to set file limit to 'f'. Try with a
/* We failed to set file limit to 'bestlimit'. Try with a
* smaller limit decrementing by a few FDs per iteration. */
if (f < decr_step) break;
f -= decr_step;
if (bestlimit < decr_step) break;
bestlimit -= decr_step;
}
/* Assume that the limit we get initially is still valid if
* our last try was even lower. */
if (f < oldlimit) f = oldlimit;
if (bestlimit < oldlimit) bestlimit = oldlimit;
if (f != maxfiles) {
if (bestlimit < maxfiles) {
int old_maxclients = server.maxclients;
server.maxclients = f-REDIS_MIN_RESERVED_FDS;
server.maxclients = bestlimit-REDIS_MIN_RESERVED_FDS;
if (server.maxclients < 1) {
redisLog(REDIS_WARNING,"Your current 'ulimit -n' "
"of %llu is not enough for Redis to start. "
......@@ -1587,7 +1587,7 @@ void adjustOpenFilesLimit(void) {
"maxclients has been reduced to %d to compensate for "
"low ulimit. "
"If you need higher maxclients increase 'ulimit -n'.",
(unsigned long long) oldlimit, server.maxclients);
(unsigned long long) bestlimit, server.maxclients);
} else {
redisLog(REDIS_NOTICE,"Increased maximum number of open files "
"to %llu (it was originally set to %llu).",
......@@ -2829,14 +2829,14 @@ sds genRedisInfoString(char *section) {
#endif
perc = ((double)server.loading_loaded_bytes /
server.loading_total_bytes) * 100;
(server.loading_total_bytes+1)) * 100;
elapsed = server.unixtime-server.loading_start_time;
elapsed = time(NULL)-server.loading_start_time;
if (elapsed == 0) {
eta = 1; /* A fake 1 second figure if we don't have
enough info */
} else {
eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes;
eta = (elapsed*remaining_bytes)/(server.loading_loaded_bytes+1);
}
info = sdscatprintf(info,
......
......@@ -32,10 +32,7 @@
#include "fmacros.h"
#include "config.h"
#if defined(__sun)
#include "solarisfixes.h"
#endif
#include <stdio.h>
#include <stdlib.h>
......
......@@ -857,6 +857,7 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
* is technically online now. */
slave->replstate = REDIS_REPL_ONLINE;
slave->repl_put_online_on_ack = 1;
slave->repl_ack_time = server.unixtime;
} else {
if (bgsaveerr != REDIS_OK) {
freeClient(slave);
......@@ -1606,12 +1607,38 @@ int cancelReplicationHandshake(void) {
return 1;
}
/* Mass-unblock clients because something changed in the instance that makes
* blocking no longer safe. For example clients blocked in list operations
* in an instance which turns from master to slave is unsafe, so this function
* is called when a master turns into a slave.
*
* The semantics is to send an -UNBLOCKED error to the client, disconnecting
* it at the same time. */
void disconnectAllBlockedClients(void) {
listNode *ln;
listIter li;
listRewind(server.clients,&li);
while((ln = listNext(&li))) {
redisClient *c = listNodeValue(ln);
if (c->flags & REDIS_BLOCKED) {
addReplySds(c,sdsnew(
"-UNBLOCKED force unblock from blocking operation, "
"instance state changed (master -> slave?)\r\n"));
unblockClientWaitingData(c);
c->flags |= REDIS_CLOSE_AFTER_REPLY;
}
}
}
/* Set replication to the specified master address and port. */
void replicationSetMaster(char *ip, int port) {
sdsfree(server.masterhost);
server.masterhost = sdsdup(ip);
server.masterport = port;
if (server.master) freeClient(server.master);
disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */
disconnectSlaves(); /* Force our slaves to resync with us as well. */
replicationDiscardCachedMaster(); /* Don't try a PSYNC. */
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
......
......@@ -213,11 +213,27 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
static int argv_size = 0;
static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE];
static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
static int inuse = 0; /* Recursive calls detection. */
/* By using Lua debug hooks it is possible to trigger a recursive call
* to luaRedisGenericCommand(), which normally should never happen.
* To make this function reentrant is futile and makes it slower, but
* we should at least detect such a misuse, and abort. */
if (inuse) {
char *recursion_warning =
"luaRedisGenericCommand() recursive call detected. "
"Are you doing funny stuff with Lua debug hooks?";
redisLog(REDIS_WARNING,"%s",recursion_warning);
luaPushError(lua,recursion_warning);
return 1;
}
inuse++;
/* Require at least one argument */
if (argc == 0) {
luaPushError(lua,
"Please specify at least one argument for redis.call()");
inuse--;
return 1;
}
......@@ -272,6 +288,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
luaPushError(lua,
"Lua redis() command arguments must be strings or integers");
inuse--;
return 1;
}
......@@ -408,8 +425,10 @@ cleanup:
* return the plain error. */
lua_pushstring(lua,"err");
lua_gettable(lua,-2);
inuse--;
return lua_error(lua);
}
inuse--;
return 1;
}
......
......@@ -74,7 +74,7 @@ sds sdsempty(void) {
return sdsnewlen("",0);
}
/* Create a new sds string starting from a null termined C string. */
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
......@@ -573,7 +573,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
* Example:
*
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
* s = sdstrim(s,"A. :");
* s = sdstrim(s,"Aa. :");
* printf("%s\n", s);
*
* Output will be just "Hello World".
......@@ -1103,6 +1103,7 @@ int main(void) {
int oldfree;
sdsfree(x);
sdsfree(y);
x = sdsnew("0");
sh = (void*) (x-(sizeof(struct sdshdr)));
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
......@@ -1115,6 +1116,8 @@ int main(void) {
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
test_cond("sdsIncrLen() -- len", sh->len == 2);
test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
sdsfree(x);
}
}
test_report()
......
......@@ -1067,6 +1067,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
else if (flags & SRI_SENTINEL) table = master->sentinels;
sdsname = sdsnew(name);
if (dictFind(table,sdsname)) {
releaseSentinelAddr(addr);
sdsfree(sdsname);
errno = EBUSY;
return NULL;
......@@ -1413,10 +1414,7 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip,
slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip,
slaves[j]->port, master->quorum, master);
releaseSentinelAddr(slaves[j]);
if (slave) {
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
if (slave) sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
}
zfree(slaves);
......@@ -1990,6 +1988,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
atoi(port), ri->quorum, ri)) != NULL)
{
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
}
}
......@@ -2761,6 +2760,31 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,
return ri;
}
#define SENTINEL_ISQR_OK 0
#define SENTINEL_ISQR_NOQUORUM (1<<0)
#define SENTINEL_ISQR_NOAUTH (1<<1)
int sentinelIsQuorumReachable(sentinelRedisInstance *master, int *usableptr) {
dictIterator *di;
dictEntry *de;
int usable = 1; /* Number of usable Sentinels. Init to 1 to count myself. */
int result = SENTINEL_ISQR_OK;
int voters = dictSize(master->sentinels)+1; /* Known Sentinels + myself. */
di = dictGetIterator(master->sentinels);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
if (ri->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;
usable++;
}
dictReleaseIterator(di);
if (usable < (int)master->quorum) result |= SENTINEL_ISQR_NOQUORUM;
if (usable < voters/2+1) result |= SENTINEL_ISQR_NOAUTH;
if (usableptr) *usableptr = usable;
return result;
}
void sentinelCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"masters")) {
/* SENTINEL MASTERS */
......@@ -2911,6 +2935,10 @@ void sentinelCommand(redisClient *c) {
sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum);
addReply(c,shared.ok);
}
} else if (!strcasecmp(c->argv[1]->ptr,"flushconfig")) {
sentinelFlushConfig();
addReply(c,shared.ok);
return;
} else if (!strcasecmp(c->argv[1]->ptr,"remove")) {
/* SENTINEL REMOVE <name> */
sentinelRedisInstance *ri;
......@@ -2921,6 +2949,32 @@ void sentinelCommand(redisClient *c) {
dictDelete(sentinel.masters,c->argv[2]->ptr);
sentinelFlushConfig();
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"ckquorum")) {
/* SENTINEL CKQUORUM <name> */
sentinelRedisInstance *ri;
int usable;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
int result = sentinelIsQuorumReachable(ri,&usable);
if (result == SENTINEL_ISQR_OK) {
addReplySds(c, sdscatfmt(sdsempty(),
"+OK %i usable Sentinels. Quorum and failover authorization "
"can be reached\r\n",usable));
} else {
sds e = sdscatfmt(sdsempty(),
"-NOQUORUM %i usable Sentinels. ",usable);
if (result & SENTINEL_ISQR_NOQUORUM)
e = sdscat(e,"Not enough available Sentinels to reach the"
" specified quorum for this master");
if (result & SENTINEL_ISQR_NOAUTH) {
if (result & SENTINEL_ISQR_NOQUORUM) e = sdscat(e,". ");
e = sdscat(e, "Not enough available Sentinels to reach the"
" majority and authorize a failover");
}
e = sdscat(e,"\r\n");
addReplySds(c,e);
}
} else if (!strcasecmp(c->argv[1]->ptr,"set")) {
if (c->argc < 3 || c->argc % 2 == 0) goto numargserr;
sentinelSetCommand(c);
......
......@@ -23,10 +23,8 @@ A million repetitions of "a"
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* for u_int*_t */
#if defined(__sun)
#include <stdint.h>
#include "solarisfixes.h"
#endif
#ifdef _WIN32
#include "win32_Interop/win32fixes.h"
#endif
......@@ -62,12 +60,12 @@ A million repetitions of "a"
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
u_int32_t a, b, c, d, e;
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
......@@ -137,9 +135,9 @@ void SHA1Init(SHA1_CTX* context)
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
u_int32_t i, j;
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
......@@ -177,7 +175,7 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
for (i = 0; i < 2; i++)
{
u_int32_t t = context->count[i];
uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
......
......@@ -6,12 +6,12 @@ By Steve Reid <steve@edmweb.com>
*/
typedef struct {
u_int32_t state[5];
u_int32_t count[2];
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
......@@ -28,6 +28,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(__sun)
#if defined(__GNUC__)
#include <math.h>
#undef isnan
......@@ -48,3 +50,5 @@
#define u_int uint
#define u_int32_t uint32_t
#endif /* __GNUC__ */
#endif /* __sun */
......@@ -321,7 +321,7 @@ void smoveCommand(redisClient *c) {
/* If srcset and dstset are equal, SMOVE is a no-op */
if (srcset == dstset) {
addReply(c,shared.cone);
addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero);
return;
}
......
#define REDIS_VERSION "2.8.19"
#define REDIS_VERSION "2.8.20"
start_server {tags {"repl"}} {
set A [srv 0 client]
set A_host [srv 0 host]
set A_port [srv 0 port]
start_server {} {
test {First server should have role slave after SLAVEOF} {
r -1 slaveof [srv 0 host] [srv 0 port]
set B [srv 0 client]
set B_host [srv 0 host]
set B_port [srv 0 port]
test {Set instance A as slave of B} {
$A slaveof $B_host $B_port
wait_for_condition 50 100 {
[s -1 role] eq {slave} &&
[string match {*master_link_status:up*} [r -1 info replication]]
[lindex [$A role] 0] eq {slave} &&
[string match {*master_link_status:up*} [$A info replication]]
} else {
fail "Can't turn the instance into a slave"
}
......@@ -15,9 +22,9 @@ start_server {tags {"repl"}} {
$rd brpoplpush a b 5
r lpush a foo
wait_for_condition 50 100 {
[r debug digest] eq [r -1 debug digest]
[$A debug digest] eq [$B debug digest]
} else {
fail "Master and slave have different digest: [r debug digest] VS [r -1 debug digest]"
fail "Master and slave have different digest: [$A debug digest] VS [$B debug digest]"
}
}
......@@ -28,7 +35,36 @@ start_server {tags {"repl"}} {
r lpush c 3
$rd brpoplpush c d 5
after 1000
assert_equal [r debug digest] [r -1 debug digest]
assert_equal [$A debug digest] [$B debug digest]
}
test {BLPOP followed by role change, issue #2473} {
set rd [redis_deferring_client]
$rd blpop foo 0 ; # Block while B is a master
# Turn B into master of A
$A slaveof no one
$B slaveof $A_host $A_port
wait_for_condition 50 100 {
[lindex [$B role] 0] eq {slave} &&
[string match {*master_link_status:up*} [$B info replication]]
} else {
fail "Can't turn the instance into a slave"
}
# Push elements into the "foo" list of the new slave.
# If the client is still attached to the instance, we'll get
# a desync between the two instances.
$A rpush foo a b c
after 100
wait_for_condition 50 100 {
[$A debug digest] eq [$B debug digest] &&
[$A lrange foo 0 -1] eq {a b c} &&
[$B lrange foo 0 -1] eq {a b c}
} else {
fail "Master and slave have different digest: [$A debug digest] VS [$B debug digest]"
}
}
}
}
......@@ -113,7 +149,7 @@ foreach dl {no yes} {
start_server {} {
lappend slaves [srv 0 client]
test "Connect multiple slaves at the same time (issue #141), diskless=$dl" {
# Send SALVEOF commands to slaves
# Send SLAVEOF commands to slaves
[lindex $slaves 0] slaveof $master_host $master_port
[lindex $slaves 1] slaveof $master_host $master_port
[lindex $slaves 2] slaveof $master_host $master_port
......
# Test for the SENTINEL CKQUORUM command
source "../tests/includes/init-tests.tcl"
set num_sentinels [llength $::sentinel_instances]
test "CKQUORUM reports OK and the right amount of Sentinels" {
foreach_sentinel_id id {
assert_match "*OK $num_sentinels usable*" [S $id SENTINEL CKQUORUM mymaster]
}
}
test "CKQUORUM detects quorum cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum [expr {$num_sentinels+1}]
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
}
test "CKQUORUM detects failover authorization cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum 1
kill_instance sentinel 1
kill_instance sentinel 2
kill_instance sentinel 3
after 5000
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
restart_instance sentinel 1
restart_instance sentinel 2
restart_instance sentinel 3
}
proc test_memory_efficiency {range} {
r flushall
set rd [redis_deferring_client]
set base_mem [s used_memory]
set written 0
for {set j 0} {$j < 10000} {incr j} {
set key key:$j
set val [string repeat A [expr {int(rand()*$range)}]]
r set $key $val
$rd set $key $val
incr written [string length $key]
incr written [string length $val]
incr written 2 ;# A separator is the minimum to store key-value data.
}
for {set j 0} {$j < 10000} {incr j} {
$rd read ; # Discard replies
}
set current_mem [s used_memory]
set used [expr {$current_mem-$base_mem}]
set efficiency [expr {double($written)/$used}]
......
......@@ -450,6 +450,7 @@ start_server {
test "SMOVE non existing key" {
setup_move
assert_equal 0 [r smove myset1 myset2 foo]
assert_equal 0 [r smove myset1 myset1 foo]
assert_equal {1 a b} [lsort [r smembers myset1]]
assert_equal {2 3 4} [lsort [r smembers myset2]]
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册