Files
redis-go/database/persistence.go
2025-05-05 18:26:10 +08:00

132 lines
3.3 KiB
Go

package database
import (
"fmt"
"os"
"sync/atomic"
"github.com/hdt3213/godis/aof"
"github.com/hdt3213/godis/config"
"github.com/hdt3213/godis/datastruct/dict"
List "github.com/hdt3213/godis/datastruct/list"
HashSet "github.com/hdt3213/godis/datastruct/set"
SortedSet "github.com/hdt3213/godis/datastruct/sortedset"
"github.com/hdt3213/godis/interface/database"
"github.com/hdt3213/rdb/core"
rdb "github.com/hdt3213/rdb/parser"
)
// loadRdbFile loads rdb file from disk
func (server *Server) loadRdbFile() error {
rdbFile, err := os.Open(config.Properties.RDBFilename)
if err != nil {
return fmt.Errorf("open rdb file failed " + err.Error())
}
defer func() {
_ = rdbFile.Close()
}()
decoder := rdb.NewDecoder(rdbFile)
err = server.LoadRDB(decoder)
if err != nil {
return fmt.Errorf("load rdb file failed " + err.Error())
}
return nil
}
// LoadRDB real implementation of loading rdb file
func (server *Server) LoadRDB(dec *core.Decoder) error {
return dec.Parse(func(o rdb.RedisObject) bool {
db := server.mustSelectDB(o.GetDBIndex())
var entity *database.DataEntity
switch o.GetType() {
case rdb.StringType:
str := o.(*rdb.StringObject)
entity = &database.DataEntity{
Data: str.Value,
}
case rdb.ListType:
listObj := o.(*rdb.ListObject)
list := List.NewQuickList()
for _, v := range listObj.Values {
list.Add(v)
}
entity = &database.DataEntity{
Data: list,
}
case rdb.HashType:
hashObj := o.(*rdb.HashObject)
hash := dict.MakeSimple()
for k, v := range hashObj.Hash {
hash.Put(k, v)
}
entity = &database.DataEntity{
Data: hash,
}
case rdb.SetType:
setObj := o.(*rdb.SetObject)
set := HashSet.Make()
for _, mem := range setObj.Members {
set.Add(string(mem))
}
entity = &database.DataEntity{
Data: set,
}
case rdb.ZSetType:
zsetObj := o.(*rdb.ZSetObject)
zSet := SortedSet.Make()
for _, e := range zsetObj.Entries {
zSet.Add(e.Member, e.Score)
}
entity = &database.DataEntity{
Data: zSet,
}
}
if entity != nil {
db.PutEntity(o.GetKey(), entity)
if o.GetExpiration() != nil {
db.Expire(o.GetKey(), *o.GetExpiration())
}
// add to aof
db.addAof(aof.EntityToCmd(o.GetKey(), entity).Args)
}
return true
})
}
func NewPersister(db database.DBEngine, filename string, load bool, fsync string) (*aof.Persister, error) {
return aof.NewPersister(db, filename, load, fsync, func() database.DBEngine {
return MakeAuxiliaryServer()
})
}
func (server *Server) AddAof(dbIndex int, cmdLine CmdLine) {
if server.persister != nil {
server.persister.SaveCmdLine(dbIndex, cmdLine)
}
}
func (server *Server) bindPersister(persister *aof.Persister) {
server.persister = persister
// bind SaveCmdLine
for _, db := range server.dbSet {
singleDB := db.Load().(*DB)
singleDB.addAof = func(line CmdLine) {
if config.Properties.AppendOnly { // config may be changed during runtime
server.persister.SaveCmdLine(singleDB.index, line)
}
}
}
}
// MakeAuxiliaryServer create a Server only with basic capabilities for aof rewrite and other usages
func MakeAuxiliaryServer() *Server {
mdb := &Server{}
mdb.dbSet = make([]*atomic.Value, config.Properties.Databases)
for i := range mdb.dbSet {
holder := &atomic.Value{}
holder.Store(makeBasicDB())
mdb.dbSet[i] = holder
}
return mdb
}