1set defaults { appendonly {yes} appendfilename {appendonly.aof} } 2set server_path [tmpdir server.aof] 3set aof_path "$server_path/appendonly.aof" 4 5proc append_to_aof {str} { 6 upvar fp fp 7 puts -nonewline $fp $str 8} 9 10proc create_aof {code} { 11 upvar fp fp aof_path aof_path 12 set fp [open $aof_path w+] 13 uplevel 1 $code 14 close $fp 15} 16 17proc start_server_aof {overrides code} { 18 upvar defaults defaults srv srv server_path server_path 19 set config [concat $defaults $overrides] 20 set srv [start_server [list overrides $config]] 21 uplevel 1 $code 22 kill_server $srv 23} 24 25tags {"aof"} { 26 ## Server can start when aof-load-truncated is set to yes and AOF 27 ## is truncated, with an incomplete MULTI block. 28 create_aof { 29 append_to_aof [formatCommand set foo hello] 30 append_to_aof [formatCommand multi] 31 append_to_aof [formatCommand set bar world] 32 } 33 34 start_server_aof [list dir $server_path aof-load-truncated yes] { 35 test "Unfinished MULTI: Server should start if load-truncated is yes" { 36 assert_equal 1 [is_alive $srv] 37 } 38 } 39 40 ## Should also start with truncated AOF without incomplete MULTI block. 41 create_aof { 42 append_to_aof [formatCommand incr foo] 43 append_to_aof [formatCommand incr foo] 44 append_to_aof [formatCommand incr foo] 45 append_to_aof [formatCommand incr foo] 46 append_to_aof [formatCommand incr foo] 47 append_to_aof [string range [formatCommand incr foo] 0 end-1] 48 } 49 50 start_server_aof [list dir $server_path aof-load-truncated yes] { 51 test "Short read: Server should start if load-truncated is yes" { 52 assert_equal 1 [is_alive $srv] 53 } 54 55 set client [redis [dict get $srv host] [dict get $srv port]] 56 57 test "Truncated AOF loaded: we expect foo to be equal to 5" { 58 assert {[$client get foo] eq "5"} 59 } 60 61 test "Append a new command after loading an incomplete AOF" { 62 $client incr foo 63 } 64 } 65 66 # Now the AOF file is expected to be correct 67 start_server_aof [list dir $server_path aof-load-truncated yes] { 68 test "Short read + command: Server should start" { 69 assert_equal 1 [is_alive $srv] 70 } 71 72 set client [redis [dict get $srv host] [dict get $srv port]] 73 74 test "Truncated AOF loaded: we expect foo to be equal to 6 now" { 75 assert {[$client get foo] eq "6"} 76 } 77 } 78 79 ## Test that the server exits when the AOF contains a format error 80 create_aof { 81 append_to_aof [formatCommand set foo hello] 82 append_to_aof "!!!" 83 append_to_aof [formatCommand set foo hello] 84 } 85 86 start_server_aof [list dir $server_path aof-load-truncated yes] { 87 test "Bad format: Server should have logged an error" { 88 set pattern "*Bad file format reading the append only file*" 89 set retry 10 90 while {$retry} { 91 set result [exec tail -n1 < [dict get $srv stdout]] 92 if {[string match $pattern $result]} { 93 break 94 } 95 incr retry -1 96 after 1000 97 } 98 if {$retry == 0} { 99 error "assertion:expected error not found on config file" 100 } 101 } 102 } 103 104 ## Test the server doesn't start when the AOF contains an unfinished MULTI 105 create_aof { 106 append_to_aof [formatCommand set foo hello] 107 append_to_aof [formatCommand multi] 108 append_to_aof [formatCommand set bar world] 109 } 110 111 start_server_aof [list dir $server_path aof-load-truncated no] { 112 test "Unfinished MULTI: Server should have logged an error" { 113 set pattern "*Unexpected end of file reading the append only file*" 114 set retry 10 115 while {$retry} { 116 set result [exec tail -n1 < [dict get $srv stdout]] 117 if {[string match $pattern $result]} { 118 break 119 } 120 incr retry -1 121 after 1000 122 } 123 if {$retry == 0} { 124 error "assertion:expected error not found on config file" 125 } 126 } 127 } 128 129 ## Test that the server exits when the AOF contains a short read 130 create_aof { 131 append_to_aof [formatCommand set foo hello] 132 append_to_aof [string range [formatCommand set bar world] 0 end-1] 133 } 134 135 start_server_aof [list dir $server_path aof-load-truncated no] { 136 test "Short read: Server should have logged an error" { 137 set pattern "*Unexpected end of file reading the append only file*" 138 set retry 10 139 while {$retry} { 140 set result [exec tail -n1 < [dict get $srv stdout]] 141 if {[string match $pattern $result]} { 142 break 143 } 144 incr retry -1 145 after 1000 146 } 147 if {$retry == 0} { 148 error "assertion:expected error not found on config file" 149 } 150 } 151 } 152 153 ## Test that redis-check-aof indeed sees this AOF is not valid 154 test "Short read: Utility should confirm the AOF is not valid" { 155 catch { 156 exec src/redis-check-aof $aof_path 157 } result 158 assert_match "*not valid*" $result 159 } 160 161 test "Short read: Utility should be able to fix the AOF" { 162 set result [exec src/redis-check-aof --fix $aof_path << "y\n"] 163 assert_match "*Successfully truncated AOF*" $result 164 } 165 166 ## Test that the server can be started using the truncated AOF 167 start_server_aof [list dir $server_path aof-load-truncated no] { 168 test "Fixed AOF: Server should have been started" { 169 assert_equal 1 [is_alive $srv] 170 } 171 172 test "Fixed AOF: Keyspace should contain values that were parseable" { 173 set client [redis [dict get $srv host] [dict get $srv port]] 174 wait_for_condition 50 100 { 175 [catch {$client ping} e] == 0 176 } else { 177 fail "Loading DB is taking too much time." 178 } 179 assert_equal "hello" [$client get foo] 180 assert_equal "" [$client get bar] 181 } 182 } 183 184 ## Test that SPOP (that modifies the client's argc/argv) is correctly free'd 185 create_aof { 186 append_to_aof [formatCommand sadd set foo] 187 append_to_aof [formatCommand sadd set bar] 188 append_to_aof [formatCommand spop set] 189 } 190 191 start_server_aof [list dir $server_path aof-load-truncated no] { 192 test "AOF+SPOP: Server should have been started" { 193 assert_equal 1 [is_alive $srv] 194 } 195 196 test "AOF+SPOP: Set should have 1 member" { 197 set client [redis [dict get $srv host] [dict get $srv port]] 198 wait_for_condition 50 100 { 199 [catch {$client ping} e] == 0 200 } else { 201 fail "Loading DB is taking too much time." 202 } 203 assert_equal 1 [$client scard set] 204 } 205 } 206 207 ## Uses the alsoPropagate() API. 208 create_aof { 209 append_to_aof [formatCommand sadd set foo] 210 append_to_aof [formatCommand sadd set bar] 211 append_to_aof [formatCommand sadd set gah] 212 append_to_aof [formatCommand spop set 2] 213 } 214 215 start_server_aof [list dir $server_path] { 216 test "AOF+SPOP: Server should have been started" { 217 assert_equal 1 [is_alive $srv] 218 } 219 220 test "AOF+SPOP: Set should have 1 member" { 221 set client [redis [dict get $srv host] [dict get $srv port]] 222 wait_for_condition 50 100 { 223 [catch {$client ping} e] == 0 224 } else { 225 fail "Loading DB is taking too much time." 226 } 227 assert_equal 1 [$client scard set] 228 } 229 } 230 231 ## Test that EXPIREAT is loaded correctly 232 create_aof { 233 append_to_aof [formatCommand rpush list foo] 234 append_to_aof [formatCommand expireat list 1000] 235 append_to_aof [formatCommand rpush list bar] 236 } 237 238 start_server_aof [list dir $server_path aof-load-truncated no] { 239 test "AOF+EXPIRE: Server should have been started" { 240 assert_equal 1 [is_alive $srv] 241 } 242 243 test "AOF+EXPIRE: List should be empty" { 244 set client [redis [dict get $srv host] [dict get $srv port]] 245 wait_for_condition 50 100 { 246 [catch {$client ping} e] == 0 247 } else { 248 fail "Loading DB is taking too much time." 249 } 250 assert_equal 0 [$client llen list] 251 } 252 } 253 254 start_server {overrides {appendonly {yes} appendfilename {appendonly.aof}}} { 255 test {Redis should not try to convert DEL into EXPIREAT for EXPIRE -1} { 256 r set x 10 257 r expire x -1 258 } 259 } 260} 261