github.com/hdt3213/godis@v1.2.9/database/replication_master_test.go (about) 1 package database 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path" 8 "strconv" 9 "strings" 10 "sync/atomic" 11 "testing" 12 "time" 13 14 "github.com/hdt3213/godis/config" 15 rdb "github.com/hdt3213/rdb/parser" 16 "github.com/hdt3213/godis/lib/utils" 17 "github.com/hdt3213/godis/redis/connection" 18 "github.com/hdt3213/godis/redis/parser" 19 "github.com/hdt3213/godis/redis/protocol" 20 "github.com/hdt3213/godis/redis/protocol/asserts" 21 ) 22 23 func mockServer() *Server { 24 server := &Server{} 25 server.dbSet = make([]*atomic.Value, 16) 26 for i := range server.dbSet { 27 singleDB := makeDB() 28 singleDB.index = i 29 holder := &atomic.Value{} 30 holder.Store(singleDB) 31 server.dbSet[i] = holder 32 } 33 server.slaveStatus = initReplSlaveStatus() 34 server.initMaster() 35 return server 36 } 37 38 func TestReplicationMasterSide(t *testing.T) { 39 tmpDir, err := ioutil.TempDir("", "godis") 40 if err != nil { 41 t.Error(err) 42 return 43 } 44 aofFilename := path.Join(tmpDir, "a.aof") 45 defer func() { 46 _ = os.Remove(aofFilename) 47 }() 48 config.Properties = &config.ServerProperties{ 49 Databases: 16, 50 AppendOnly: true, 51 AppendFilename: aofFilename, 52 } 53 master := mockServer() 54 aofHandler, err := NewPersister(master, config.Properties.AppendFilename, true, config.Properties.AppendFsync) 55 if err != nil { 56 panic(err) 57 } 58 master.bindPersister(aofHandler) 59 slave := mockServer() 60 replConn := connection.NewFakeConn() 61 62 // set data to master 63 masterConn := connection.NewFakeConn() 64 resp := master.Exec(masterConn, utils.ToCmdLine("SET", "a", "a")) 65 asserts.AssertNotError(t, resp) 66 time.Sleep(time.Millisecond * 100) // wait write aof 67 68 // full re-sync 69 master.Exec(replConn, utils.ToCmdLine("psync", "?", "-1")) 70 masterChan := parser.ParseStream(replConn) 71 psyncPayload := <-masterChan 72 if psyncPayload.Err != nil { 73 t.Errorf("master bad protocol: %v", psyncPayload.Err) 74 return 75 } 76 psyncHeader, ok := psyncPayload.Data.(*protocol.StatusReply) 77 if !ok { 78 t.Error("psync header is not a status reply") 79 return 80 } 81 headers := strings.Split(psyncHeader.Status, " ") 82 if len(headers) != 3 { 83 t.Errorf("illegal psync header: %s", psyncHeader.Status) 84 return 85 } 86 87 replId := headers[1] 88 replOffset, err := strconv.ParseInt(headers[2], 10, 64) 89 if err != nil { 90 t.Errorf("illegal offset: %s", headers[2]) 91 return 92 } 93 t.Logf("repl id: %s, offset: %d", replId, replOffset) 94 95 rdbPayload := <-masterChan 96 if rdbPayload.Err != nil { 97 t.Error("read response failed: " + rdbPayload.Err.Error()) 98 return 99 } 100 rdbReply, ok := rdbPayload.Data.(*protocol.BulkReply) 101 if !ok { 102 t.Error("illegal payload header: " + string(rdbPayload.Data.ToBytes())) 103 return 104 } 105 106 rdbDec := rdb.NewDecoder(bytes.NewReader(rdbReply.Arg)) 107 err = slave.LoadRDB(rdbDec) 108 if err != nil { 109 t.Error("import rdb failed: " + err.Error()) 110 return 111 } 112 113 // get a 114 slaveConn := connection.NewFakeConn() 115 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "a")) 116 asserts.AssertBulkReply(t, resp, "a") 117 118 /*---- test broadcast aof ----*/ 119 masterConn = connection.NewFakeConn() 120 resp = master.Exec(masterConn, utils.ToCmdLine("SET", "b", "b")) 121 time.Sleep(time.Millisecond * 100) // wait write aof 122 asserts.AssertNotError(t, resp) 123 master.masterCron() 124 for { 125 payload := <-masterChan 126 if payload.Err != nil { 127 t.Error(payload.Err) 128 return 129 } 130 cmdLine, ok := payload.Data.(*protocol.MultiBulkReply) 131 if !ok { 132 t.Error("unexpected payload: " + string(payload.Data.ToBytes())) 133 return 134 } 135 slave.Exec(replConn, cmdLine.Args) 136 n := len(cmdLine.ToBytes()) 137 slave.slaveStatus.replOffset += int64(n) 138 if string(cmdLine.Args[0]) != "ping" { 139 break 140 } 141 } 142 143 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "b")) 144 asserts.AssertBulkReply(t, resp, "b") 145 146 /*---- test partial reconnect ----*/ 147 _ = replConn.Close() // mock disconnect 148 149 replConn = connection.NewFakeConn() 150 151 master.Exec(replConn, utils.ToCmdLine("psync", replId, 152 strconv.FormatInt(slave.slaveStatus.replOffset, 10))) 153 masterChan = parser.ParseStream(replConn) 154 psyncPayload = <-masterChan 155 if psyncPayload.Err != nil { 156 t.Errorf("master bad protocol: %v", psyncPayload.Err) 157 return 158 } 159 psyncHeader, ok = psyncPayload.Data.(*protocol.StatusReply) 160 if !ok { 161 t.Error("psync header is not a status reply") 162 return 163 } 164 headers = strings.Split(psyncHeader.Status, " ") 165 if len(headers) != 2 { 166 t.Errorf("illegal psync header: %s", psyncHeader.Status) 167 return 168 } 169 if headers[0] != "CONTINUE" { 170 t.Errorf("expect CONTINUE actual %s", headers[0]) 171 return 172 } 173 replId = headers[1] 174 t.Logf("partial resync repl id: %s, offset: %d", replId, slave.slaveStatus.replOffset) 175 176 resp = master.Exec(masterConn, utils.ToCmdLine("SET", "c", "c")) 177 time.Sleep(time.Millisecond * 100) // wait write aof 178 asserts.AssertNotError(t, resp) 179 master.masterCron() 180 for { 181 payload := <-masterChan 182 if payload.Err != nil { 183 t.Error(payload.Err) 184 return 185 } 186 cmdLine, ok := payload.Data.(*protocol.MultiBulkReply) 187 if !ok { 188 t.Error("unexpected payload: " + string(payload.Data.ToBytes())) 189 return 190 } 191 slave.Exec(replConn, cmdLine.Args) 192 if string(cmdLine.Args[0]) != "ping" { 193 break 194 } 195 } 196 197 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "c")) 198 asserts.AssertBulkReply(t, resp, "c") 199 } 200 201 func TestReplicationMasterRewriteRDB(t *testing.T) { 202 tmpDir, err := ioutil.TempDir("", "godis") 203 if err != nil { 204 t.Error(err) 205 return 206 } 207 aofFilename := path.Join(tmpDir, "a.aof") 208 defer func() { 209 _ = os.Remove(aofFilename) 210 }() 211 config.Properties = &config.ServerProperties{ 212 Databases: 16, 213 AppendOnly: true, 214 AppendFilename: aofFilename, 215 } 216 master := mockServer() 217 aofHandler, err := NewPersister(master, config.Properties.AppendFilename, true, config.Properties.AppendFsync) 218 if err != nil { 219 panic(err) 220 } 221 master.bindPersister(aofHandler) 222 223 masterConn := connection.NewFakeConn() 224 resp := master.Exec(masterConn, utils.ToCmdLine("SET", "a", "a")) 225 asserts.AssertNotError(t, resp) 226 resp = master.Exec(masterConn, utils.ToCmdLine("SET", "b", "b")) 227 asserts.AssertNotError(t, resp) 228 time.Sleep(time.Millisecond * 100) // wait write aof 229 230 err = master.rewriteRDB() 231 if err != nil { 232 t.Error(err) 233 return 234 } 235 resp = master.Exec(masterConn, utils.ToCmdLine("SET", "c", "c")) 236 asserts.AssertNotError(t, resp) 237 time.Sleep(time.Millisecond * 100) // wait write aof 238 239 // set slave 240 slave := mockServer() 241 replConn := connection.NewFakeConn() 242 master.Exec(replConn, utils.ToCmdLine("psync", "?", "-1")) 243 masterChan := parser.ParseStream(replConn) 244 psyncPayload := <-masterChan 245 if psyncPayload.Err != nil { 246 t.Errorf("master bad protocol: %v", psyncPayload.Err) 247 return 248 } 249 psyncHeader, ok := psyncPayload.Data.(*protocol.StatusReply) 250 if !ok { 251 t.Error("psync header is not a status reply") 252 return 253 } 254 headers := strings.Split(psyncHeader.Status, " ") 255 if len(headers) != 3 { 256 t.Errorf("illegal psync header: %s", psyncHeader.Status) 257 return 258 } 259 260 replId := headers[1] 261 replOffset, err := strconv.ParseInt(headers[2], 10, 64) 262 if err != nil { 263 t.Errorf("illegal offset: %s", headers[2]) 264 return 265 } 266 t.Logf("repl id: %s, offset: %d", replId, replOffset) 267 268 rdbPayload := <-masterChan 269 if rdbPayload.Err != nil { 270 t.Error("read response failed: " + rdbPayload.Err.Error()) 271 return 272 } 273 rdbReply, ok := rdbPayload.Data.(*protocol.BulkReply) 274 if !ok { 275 t.Error("illegal payload header: " + string(rdbPayload.Data.ToBytes())) 276 return 277 } 278 279 rdbDec := rdb.NewDecoder(bytes.NewReader(rdbReply.Arg)) 280 err = slave.LoadRDB(rdbDec) 281 if err != nil { 282 t.Error("import rdb failed: " + err.Error()) 283 return 284 } 285 286 slaveConn := connection.NewFakeConn() 287 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "a")) 288 asserts.AssertBulkReply(t, resp, "a") 289 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "b")) 290 asserts.AssertBulkReply(t, resp, "b") 291 292 master.masterCron() 293 for { 294 payload := <-masterChan 295 if payload.Err != nil { 296 t.Error(payload.Err) 297 return 298 } 299 cmdLine, ok := payload.Data.(*protocol.MultiBulkReply) 300 if !ok { 301 t.Error("unexpected payload: " + string(payload.Data.ToBytes())) 302 return 303 } 304 slave.Exec(replConn, cmdLine.Args) 305 n := len(cmdLine.ToBytes()) 306 slave.slaveStatus.replOffset += int64(n) 307 if string(cmdLine.Args[0]) != "ping" { 308 break 309 } 310 } 311 resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "c")) 312 asserts.AssertBulkReply(t, resp, "c") 313 }