提交 915c0dd6 编写于 作者: J jonathan pickett

Merge changes from Posix Redis through 2.8.15 into MSOpenTech branch

......@@ -14,6 +14,30 @@ 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.15 ] Release date: 12 Sep 2014
# UPGRADE URGENCY: LOW for Redis, HIGH for Sentinel.
* [FIX] Sentinel critical bug fixed: the absolute majority was computed in a
wrong way because of a programming error. Now the implementation does
what the specification says and the majority to authorize a failover
(that should not be confused with the ODOWN quorum) is the majority of
*all* the Sentinels ever seen for a given master, regardless of their
current state.
* [FIX] GETRANGE test no longer fails for 32 bit builds (Matt Stancliff).
* [FIX] Limit SCAN latency when the hash table is in an odd state (very few
populted buckets because rehashing is in progress). (Xiaost and
Salvatore Sanfilippo)
* [NEW] Redis is now able to load truncated AOF files without requiring a
redis-check-aof utility run. The default now is to load truncated
(but apparently not corrupted) AOFs, you can change this in redis.conf.
(Salvatore Sanfilippo).
* [NEW] Sentinel: ability to announce itself with an arbitrary IP/port to work
in the context of natted networks. However this is probably still
not enough since there is no equivalent mechanism for slaves listed
in the master INFO output. (Dara Kong and Salvatore Sanfilippo)
--[ Redis 2.8.14 ] Release date: 1 Sep 2014
# UPGRADE URGENCY: HIGH for Lua scripting users, the server could crash because
......
......@@ -7,7 +7,7 @@ $CurDir = [System.IO.Directory]::GetCurrentDirectory()
$PubDir = [System.IO.Path]::Combine($CurDir, "x64\Release\pub" )
$SourceDir = [System.IO.Path]::Combine($CurDir, "x64\Release" )
$DocumentationDir = [System.IO.Path]::Combine($CurDir, "setups\documentation" )
$Destination = [System.IO.Path]::Combine($CurDir, "..\bin\Release\redis-2.8.14.zip" )
$Destination = [System.IO.Path]::Combine($CurDir, "..\bin\Release\redis-2.8.15.zip" )
[System.IO.Directory]::CreateDirectory($PubDir) | Out-Null
......
......@@ -2,7 +2,7 @@
$CurDir = split-path -parent $MyInvocation.MyCommand.Definition
$SourceZip = [System.IO.Path]::Combine($CurDir, "..\..\bin\Release\redis-2.8.12.zip" )
$SourceZip = [System.IO.Path]::Combine($CurDir, "..\..\bin\Release\redis-2.8.15.zip" )
$Destination = [System.IO.Path]::Combine($CurDir, "signed_binaries" )
[System.IO.Directory]::CreateDirectory($Destination) | Out-Null
......
......@@ -3,7 +3,7 @@
<metadata>
<id>redis-64</id>
<title>redis-64</title>
<version>2.8.14</version>
<version>2.8.15</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.14. 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.15. 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.14</version>
<version>2.8.15</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.9 -> 2.8.14. 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.15. 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=".\" />
......
......@@ -529,6 +529,30 @@ no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# An AOF file may be found to be truncated at the end during the Redis
# startup process, when the AOF data gets loaded back into memory.
# This may happen when the system where Redis is running
# crashes, especially when an ext4 filesystem is mounted without the
# data=ordered option (however this can't happen when Redis itself
# crashes or aborts but the operating system still works correctly).
#
# Redis can either exit with an error when this happens, or load as much
# data as possible (the default now) and start if the AOF file is found
# to be truncated at the end. The following option controls this behavior.
#
# If aof-load-truncated is set to yes, a truncated AOF file is loaded and
# the Redis server starts emitting a log to inform the user of the event.
# Otherwise if the option is set to no, the server aborts with an error
# and refuses to start. When the option is set to no, the user requires
# to fix the AOF file using the "redis-check-aof" utility before to restart
# the server.
#
# Note that if the AOF file will be found to be corrupted in the middle
# the server will still exit with an error. This option only applies when
# Redis will try to read more data from the AOF file but not enough bytes
# will be found.
aof-load-truncated yes
################################ LUA SCRIPTING ###############################
# Max execution time of a Lua script in milliseconds.
......
......@@ -4,6 +4,28 @@
# The port that this sentinel instance will run on
port 26379
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4
# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
......
......@@ -425,6 +425,7 @@ static RedisParamterMapper g_redisArgMap =
{ "client-output-buffer-limit", &fp4 }, // client-output-buffer-limit [class] [hard limit] [soft limit] [soft seconds]
{ "hz", &fp1 }, // hz [number]
{ "aof-rewrite-incremental-fsync", &fp1 }, // aof-rewrite-incremental-fsync [yes/no]
{ "aof-load-truncated", &fp1 }, // aof-load-truncated [yes/no]
{ "latency-monitor-threshold", &fp1 }, // latency-monitor-threshold [number]
{ cInclude, &fp1 }, // include [path]
......
......@@ -550,6 +550,14 @@ struct redisClient *createFakeClient(void) {
return c;
}
void freeFakeClientArgv(struct redisClient *c) {
int j;
for (j = 0; j < c->argc; j++)
decrRefCount(c->argv[j]);
zfree(c->argv);
}
void freeFakeClient(struct redisClient *c) {
sdsfree(c->querybuf);
listRelease(c->reply);
......@@ -615,18 +623,35 @@ int loadAppendOnlyFile(char *filename) {
goto readerr;
}
if (buf[0] != '*') goto fmterr;
if (buf[1] == '\0') goto readerr;
argc = atoi(buf+1);
if (argc < 1) goto fmterr;
argv = zmalloc(sizeof(robj*)*argc);
fakeClient->argc = argc;
fakeClient->argv = argv;
for (j = 0; j < argc; j++) {
if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr;
if (fgets(buf,sizeof(buf),fp) == NULL) {
fakeClient->argc = j; /* Free up to j-1. */
freeFakeClientArgv(fakeClient);
goto readerr;
}
if (buf[0] != '$') goto fmterr;
len = strtol(buf+1,NULL,10);
argsds = sdsnewlen(NULL,len);
if (len && fread(argsds,len,1,fp) == 0) goto fmterr;
if (len && fread(argsds,len,1,fp) == 0) {
sdsfree(argsds);
fakeClient->argc = j; /* Free up to j-1. */
freeFakeClientArgv(fakeClient);
goto readerr;
}
argv[j] = createObject(REDIS_STRING,argsds);
if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */
if (fread(buf,2,1,fp) == 0) {
fakeClient->argc = j+1; /* Free up to j. */
freeFakeClientArgv(fakeClient);
goto readerr; /* discard CRLF */
}
}
/* Command lookup */
......@@ -635,9 +660,8 @@ int loadAppendOnlyFile(char *filename) {
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr);
exit(1);
}
/* Run the command in the context of a fake client */
fakeClient->argc = argc;
fakeClient->argv = argv;
cmd->proc(fakeClient);
/* The fake client should not have a reply */
......@@ -647,15 +671,14 @@ int loadAppendOnlyFile(char *filename) {
/* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */
for (j = 0; j < fakeClient->argc; j++)
decrRefCount(fakeClient->argv[j]);
zfree(fakeClient->argv);
freeFakeClientArgv(fakeClient);
}
/* This point can only be reached when EOF is reached without errors.
* If the client is in the middle of a MULTI/EXEC, log error and quit. */
if (fakeClient->flags & REDIS_MULTI) goto readerr;
if (fakeClient->flags & REDIS_MULTI) goto uxeof;
loaded_ok: /* DB loaded, cleanup and return REDIS_OK to the caller. */
fclose(fp);
freeFakeClient(fakeClient);
server.aof_state = old_aof_state;
......@@ -664,14 +687,23 @@ int loadAppendOnlyFile(char *filename) {
server.aof_rewrite_base_size = server.aof_current_size;
return REDIS_OK;
readerr:
if (feof(fp)) {
redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file");
} else {
readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */
if (!feof(fp)) {
redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno));
exit(1);
}
uxeof: /* Unexpected AOF end of file. */
if (server.aof_load_truncated) {
redisLog(REDIS_WARNING,"!!! Warning: short read while loading the AOF file !!!");
redisLog(REDIS_WARNING,
"AOF loaded anyway because aof-load-truncated is enabled");
goto loaded_ok;
}
redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.");
exit(1);
fmterr:
fmterr: /* Format error. */
redisLog(REDIS_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>");
exit(1);
}
......@@ -1158,7 +1190,7 @@ void aofRemoveTempFile(pid_t childpid) {
unlink(tmpfile);
}
/* Update the server.aof_current_size filed explicitly using stat(2)
/* Update the server.aof_current_size field explicitly using stat(2)
* to check the size of the file. This is useful after a rewrite or after
* a restart, normally the size is updated just adding the write length
* to the current length, that is much faster. */
......
......@@ -395,7 +395,12 @@ void loadServerConfigFromString(char *config) {
} else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
argc == 2)
{
if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {
if ((server.aof_rewrite_incremental_fsync =
yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"aof-load-truncated") && argc == 2) {
if ((server.aof_load_truncated = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
......@@ -750,6 +755,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.aof_rewrite_incremental_fsync = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"aof-load-truncated")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.aof_load_truncated = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"save")) {
int vlen, j;
sds *v = sdssplitlen(o->ptr,(int)sdslen(o->ptr)," ",1,&vlen);
......@@ -1070,6 +1080,8 @@ void configGetCommand(redisClient *c) {
server.repl_disable_tcp_nodelay);
config_get_bool_field("aof-rewrite-incremental-fsync",
server.aof_rewrite_incremental_fsync);
config_get_bool_field("aof-load-truncated",
server.aof_load_truncated);
/* Everything we can't handle with macros follows. */
......@@ -1440,7 +1452,7 @@ void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, c
return;
}
/* Compare the strings as sds strings to have a binary safe comparison. */
/* Set force to zero if the value is set to its default. */
if (defvalue && strcmp(value,defvalue) == 0) force = 0;
line = sdsnew(option);
......@@ -1840,6 +1852,7 @@ int rewriteConfig(char *path) {
rewriteConfigClientoutputbufferlimitOption(state);
rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ);
rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,REDIS_DEFAULT_AOF_LOAD_TRUNCATED);
if (server.sentinel_mode) rewriteConfigSentinelOption(state);
/* Step 3: remove all the orphaned lines in the old file, that is, lines
......
......@@ -487,6 +487,11 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
if (ht) {
void *privdata[2];
/* We set the max number of iterations to ten times the specified
* COUNT, so if the hash table is in a pathological state (very
* sparsely populated) we avoid to block too much time at the cost
* of returning no or very few elements. */
long maxiterations = count*10;
/* We pass two pointers to the callback: the list to which it will
* add new elements, and the object containing the dictionary so that
......@@ -495,7 +500,9 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
privdata[1] = o;
do {
cursor = dictScan(ht, cursor, scanCallback, privdata);
} while (cursor && listLength(keys) < (unsigned long)count);
} while (cursor &&
maxiterations-- &&
listLength(keys) < (unsigned long)count);
} else if (o->type == REDIS_SET) {
int pos = 0;
int64_t ll;
......
......@@ -1399,6 +1399,7 @@ void initServerConfig(void) {
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
server.aof_load_truncated = REDIS_DEFAULT_AOF_LOAD_TRUNCATED;
server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME);
......
......@@ -132,6 +132,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 3
#define REDIS_DEFAULT_AOF_FILENAME "appendonly.aof"
#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
#define REDIS_DEFAULT_AOF_LOAD_TRUNCATED 1
#define REDIS_DEFAULT_ACTIVE_REHASHING 1
#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
#define REDIS_DEFAULT_MIN_SLAVES_TO_WRITE 0
......@@ -722,6 +723,7 @@ struct redisServer {
int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */
int aof_last_write_status; /* REDIS_OK or REDIS_ERR */
int aof_last_write_errno; /* Valid if aof_last_write_status is ERR */
int aof_load_truncated; /* Don't stop on unexpected AOF EOF. */
/* RDB persistence */
long long dirty; /* Changes to DB from the last save */
long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */
......
......@@ -222,10 +222,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
/* Build the arguments vector */
if (!argv) {
argv = zmalloc(sizeof(robj*)*argc);
argv_size = argc;
} else if (argv_size < argc) {
if (argv_size < argc) {
argv = zrealloc(argv,sizeof(robj*)*argc);
argv_size = argc;
}
......@@ -402,6 +399,7 @@ cleanup:
if (c->argv != argv) {
zfree(c->argv);
argv = NULL;
argv_size = 0;
}
if (raise_error) {
......
......@@ -213,6 +213,10 @@ struct sentinelState {
mstime_t tilt_start_time; /* When TITL started. */
mstime_t previous_time; /* Last time we ran the time handler. */
list *scripts_queue; /* Queue of user scripts to execute. */
char *announce_ip; /* IP addr that is gossiped to other sentinels if
not NULL. */
int announce_port; /* Port that is gossiped to other sentinels if
non zero. */
} sentinel;
/* A script execution job. */
......@@ -474,6 +478,8 @@ void initSentinel(void) {
sentinel.previous_time = mstime();
sentinel.running_scripts = 0;
sentinel.scripts_queue = listCreate();
sentinel.announce_ip = NULL;
sentinel.announce_port = 0;
}
/* This function gets called when the server is in Sentinel mode, started,
......@@ -1570,6 +1576,13 @@ char *sentinelHandleConfiguration(char **argv, int argc) {
return "Wrong hostname or port for sentinel.";
}
if (argc == 5) si->runid = sdsnew(argv[4]);
} else if (!strcasecmp(argv[0],"announce-ip") && argc == 2) {
/* announce-ip <ip-address> */
if (strlen(argv[1]))
sentinel.announce_ip = sdsnew(argv[1]);
} else if (!strcasecmp(argv[0],"announce-port") && argc == 2) {
/* announce-port <port> */
sentinel.announce_port = atoi(argv[1]);
} else {
return "Unrecognized sentinel configuration statement.";
}
......@@ -1701,6 +1714,20 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
"sentinel current-epoch %llu", (unsigned long long) sentinel.current_epoch);
rewriteConfigRewriteLine(state,"sentinel",line,1);
/* sentinel announce-ip. */
if (sentinel.announce_ip) {
line = sdsnew("sentinel announce-ip ");
line = sdscatrepr(line, sentinel.announce_ip, sdslen(sentinel.announce_ip));
rewriteConfigRewriteLine(state,"sentinel",line,1);
}
/* sentinel announce-port. */
if (sentinel.announce_port) {
line = sdscatprintf(sdsempty(),"sentinel announce-port %d",
sentinel.announce_port);
rewriteConfigRewriteLine(state,"sentinel",line,1);
}
dictReleaseIterator(di);
}
......@@ -2360,19 +2387,30 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
char ip[REDIS_IP_STR_LEN];
char payload[REDIS_IP_STR_LEN+1024];
int retval;
char *announce_ip;
int announce_port;
sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;
sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);
if (ri->flags & SRI_DISCONNECTED) return REDIS_ERR;
/* Try to obtain our own IP address. */
if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1) return REDIS_ERR;
/* Use the specified announce address if specified, otherwise try to
* obtain our own IP address. */
if (sentinel.announce_ip) {
announce_ip = sentinel.announce_ip;
} else {
if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1)
return REDIS_ERR;
announce_ip = ip;
}
announce_port = sentinel.announce_port ?
sentinel.announce_port : server.port;
/* Format and send the Hello message. */
snprintf(payload,sizeof(payload),
"%s,%d,%s,%llu," /* Info about this sentinel. */
"%s,%s,%d,%llu", /* Info about current master. */
ip, server.port, server.runid,
announce_ip, announce_port, server.runid,
(unsigned long long) sentinel.current_epoch,
/* --- */
master->name,master_addr->ip,master_addr->port,
......@@ -3334,9 +3372,9 @@ int sentinelLeaderIncr(dict *counters, char *runid) {
/* Scan all the Sentinels attached to this master to check if there
* is a leader for the specified epoch.
*
* To be a leader for a given epoch, we should have the majorify of
* the Sentinels we know that reported the same instance as
* leader for the same epoch. */
* To be a leader for a given epoch, we should have the majority of
* the Sentinels we know (ever seen since the last SENTINEL RESET) that
* reported the same instance as leader for the same epoch. */
char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
dict *counters;
dictIterator *di;
......@@ -3350,13 +3388,14 @@ char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
redisAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS));
counters = dictCreate(&leaderVotesDictType,NULL);
voters = dictSize(master->sentinels)+1; /* All the other sentinels and me. */
/* Count other sentinels votes */
di = dictGetIterator(master->sentinels);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch)
sentinelLeaderIncr(counters,ri->leader);
voters++;
}
dictReleaseIterator(di);
......@@ -3390,7 +3429,6 @@ char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
winner = myvote;
}
}
voters++; /* Anyway, count me as one of the voters. */
voters_quorum = voters/2+1;
if (winner && (max_votes < voters_quorum || max_votes < master->quorum))
......
......@@ -231,31 +231,14 @@ void setrangeCommand(redisClient *c) {
void getrangeCommand(redisClient *c) {
robj *o;
#ifdef _WIN64
int64_t start, end;
#else
long start, end;
#endif
long long start, end;
char *str, llbuf[32];
size_t strlen;
#ifdef _WIN64
// In the VS compiler, longs are not upgraded to 64-bits wien compiled as 64-bits. With GCC this is not the case.
// (https://software.intel.com/en-us/articles/size-of-long-integer-type-on-different-architecture-and-os).
//
// The immediate fix here is to use a 64bit type and call the conversion appropriate function. Longer term we need to
// eliminate the use of 'long' throughout the code base.
if (getLongLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK)
if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
return;
if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
return;
#else
if (getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK)
return;
if (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)
return;
#endif
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
......@@ -268,15 +251,15 @@ void getrangeCommand(redisClient *c) {
}
/* Convert negative indexes */
if (start < 0) start = (long)(strlen+start);
if (end < 0) end = (long)(strlen+end);
if (start < 0) start = strlen+start;
if (end < 0) end = strlen+end;
if (start < 0) start = 0;
if (end < 0) end = 0;
if ((size_t)end >= strlen) end = strlen-1;
if ((unsigned long long)end >= strlen) end = strlen-1;
/* Precondition: end >= 0 && end < strlen, so the only condition where
* nothing can be returned is: start > end. */
if (start > end) {
if (start > end || strlen == 0) {
addReply(c,shared.emptybulk);
} else {
addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
......
#define REDIS_VERSION "2.8.14"
#define REDIS_VERSION "2.8.15"
......@@ -23,6 +23,57 @@ proc start_server_aof {overrides code} {
}
tags {"aof"} {
## Server can start when aof-load-truncated is set to yes and AOF
## is truncated, with an incomplete MULTI block.
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof [formatCommand multi]
append_to_aof [formatCommand set bar world]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Unfinished MULTI: Server should start if load-truncated is yes" {
assert_equal 1 [is_alive $srv]
}
}
## Should also start with truncated AOF without incomplete MULTI block.
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof [string range [formatCommand set bar world] 0 end-1]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Short read: Server should start if load-truncated is yes" {
assert_equal 1 [is_alive $srv]
}
}
## Test that the server exits when the AOF contains a format error
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof "!!!"
append_to_aof [formatCommand set foo hello]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Bad format: Server should have logged an error" {
set pattern "*Bad file format reading the append only file*"
set retry 10
while {$retry} {
set result [exec tail -n1 < [dict get $srv stdout]]
if {[string match $pattern $result]} {
break
}
incr retry -1
after 1000
}
if {$retry == 0} {
error "assertion:expected error not found on config file"
}
}
}
## Test the server doesn't start when the AOF contains an unfinished MULTI
create_aof {
append_to_aof [formatCommand set foo hello]
......@@ -30,7 +81,7 @@ tags {"aof"} {
append_to_aof [formatCommand set bar world]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Unfinished MULTI: Server should have logged an error" {
set pattern "*Unexpected end of file reading the append only file*"
set retry 10
......@@ -54,9 +105,9 @@ tags {"aof"} {
append_to_aof [string range [formatCommand set bar world] 0 end-1]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Short read: Server should have logged an error" {
set pattern "*Bad file format reading the append only file*"
set pattern "*Unexpected end of file reading the append only file*"
set retry 10
while {$retry} {
set result [exec tail -n1 < [dict get $srv stdout]]
......@@ -86,7 +137,7 @@ tags {"aof"} {
}
## Test that the server can be started using the truncated AOF
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Fixed AOF: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
......@@ -110,7 +161,7 @@ tags {"aof"} {
append_to_aof [formatCommand spop set]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "AOF+SPOP: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
......@@ -133,7 +184,7 @@ tags {"aof"} {
append_to_aof [formatCommand rpush list bar]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "AOF+EXPIRE: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册