mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 16:57:06 +08:00
replication master side
This commit is contained in:
194
database/replication_master_test.go
Normal file
194
database/replication_master_test.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/hdt3213/godis/config"
|
||||
"github.com/hdt3213/godis/lib/utils"
|
||||
"github.com/hdt3213/godis/redis/connection"
|
||||
"github.com/hdt3213/godis/redis/parser"
|
||||
"github.com/hdt3213/godis/redis/protocol"
|
||||
"github.com/hdt3213/godis/redis/protocol/asserts"
|
||||
rdb "github.com/hdt3213/rdb/parser"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mockServer() *MultiDB {
|
||||
server := &MultiDB{}
|
||||
server.dbSet = make([]*atomic.Value, 16)
|
||||
for i := range server.dbSet {
|
||||
singleDB := makeDB()
|
||||
singleDB.index = i
|
||||
holder := &atomic.Value{}
|
||||
holder.Store(singleDB)
|
||||
server.dbSet[i] = holder
|
||||
}
|
||||
server.slaveStatus = initReplSlaveStatus()
|
||||
return server
|
||||
}
|
||||
|
||||
func TestReplicationMasterSide(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "godis")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
aofFilename := path.Join(tmpDir, "a.aof")
|
||||
defer func() {
|
||||
_ = os.Remove(aofFilename)
|
||||
}()
|
||||
config.Properties = &config.ServerProperties{
|
||||
Databases: 16,
|
||||
AppendOnly: true,
|
||||
AppendFilename: aofFilename,
|
||||
}
|
||||
master := mockServer()
|
||||
master.initAof()
|
||||
master.startAsMaster()
|
||||
slave := mockServer()
|
||||
replConn := connection.NewFakeConn()
|
||||
|
||||
// set data to master
|
||||
masterConn := connection.NewFakeConn()
|
||||
resp := master.Exec(masterConn, utils.ToCmdLine("SET", "a", "a"))
|
||||
asserts.AssertNotError(t, resp)
|
||||
time.Sleep(time.Millisecond * 100) // wait write aof
|
||||
|
||||
// full re-sync
|
||||
master.Exec(replConn, utils.ToCmdLine("psync", "?", "-1"))
|
||||
masterChan := parser.ParseStream(replConn)
|
||||
psyncPayload := <-masterChan
|
||||
if psyncPayload.Err != nil {
|
||||
t.Errorf("master bad protocol: %v", psyncPayload.Err)
|
||||
return
|
||||
}
|
||||
psyncHeader, ok := psyncPayload.Data.(*protocol.StatusReply)
|
||||
if !ok {
|
||||
t.Error("psync header is not a status reply")
|
||||
return
|
||||
}
|
||||
headers := strings.Split(psyncHeader.Status, " ")
|
||||
if len(headers) != 3 {
|
||||
t.Errorf("illegal psync header: %s", psyncHeader.Status)
|
||||
return
|
||||
}
|
||||
|
||||
replId := headers[1]
|
||||
replOffset, err := strconv.ParseInt(headers[2], 10, 64)
|
||||
if err != nil {
|
||||
t.Errorf("illegal offset: %s", headers[2])
|
||||
return
|
||||
}
|
||||
t.Logf("repl id: %s, offset: %d", replId, replOffset)
|
||||
|
||||
rdbPayload := <-masterChan
|
||||
if rdbPayload.Err != nil {
|
||||
t.Error("read response failed: " + rdbPayload.Err.Error())
|
||||
return
|
||||
}
|
||||
rdbReply, ok := rdbPayload.Data.(*protocol.BulkReply)
|
||||
if !ok {
|
||||
t.Error("illegal payload header: " + string(rdbPayload.Data.ToBytes()))
|
||||
return
|
||||
}
|
||||
|
||||
rdbDec := rdb.NewDecoder(bytes.NewReader(rdbReply.Arg))
|
||||
err = importRDB(rdbDec, slave)
|
||||
if err != nil {
|
||||
t.Error("import rdb failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// get a
|
||||
slaveConn := connection.NewFakeConn()
|
||||
resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "a"))
|
||||
asserts.AssertBulkReply(t, resp, "a")
|
||||
|
||||
/*---- test broadcast aof ----*/
|
||||
masterConn = connection.NewFakeConn()
|
||||
resp = master.Exec(masterConn, utils.ToCmdLine("SET", "b", "b"))
|
||||
time.Sleep(time.Millisecond * 100) // wait write aof
|
||||
asserts.AssertNotError(t, resp)
|
||||
master.masterCron()
|
||||
for {
|
||||
payload := <-masterChan
|
||||
if payload.Err != nil {
|
||||
t.Error(payload.Err)
|
||||
return
|
||||
}
|
||||
cmdLine, ok := payload.Data.(*protocol.MultiBulkReply)
|
||||
if !ok {
|
||||
t.Error("unexpected payload: " + string(payload.Data.ToBytes()))
|
||||
return
|
||||
}
|
||||
slave.Exec(replConn, cmdLine.Args)
|
||||
n := len(cmdLine.ToBytes())
|
||||
slave.slaveStatus.replOffset += int64(n)
|
||||
if string(cmdLine.Args[0]) != "ping" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "b"))
|
||||
asserts.AssertBulkReply(t, resp, "b")
|
||||
|
||||
/*---- test partial reconnect ----*/
|
||||
_ = replConn.Close() // mock disconnect
|
||||
|
||||
replConn = connection.NewFakeConn()
|
||||
|
||||
master.Exec(replConn, utils.ToCmdLine("psync", replId,
|
||||
strconv.FormatInt(slave.slaveStatus.replOffset, 10)))
|
||||
masterChan = parser.ParseStream(replConn)
|
||||
psyncPayload = <-masterChan
|
||||
if psyncPayload.Err != nil {
|
||||
t.Errorf("master bad protocol: %v", psyncPayload.Err)
|
||||
return
|
||||
}
|
||||
psyncHeader, ok = psyncPayload.Data.(*protocol.StatusReply)
|
||||
if !ok {
|
||||
t.Error("psync header is not a status reply")
|
||||
return
|
||||
}
|
||||
headers = strings.Split(psyncHeader.Status, " ")
|
||||
if len(headers) != 2 {
|
||||
t.Errorf("illegal psync header: %s", psyncHeader.Status)
|
||||
return
|
||||
}
|
||||
if headers[0] != "CONTINUE" {
|
||||
t.Errorf("expect CONTINUE actual %s", headers[0])
|
||||
return
|
||||
}
|
||||
replId = headers[1]
|
||||
t.Logf("partial resync repl id: %s, offset: %d", replId, slave.slaveStatus.replOffset)
|
||||
|
||||
resp = master.Exec(masterConn, utils.ToCmdLine("SET", "c", "c"))
|
||||
time.Sleep(time.Millisecond * 100) // wait write aof
|
||||
asserts.AssertNotError(t, resp)
|
||||
master.masterCron()
|
||||
for {
|
||||
payload := <-masterChan
|
||||
if payload.Err != nil {
|
||||
t.Error(payload.Err)
|
||||
return
|
||||
}
|
||||
cmdLine, ok := payload.Data.(*protocol.MultiBulkReply)
|
||||
if !ok {
|
||||
t.Error("unexpected payload: " + string(payload.Data.ToBytes()))
|
||||
return
|
||||
}
|
||||
slave.Exec(replConn, cmdLine.Args)
|
||||
if string(cmdLine.Args[0]) != "ping" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
resp = slave.Exec(slaveConn, utils.ToCmdLine("get", "c"))
|
||||
asserts.AssertBulkReply(t, resp, "c")
|
||||
}
|
Reference in New Issue
Block a user