github.com/hdt3213/godis@v1.2.9/database/aof_test.go (about) 1 package database 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/hdt3213/godis/aof" 12 13 "github.com/hdt3213/godis/config" 14 "github.com/hdt3213/godis/interface/database" 15 "github.com/hdt3213/godis/interface/redis" 16 "github.com/hdt3213/godis/lib/utils" 17 "github.com/hdt3213/godis/redis/connection" 18 "github.com/hdt3213/godis/redis/protocol" 19 "github.com/hdt3213/godis/redis/protocol/asserts" 20 ) 21 22 func makeTestData(db database.DB, dbIndex int, prefix string, size int) { 23 conn := connection.NewFakeConn() 24 conn.SelectDB(dbIndex) 25 db.Exec(conn, utils.ToCmdLine("FlushDB")) 26 cursor := 0 27 for i := 0; i < size; i++ { 28 key := prefix + strconv.Itoa(cursor) 29 cursor++ 30 db.Exec(conn, utils.ToCmdLine("SET", key, key, "EX", "10000")) 31 } 32 for i := 0; i < size; i++ { 33 key := prefix + strconv.Itoa(cursor) 34 cursor++ 35 db.Exec(conn, utils.ToCmdLine("RPUSH", key, key)) 36 } 37 for i := 0; i < size; i++ { 38 key := prefix + strconv.Itoa(cursor) 39 cursor++ 40 db.Exec(conn, utils.ToCmdLine("HSET", key, key, key)) 41 } 42 for i := 0; i < size; i++ { 43 key := prefix + strconv.Itoa(cursor) 44 cursor++ 45 db.Exec(conn, utils.ToCmdLine("SADD", key, key)) 46 } 47 for i := 0; i < size; i++ { 48 key := prefix + strconv.Itoa(cursor) 49 cursor++ 50 db.Exec(conn, utils.ToCmdLine("ZADD", key, "10", key)) 51 } 52 } 53 54 func validateTestData(t *testing.T, db database.DB, dbIndex int, prefix string, size int) { 55 conn := connection.NewFakeConn() 56 conn.SelectDB(dbIndex) 57 cursor := 0 58 var ret redis.Reply 59 for i := 0; i < size; i++ { 60 key := prefix + strconv.Itoa(cursor) 61 cursor++ 62 ret = db.Exec(conn, utils.ToCmdLine("GET", key)) 63 asserts.AssertBulkReply(t, ret, key) 64 ret = db.Exec(conn, utils.ToCmdLine("TTL", key)) 65 intResult, ok := ret.(*protocol.IntReply) 66 if !ok { 67 t.Errorf("expected int protocol, actually %s", ret.ToBytes()) 68 return 69 } 70 if intResult.Code <= 0 || intResult.Code > 10000 { 71 t.Error("wrong ttl") 72 } 73 } 74 for i := 0; i < size; i++ { 75 key := prefix + strconv.Itoa(cursor) 76 cursor++ 77 ret = db.Exec(conn, utils.ToCmdLine("LRANGE", key, "0", "-1")) 78 asserts.AssertMultiBulkReply(t, ret, []string{key}) 79 } 80 for i := 0; i < size; i++ { 81 key := prefix + strconv.Itoa(cursor) 82 cursor++ 83 ret = db.Exec(conn, utils.ToCmdLine("HGET", key, key)) 84 asserts.AssertBulkReply(t, ret, key) 85 } 86 for i := 0; i < size; i++ { 87 key := prefix + strconv.Itoa(cursor) 88 cursor++ 89 ret = db.Exec(conn, utils.ToCmdLine("SIsMember", key, key)) 90 asserts.AssertIntReply(t, ret, 1) 91 } 92 for i := 0; i < size; i++ { 93 key := prefix + strconv.Itoa(cursor) 94 cursor++ 95 ret = db.Exec(conn, utils.ToCmdLine("ZRANGE", key, "0", "-1")) 96 asserts.AssertMultiBulkReply(t, ret, []string{key}) 97 } 98 } 99 100 func TestAof(t *testing.T) { 101 tmpDir, err := ioutil.TempDir("", "godis") 102 if err != nil { 103 t.Error(err) 104 return 105 } 106 aofFilename := path.Join(tmpDir, "a.aof") 107 defer func() { 108 _ = os.Remove(aofFilename) 109 }() 110 config.Properties = &config.ServerProperties{ 111 AppendOnly: true, 112 AppendFilename: aofFilename, 113 AofUseRdbPreamble: false, 114 AppendFsync: aof.FsyncEverySec, 115 } 116 dbNum := 4 117 size := 10 118 var prefixes []string 119 aofWriteDB := NewStandaloneServer() 120 // generate test data 121 for i := 0; i < dbNum; i++ { 122 prefix := utils.RandString(8) 123 prefixes = append(prefixes, prefix) 124 makeTestData(aofWriteDB, i, prefix, size) 125 } 126 aofWriteDB.Close() // wait for aof finished 127 aofReadDB := NewStandaloneServer() // start new db and read aof file 128 for i := 0; i < dbNum; i++ { 129 prefix := prefixes[i] 130 validateTestData(t, aofReadDB, i, prefix, size) 131 } 132 aofReadDB.Close() 133 } 134 135 func TestRDB(t *testing.T) { 136 tmpDir, err := ioutil.TempDir("", "godis") 137 if err != nil { 138 t.Error(err) 139 return 140 } 141 aofFilename := path.Join(tmpDir, "a.aof") 142 rdbFilename := path.Join(tmpDir, "dump.rdb") 143 defer func() { 144 _ = os.Remove(aofFilename) 145 _ = os.Remove(rdbFilename) 146 }() 147 config.Properties = &config.ServerProperties{ 148 AppendOnly: true, 149 AppendFilename: aofFilename, 150 RDBFilename: rdbFilename, 151 } 152 dbNum := 4 153 size := 10 154 var prefixes []string 155 conn := connection.NewFakeConn() 156 writeDB := NewStandaloneServer() 157 for i := 0; i < dbNum; i++ { 158 prefix := utils.RandString(8) 159 prefixes = append(prefixes, prefix) 160 makeTestData(writeDB, i, prefix, size) 161 } 162 time.Sleep(time.Second) // wait for aof finished 163 writeDB.Exec(conn, utils.ToCmdLine("save")) 164 writeDB.Close() 165 readDB := NewStandaloneServer() // start new db and read aof file 166 for i := 0; i < dbNum; i++ { 167 prefix := prefixes[i] 168 validateTestData(t, readDB, i, prefix, size) 169 } 170 readDB.Close() 171 } 172 173 func TestRewriteAOF(t *testing.T) { 174 tmpFile, err := os.CreateTemp(config.GetTmpDir(), "*.aof") 175 if err != nil { 176 t.Error(err) 177 return 178 } 179 aofFilename := tmpFile.Name() 180 defer func() { 181 _ = os.Remove(aofFilename) 182 }() 183 config.Properties = &config.ServerProperties{ 184 AppendOnly: true, 185 AppendFilename: aofFilename, 186 AofUseRdbPreamble: false, 187 AppendFsync: aof.FsyncEverySec, 188 } 189 aofWriteDB := NewStandaloneServer() 190 size := 1 191 dbNum := 4 192 var prefixes []string 193 for i := 0; i < dbNum; i++ { 194 prefix := "" // utils.RandString(8) 195 prefixes = append(prefixes, prefix) 196 makeTestData(aofWriteDB, i, prefix, size) 197 } 198 //time.Sleep(2 * time.Second) 199 aofWriteDB.Exec(nil, utils.ToCmdLine("rewriteaof")) 200 time.Sleep(2 * time.Second) // wait for async goroutine finish its job 201 aofWriteDB.Close() // wait for aof finished 202 aofReadDB := NewStandaloneServer() // start new db and read aof file 203 for i := 0; i < dbNum; i++ { 204 prefix := prefixes[i] 205 validateTestData(t, aofReadDB, i, prefix, size) 206 } 207 aofReadDB.Close() 208 } 209 210 // TestRewriteAOF2 tests execute commands during rewrite procedure 211 func TestRewriteAOF2(t *testing.T) { 212 /* prepare */ 213 tmpFile, err := os.CreateTemp(config.GetTmpDir(), "*.aof") 214 if err != nil { 215 t.Error(err) 216 return 217 } 218 aofFilename := tmpFile.Name() 219 config.Properties = &config.ServerProperties{ 220 AppendOnly: true, 221 AppendFilename: aofFilename, 222 // set Aof-use-rdb-preamble to true to make sure rewrite procedure 223 AppendFsync: aof.FsyncAlways, 224 AofUseRdbPreamble: true, 225 } 226 227 keySize1 := 100 228 keySize2 := 250 229 /* write data */ 230 aofWriteDB := NewStandaloneServer() 231 dbNum := 4 232 conn := connection.NewFakeConn() 233 for i := 0; i < dbNum; i++ { 234 conn.SelectDB(i) 235 for j := 0; j < keySize1; j++ { 236 key := strconv.Itoa(j) 237 aofWriteDB.Exec(conn, utils.ToCmdLine("SET", key, key)) 238 } 239 } 240 241 /* rewrite */ 242 ctx, err := aofWriteDB.persister.StartRewrite() 243 if err != nil { 244 t.Error(err, "start rewrite failed") 245 return 246 } 247 248 /* add data during rewrite */ 249 ch := make(chan struct{}) 250 go func() { 251 for i := 0; i < dbNum; i++ { 252 conn.SelectDB(i) 253 for j := 0; j < keySize2; j++ { 254 key := "a" + strconv.Itoa(j) 255 aofWriteDB.Exec(conn, utils.ToCmdLine("SET", key, key)) 256 } 257 } 258 ch <- struct{}{} 259 }() 260 261 doRewriteErr := aofWriteDB.persister.DoRewrite(ctx) 262 if doRewriteErr != nil { 263 t.Error(doRewriteErr, "do rewrite failed") 264 return 265 } 266 aofWriteDB.persister.FinishRewrite(ctx) 267 <-ch 268 aofWriteDB.Close() // wait for aof finished 269 270 // start new db and read aof file 271 aofReadDB := NewStandaloneServer() 272 for i := 0; i < dbNum; i++ { 273 conn.SelectDB(i) 274 275 for j := 0; j < keySize1; j++ { 276 key := strconv.Itoa(j) 277 ret := aofReadDB.Exec(conn, utils.ToCmdLine("GET", key)) 278 asserts.AssertBulkReply(t, ret, key) 279 } 280 for j := 0; j < keySize2; j++ { 281 key := "a" + strconv.Itoa(j) 282 ret := aofReadDB.Exec(conn, utils.ToCmdLine("GET", key)) 283 asserts.AssertBulkReply(t, ret, key) 284 } 285 } 286 aofReadDB.Close() 287 }