direct call for FsyncAlways

This commit is contained in:
finley
2022-12-26 08:50:05 +08:00
parent d399353394
commit df672d4c92
2 changed files with 47 additions and 57 deletions

View File

@@ -61,6 +61,8 @@ type Persister struct {
pausingAof sync.Mutex pausingAof sync.Mutex
currentDB int currentDB int
listeners map[Listener]struct{} listeners map[Listener]struct{}
// reuse cmdLine buffer
buffer []CmdLine
} }
// NewPersister creates a new aof.Persister // NewPersister creates a new aof.Persister
@@ -70,6 +72,7 @@ func NewPersister(db database.DBEngine, filename string, load bool, fsync string
persister.aofFsync = strings.ToLower(fsync) persister.aofFsync = strings.ToLower(fsync)
persister.db = db persister.db = db
persister.tmpDBMaker = tmpDBMaker persister.tmpDBMaker = tmpDBMaker
persister.currentDB = 0
if load { if load {
persister.LoadAof(0) persister.LoadAof(0)
} }
@@ -100,20 +103,6 @@ func (persister *Persister) RemoveListener(listener Listener) {
delete(persister.listeners, listener) delete(persister.listeners, listener)
} }
var wgPool = sync.Pool{
New: func() interface{} {
return &sync.WaitGroup{}
},
}
func getWg() *sync.WaitGroup {
return wgPool.Get().(*sync.WaitGroup)
}
func returnWg(wg *sync.WaitGroup) {
wgPool.Put(wg)
}
// SaveCmdLine send command to aof goroutine through channel // SaveCmdLine send command to aof goroutine through channel
func (persister *Persister) SaveCmdLine(dbIndex int, cmdLine CmdLine) { func (persister *Persister) SaveCmdLine(dbIndex int, cmdLine CmdLine) {
// aofChan will be set as nil temporarily during load aof see Persister.LoadAof // aofChan will be set as nil temporarily during load aof see Persister.LoadAof
@@ -121,16 +110,12 @@ func (persister *Persister) SaveCmdLine(dbIndex int, cmdLine CmdLine) {
return return
} }
if persister.aofFsync == FsyncAlways { if persister.aofFsync == FsyncAlways {
// use WaitGroup to wait for saving finished p := &payload{
wg := getWg()
defer returnWg(wg)
wg.Add(1)
persister.aofChan <- &payload{
cmdLine: cmdLine, cmdLine: cmdLine,
dbIndex: dbIndex, dbIndex: dbIndex,
wg: wg,
} }
wg.Wait() persister.writeAof(p)
return
} }
persister.aofChan <- &payload{ persister.aofChan <- &payload{
cmdLine: cmdLine, cmdLine: cmdLine,
@@ -140,46 +125,42 @@ func (persister *Persister) SaveCmdLine(dbIndex int, cmdLine CmdLine) {
// listenCmd listen aof channel and write into file // listenCmd listen aof channel and write into file
func (persister *Persister) listenCmd() { func (persister *Persister) listenCmd() {
// serialized execution
var cmdLines []CmdLine
persister.currentDB = 0
for p := range persister.aofChan { for p := range persister.aofChan {
cmdLines = cmdLines[:0] // reuse underlying array persister.writeAof(p)
persister.pausingAof.Lock() // prevent other goroutines from pausing aof }
// ensure aof is in the right database persister.aofFinished <- struct{}{}
if p.dbIndex != persister.currentDB { }
// select db
selectCmd := utils.ToCmdLine("SELECT", strconv.Itoa(p.dbIndex)) func (persister *Persister) writeAof(p *payload) {
cmdLines = append(cmdLines, selectCmd) persister.buffer = persister.buffer[:0] // reuse underlying array
data := protocol.MakeMultiBulkReply(selectCmd).ToBytes() persister.pausingAof.Lock() // prevent other goroutines from pausing aof
_, err := persister.aofFile.Write(data) defer persister.pausingAof.Unlock()
if err != nil { // ensure aof is in the right database
logger.Warn(err) if p.dbIndex != persister.currentDB {
persister.pausingAof.Unlock() // select db
continue // skip this command selectCmd := utils.ToCmdLine("SELECT", strconv.Itoa(p.dbIndex))
} persister.buffer = append(persister.buffer, selectCmd)
persister.currentDB = p.dbIndex data := protocol.MakeMultiBulkReply(selectCmd).ToBytes()
}
// save command
data := protocol.MakeMultiBulkReply(p.cmdLine).ToBytes()
cmdLines = append(cmdLines, p.cmdLine)
_, err := persister.aofFile.Write(data) _, err := persister.aofFile.Write(data)
if err != nil { if err != nil {
logger.Warn(err) logger.Warn(err)
return // skip this command
} }
for listener := range persister.listeners { persister.currentDB = p.dbIndex
listener.Callback(cmdLines) }
} // save command
if persister.aofFsync == FsyncAlways { data := protocol.MakeMultiBulkReply(p.cmdLine).ToBytes()
_ = persister.aofFile.Sync() persister.buffer = append(persister.buffer, p.cmdLine)
} _, err := persister.aofFile.Write(data)
if p.wg != nil { if err != nil {
p.wg.Done() logger.Warn(err)
} }
persister.pausingAof.Unlock() for listener := range persister.listeners {
listener.Callback(persister.buffer)
}
if persister.aofFsync == FsyncAlways {
_ = persister.aofFile.Sync()
} }
persister.aofFinished <- struct{}{}
} }
// LoadAof read aof file, can only be used before Persister.listenCmd started // LoadAof read aof file, can only be used before Persister.listenCmd started
@@ -231,6 +212,13 @@ func (persister *Persister) LoadAof(maxBytes int) {
if protocol.IsErrorReply(ret) { if protocol.IsErrorReply(ret) {
logger.Error("exec err", string(ret.ToBytes())) logger.Error("exec err", string(ret.ToBytes()))
} }
if strings.ToLower(string(r.Args[0])) == "select" {
// execSelect success, here must be no error
dbIndex, err := strconv.Atoi(string(r.Args[1]))
if err == nil {
persister.currentDB = dbIndex
}
}
} }
} }

View File

@@ -53,7 +53,8 @@ func TestServerFsyncAlways(t *testing.T) {
config.Properties.AppendFsync = aof.FsyncAlways config.Properties.AppendFsync = aof.FsyncAlways
server := NewStandaloneServer() server := NewStandaloneServer()
conn := connection.NewFakeConn() conn := connection.NewFakeConn()
ret := server.Exec(conn, utils.ToCmdLine("set", "1", "1")) server.Exec(conn, utils.ToCmdLine("del", "1"))
ret := server.Exec(conn, utils.ToCmdLine("incr", "1"))
asserts.AssertNotError(t, ret) asserts.AssertNotError(t, ret)
reader := NewStandaloneServer() reader := NewStandaloneServer()
ret = reader.Exec(conn, utils.ToCmdLine("get", "1")) ret = reader.Exec(conn, utils.ToCmdLine("get", "1"))
@@ -71,7 +72,8 @@ func TestServerFsyncEverySec(t *testing.T) {
config.Properties.AppendFsync = aof.FsyncEverySec config.Properties.AppendFsync = aof.FsyncEverySec
server := NewStandaloneServer() server := NewStandaloneServer()
conn := connection.NewFakeConn() conn := connection.NewFakeConn()
ret := server.Exec(conn, utils.ToCmdLine("set", "1", "1")) server.Exec(conn, utils.ToCmdLine("del", "1"))
ret := server.Exec(conn, utils.ToCmdLine("incr", "1"))
asserts.AssertNotError(t, ret) asserts.AssertNotError(t, ret)
time.Sleep(1500 * time.Millisecond) time.Sleep(1500 * time.Millisecond)
reader := NewStandaloneServer() reader := NewStandaloneServer()