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 }