diff --git a/00-RELEASENOTES b/00-RELEASENOTES index a41effad4793db452f0a8c3af0b8b9d9b58c3b36..bd5fb021fcd725469c055752a7c8d7c3474eab29 100644 --- a/00-RELEASENOTES +++ b/00-RELEASENOTES @@ -14,6 +14,39 @@ 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.16 ] Release date: 16 Sep 2014 + +# UPGRADE URGENCY: HIGH for Redis if you are using 2.8.15 + AOF. + LOW for Sentinel. + +* [FIX] The ability to load truncated AOF files introduced with Redis 2.8.15 + contains a bug fixed in this release: after loading the file was not + truncated to the last valid command, so the new commands are appended + after a non well formed command. This means that: + + 1) The first AOF rewrite triggered by the server will automatically + fix the problem. + 2) However, if the server is restarted before the rewrite, Redis may + not be able to load the file and you need to manually fix it. + + In order to fix a corrupted file you should start the redis-check-aof + utility WITHOUT the --fix option, just to check the offset where the + corruption is found. Around the offset reported by the check utility + you'll find, inside your AOF file, a command which is not complete + according to the Redis protocol. Just remove this incomplete command + leafing the file unaltered before and after the offending command, + and restart the server. + + IMPORTANT #1: Redis 2.8.15 is the only stable version of Redis with + this bug so probably no actual real-world problem happened since the + problem is automatically fixed at the first automatic AOF rewrite. + + IMPORTANT #2: Before upgrading to Redis 2.8.16, if you are using Redis + 2.8.15 with AOF enabled, make sure to trigger a manual AOF rewrite + using the BGREWRITEAOF command. + +* [FIX] SAVE is no longer propagated to AOF / slaves. + --[ Redis 2.8.15 ] Release date: 12 Sep 2014 # UPGRADE URGENCY: LOW for Redis, HIGH for Sentinel. diff --git a/src/aof.c b/src/aof.c index e4fe81b1289003b93b1ed7852241031c1b45cd9c..7b5462e9e5538ab966ea71c3f610dbc6f357824c 100644 --- a/src/aof.c +++ b/src/aof.c @@ -579,6 +579,7 @@ int loadAppendOnlyFile(char *filename) { struct redis_stat sb; int old_aof_state = server.aof_state; long loops = 0; + off_t valid_up_to = 0; /* Offset of the latest well-formed command loaded. */ if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) { server.aof_current_size = 0; @@ -672,6 +673,7 @@ 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. */ freeFakeClientArgv(fakeClient); + if (server.aof_load_truncated) valid_up_to = ftello(fp); } /* This point can only be reached when EOF is reached without errors. @@ -696,9 +698,27 @@ readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */ 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,"!!! Truncating the AOF at offset %llu !!!", + (unsigned long long) valid_up_to); + if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) { + if (valid_up_to == -1) { + redisLog(REDIS_WARNING,"Last valid command offset is invalid"); + } else { + redisLog(REDIS_WARNING,"Error truncating the AOF file: %s", + strerror(errno)); + } + } else { + /* Make sure the AOF file descriptor points to the end of the + * file after the truncate call. */ + if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) { + redisLog(REDIS_WARNING,"Can't seek the end of the AOF file: %s", + strerror(errno)); + } else { + 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 . 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server."); exit(1); diff --git a/src/redis.c b/src/redis.c index 830fead90044950e92e4ba77219a5abf734a8898..df3717ffb614236214bf7a4ca9a87e87500bae00 100644 --- a/src/redis.c +++ b/src/redis.c @@ -2000,6 +2000,7 @@ void call(redisClient *c, int flags) { c->cmd->proc(c); duration = ustime()-start; dirty = server.dirty-dirty; + if (dirty < 0) dirty = 0; /* When EVAL is called loading the AOF we don't want commands called * from Lua to go into the slowlog or to populate statistics. */ diff --git a/src/version.h b/src/version.h index 5834efb1d466c170d77f2ebc30062658bdd2ff53..7ea19b020885740be8a326c2a3f955e4e52f75fd 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define REDIS_VERSION "2.8.15" +#define REDIS_VERSION "2.8.16" diff --git a/tests/integration/aof.tcl b/tests/integration/aof.tcl index 462ec33e714113c46ae2b9cc11945182257a1b58..5fe745dc2595f77595ebf83bdc43e539802cf91d 100644 --- a/tests/integration/aof.tcl +++ b/tests/integration/aof.tcl @@ -39,14 +39,41 @@ tags {"aof"} { ## 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] + append_to_aof [formatCommand incr foo] + append_to_aof [formatCommand incr foo] + append_to_aof [formatCommand incr foo] + append_to_aof [formatCommand incr foo] + append_to_aof [formatCommand incr foo] + append_to_aof [string range [formatCommand incr foo] 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] } + + set client [redis [dict get $srv host] [dict get $srv port]] + + test "Truncated AOF loaded: we expect foo to be equal to 5" { + assert {[$client get foo] eq "5"} + } + + test "Append a new command after loading an incomplete AOF" { + $client incr foo + } + } + + # Now the AOF file is expected to be correct + start_server_aof [list dir $server_path aof-load-truncated yes] { + test "Short read + command: Server should start" { + assert_equal 1 [is_alive $srv] + } + + set client [redis [dict get $srv host] [dict get $srv port]] + + test "Truncated AOF loaded: we expect foo to be equal to 6 now" { + assert {[$client get foo] eq "6"} + } } ## Test that the server exits when the AOF contains a format error