forked from mirror/ledisdb
add readonly support
This commit is contained in:
parent
848fbf34ad
commit
855f0a3477
|
@ -1,4 +1,4 @@
|
||||||
//This file was generated by .tools/generate_commands.py on Wed Oct 08 2014 16:36:20 +0800
|
//This file was generated by .tools/generate_commands.py on Fri Oct 10 2014 09:08:54 +0800
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var helpCommands = [][]string{
|
var helpCommands = [][]string{
|
||||||
|
@ -87,7 +87,7 @@ var helpCommands = [][]string{
|
||||||
{"SINTER", "key [key ...]", "Set"},
|
{"SINTER", "key [key ...]", "Set"},
|
||||||
{"SINTERSTORE", "destination key [key ...]", "Set"},
|
{"SINTERSTORE", "destination key [key ...]", "Set"},
|
||||||
{"SISMEMBER", "key member", "Set"},
|
{"SISMEMBER", "key member", "Set"},
|
||||||
{"SLAVEOF", "host port [restart]", "Replication"},
|
{"SLAVEOF", "host port [RESTART] [READONLY]", "Replication"},
|
||||||
{"SMCLEAR", "key [key ...]", "Set"},
|
{"SMCLEAR", "key [key ...]", "Set"},
|
||||||
{"SMEMBERS", "key", "Set"},
|
{"SMEMBERS", "key", "Set"},
|
||||||
{"SPERSIST", "key", "Set"},
|
{"SPERSIST", "key", "Set"},
|
||||||
|
|
|
@ -18,6 +18,8 @@ var configFile = flag.String("config", "", "ledisdb config file")
|
||||||
var dbName = flag.String("db_name", "", "select a db to use, it will overwrite the config's db name")
|
var dbName = flag.String("db_name", "", "select a db to use, it will overwrite the config's db name")
|
||||||
var usePprof = flag.Bool("pprof", false, "enable pprof")
|
var usePprof = flag.Bool("pprof", false, "enable pprof")
|
||||||
var pprofPort = flag.Int("pprof_port", 6060, "pprof http port")
|
var pprofPort = flag.Int("pprof_port", 6060, "pprof http port")
|
||||||
|
var slaveof = flag.String("slaveof", "", "make the server a slave of another instance")
|
||||||
|
var readonly = flag.Bool("readonly", false, "set readonly mode, salve server is always readonly")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
@ -43,6 +45,13 @@ func main() {
|
||||||
cfg.DBName = *dbName
|
cfg.DBName = *dbName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*slaveof) > 0 {
|
||||||
|
cfg.SlaveOf = *slaveof
|
||||||
|
cfg.Readonly = true
|
||||||
|
} else {
|
||||||
|
cfg.Readonly = *readonly
|
||||||
|
}
|
||||||
|
|
||||||
var app *server.App
|
var app *server.App
|
||||||
app, err = server.NewApp(cfg)
|
app, err = server.NewApp(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -54,6 +54,8 @@ type Config struct {
|
||||||
|
|
||||||
SlaveOf string `toml:"slaveof"`
|
SlaveOf string `toml:"slaveof"`
|
||||||
|
|
||||||
|
Readonly bool `toml:readonly`
|
||||||
|
|
||||||
DataDir string `toml:"data_dir"`
|
DataDir string `toml:"data_dir"`
|
||||||
|
|
||||||
DBName string `toml:"db_name"`
|
DBName string `toml:"db_name"`
|
||||||
|
@ -106,6 +108,7 @@ func NewConfigDefault() *Config {
|
||||||
cfg.DBName = DefaultDBName
|
cfg.DBName = DefaultDBName
|
||||||
|
|
||||||
cfg.SlaveOf = ""
|
cfg.SlaveOf = ""
|
||||||
|
cfg.Readonly = false
|
||||||
|
|
||||||
// disable access log
|
// disable access log
|
||||||
cfg.AccessLog = ""
|
cfg.AccessLog = ""
|
||||||
|
|
|
@ -16,6 +16,10 @@ access_log = ""
|
||||||
# Any write operations except flushall and replication will be disabled in slave mode.
|
# Any write operations except flushall and replication will be disabled in slave mode.
|
||||||
slaveof = ""
|
slaveof = ""
|
||||||
|
|
||||||
|
# Readonly mode, slave server is always readonly even readonly = false
|
||||||
|
# for readonly mode, only replication and flushall can write
|
||||||
|
readonly = false
|
||||||
|
|
||||||
# Choose which backend storage to use, now support:
|
# Choose which backend storage to use, now support:
|
||||||
#
|
#
|
||||||
# leveldb
|
# leveldb
|
||||||
|
|
|
@ -301,7 +301,7 @@
|
||||||
"readonly": false
|
"readonly": false
|
||||||
},
|
},
|
||||||
"SLAVEOF": {
|
"SLAVEOF": {
|
||||||
"arguments": "host port [restart]",
|
"arguments": "host port [RESTART] [READONLY]",
|
||||||
"group": "Replication",
|
"group": "Replication",
|
||||||
"readonly": false
|
"readonly": false
|
||||||
},
|
},
|
||||||
|
|
|
@ -122,7 +122,7 @@ Table of Contents
|
||||||
- [BPERSIST key](#bpersist-key)
|
- [BPERSIST key](#bpersist-key)
|
||||||
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
|
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
|
||||||
- [Replication](#replication)
|
- [Replication](#replication)
|
||||||
- [SLAVEOF host port [restart]](#slaveof-host-port-restart)
|
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
|
||||||
- [FULLSYNC](#fullsync)
|
- [FULLSYNC](#fullsync)
|
||||||
- [SYNC logid](#sync-logid)
|
- [SYNC logid](#sync-logid)
|
||||||
- [Server](#server)
|
- [Server](#server)
|
||||||
|
@ -2466,13 +2466,15 @@ See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
## Replication
|
## Replication
|
||||||
|
|
||||||
### SLAVEOF host port [restart]
|
### SLAVEOF host port [RESTART] [READONLY]
|
||||||
|
|
||||||
Changes the replication settings of a slave on the fly. If the server is already acting as slave, SLAVEOF NO ONE will turn off the replication.
|
Changes the replication settings of a slave on the fly. If the server is already acting as slave, `SLAVEOF NO ONE` will turn off the replication and turn the server into master. `SLAVEOF NO ONE READONLY` will turn the server into master with readonly mode.
|
||||||
|
|
||||||
SLAVEOF host port will make the server a slave of another server listening at the specified host and port.
|
If the server is already master, `SLAVEOF NO ONE READONLY` will force the server to readonly mode, and `SLAVEOF NO ONE` will disable readonly.
|
||||||
|
|
||||||
If a server is already a slave of a master, SLAVEOF host port will stop the replication against the old and start the synchronization against the new one, if restart is set, it will discard the old dataset, otherwise it will sync with LastLogID + 1.
|
`SLAVEOF host port` will make the server a slave of another server listening at the specified host and port.
|
||||||
|
|
||||||
|
If a server is already a slave of a master, `SLAVEOF host port` will stop the replication against the old and start the synchronization against the new one, if RESTART is set, it will discard the old dataset, otherwise it will sync with LastLogID + 1.
|
||||||
|
|
||||||
|
|
||||||
### FULLSYNC
|
### FULLSYNC
|
||||||
|
|
|
@ -16,6 +16,10 @@ access_log = ""
|
||||||
# Any write operations except flushall and replication will be disabled in slave mode.
|
# Any write operations except flushall and replication will be disabled in slave mode.
|
||||||
slaveof = ""
|
slaveof = ""
|
||||||
|
|
||||||
|
# Readonly mode, slave server is always readonly even readonly = false
|
||||||
|
# for readonly mode, only replication and flushall can write
|
||||||
|
readonly = false
|
||||||
|
|
||||||
# Choose which backend storage to use, now support:
|
# Choose which backend storage to use, now support:
|
||||||
#
|
#
|
||||||
# leveldb
|
# leveldb
|
||||||
|
|
|
@ -46,11 +46,6 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
RDWRMode = 0
|
|
||||||
ROnlyMode = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultScanCount int = 10
|
defaultScanCount int = 10
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,17 +33,10 @@ type Ledis struct {
|
||||||
wLock sync.RWMutex //allow one write at same time
|
wLock sync.RWMutex //allow one write at same time
|
||||||
commitLock sync.Mutex //allow one write commit at same time
|
commitLock sync.Mutex //allow one write commit at same time
|
||||||
|
|
||||||
// for readonly mode, only replication and flushall can write
|
|
||||||
readOnly bool
|
|
||||||
|
|
||||||
lock io.Closer
|
lock io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(cfg *config.Config) (*Ledis, error) {
|
func Open(cfg *config.Config) (*Ledis, error) {
|
||||||
return Open2(cfg, RDWRMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open2(cfg *config.Config, flags int) (*Ledis, error) {
|
|
||||||
if len(cfg.DataDir) == 0 {
|
if len(cfg.DataDir) == 0 {
|
||||||
cfg.DataDir = config.DefaultDataDir
|
cfg.DataDir = config.DefaultDataDir
|
||||||
}
|
}
|
||||||
|
@ -53,13 +46,12 @@ func Open2(cfg *config.Config, flags int) (*Ledis, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
l := new(Ledis)
|
l := new(Ledis)
|
||||||
|
l.cfg = cfg
|
||||||
|
|
||||||
if l.lock, err = filelock.Lock(path.Join(cfg.DataDir, "LOCK")); err != nil {
|
if l.lock, err = filelock.Lock(path.Join(cfg.DataDir, "LOCK")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
l.readOnly = (flags&ROnlyMode > 0)
|
|
||||||
|
|
||||||
l.quit = make(chan struct{})
|
l.quit = make(chan struct{})
|
||||||
|
|
||||||
if l.ldb, err = store.Open(cfg); err != nil {
|
if l.ldb, err = store.Open(cfg); err != nil {
|
||||||
|
@ -163,7 +155,7 @@ func (l *Ledis) flushAll() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Ledis) IsReadOnly() bool {
|
func (l *Ledis) IsReadOnly() bool {
|
||||||
if l.readOnly {
|
if l.cfg.Readonly {
|
||||||
return true
|
return true
|
||||||
} else if l.r != nil {
|
} else if l.r != nil {
|
||||||
if b, _ := l.r.CommitIDBehind(); b {
|
if b, _ := l.r.CommitIDBehind(); b {
|
||||||
|
@ -173,10 +165,6 @@ func (l *Ledis) IsReadOnly() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Ledis) SetReadOnly(b bool) {
|
|
||||||
l.readOnly = b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Ledis) onDataExpired() {
|
func (l *Ledis) onDataExpired() {
|
||||||
defer l.wg.Done()
|
defer l.wg.Done()
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ func (l *Ledis) WaitReplication() error {
|
||||||
func (l *Ledis) StoreLogsFromReader(rb io.Reader) error {
|
func (l *Ledis) StoreLogsFromReader(rb io.Reader) error {
|
||||||
if !l.ReplicationUsed() {
|
if !l.ReplicationUsed() {
|
||||||
return ErrRplNotSupport
|
return ErrRplNotSupport
|
||||||
} else if !l.readOnly {
|
} else if !l.cfg.Readonly {
|
||||||
return ErrRplInRDWR
|
return ErrRplInRDWR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,11 @@ func TestReplication(t *testing.T) {
|
||||||
cfgS := new(config.Config)
|
cfgS := new(config.Config)
|
||||||
cfgS.DataDir = "/tmp/test_repl/slave"
|
cfgS.DataDir = "/tmp/test_repl/slave"
|
||||||
cfgS.UseReplication = true
|
cfgS.UseReplication = true
|
||||||
|
cfgS.Readonly = true
|
||||||
|
|
||||||
os.RemoveAll(cfgS.DataDir)
|
os.RemoveAll(cfgS.DataDir)
|
||||||
|
|
||||||
slave, err = Open2(cfgS, ROnlyMode)
|
slave, err = Open(cfgS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,13 +88,12 @@ func NewApp(cfg *config.Config) (*App, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flag := ledis.RDWRMode
|
|
||||||
if len(app.cfg.SlaveOf) > 0 {
|
if len(app.cfg.SlaveOf) > 0 {
|
||||||
//slave must readonly
|
//slave must readonly
|
||||||
flag = ledis.ROnlyMode
|
app.cfg.Readonly = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.ldb, err = ledis.Open2(cfg, flag); err != nil {
|
if app.ldb, err = ledis.Open(cfg); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +134,7 @@ func (app *App) Close() {
|
||||||
|
|
||||||
func (app *App) Run() {
|
func (app *App) Run() {
|
||||||
if len(app.cfg.SlaveOf) > 0 {
|
if len(app.cfg.SlaveOf) > 0 {
|
||||||
app.slaveof(app.cfg.SlaveOf, false)
|
app.slaveof(app.cfg.SlaveOf, false, app.cfg.Readonly)
|
||||||
}
|
}
|
||||||
|
|
||||||
go app.httpServe()
|
go app.httpServe()
|
||||||
|
|
|
@ -13,18 +13,19 @@ import (
|
||||||
func slaveofCommand(c *client) error {
|
func slaveofCommand(c *client) error {
|
||||||
args := c.args
|
args := c.args
|
||||||
|
|
||||||
if len(args) != 2 || len(args) != 3 {
|
if len(args) != 2 && len(args) != 3 {
|
||||||
return ErrCmdParams
|
return ErrCmdParams
|
||||||
}
|
}
|
||||||
|
|
||||||
masterAddr := ""
|
masterAddr := ""
|
||||||
restart := false
|
restart := false
|
||||||
|
readonly := false
|
||||||
|
|
||||||
if strings.ToLower(hack.String(args[0])) == "no" &&
|
if strings.ToLower(hack.String(args[0])) == "no" &&
|
||||||
strings.ToLower(hack.String(args[1])) == "one" {
|
strings.ToLower(hack.String(args[1])) == "one" {
|
||||||
//stop replication, use master = ""
|
//stop replication, use master = ""
|
||||||
if len(args) != 2 {
|
if len(args) == 3 && strings.ToLower(hack.String(args[2])) == "readonly" {
|
||||||
return ErrCmdParams
|
readonly = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := strconv.ParseInt(hack.String(args[1]), 10, 16); err != nil {
|
if _, err := strconv.ParseInt(hack.String(args[1]), 10, 16); err != nil {
|
||||||
|
@ -38,7 +39,7 @@ func slaveofCommand(c *client) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.app.slaveof(masterAddr, restart); err != nil {
|
if err := c.app.slaveof(masterAddr, restart, readonly); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ func TestReplication(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
slave.slaveof("", false)
|
slave.slaveof("", false, false)
|
||||||
|
|
||||||
db.Set([]byte("a2"), value)
|
db.Set([]byte("a2"), value)
|
||||||
db.Set([]byte("b2"), value)
|
db.Set([]byte("b2"), value)
|
||||||
|
@ -112,7 +112,7 @@ func TestReplication(t *testing.T) {
|
||||||
t.Fatal("must error")
|
t.Fatal("must error")
|
||||||
}
|
}
|
||||||
|
|
||||||
slave.slaveof(masterCfg.Addr, false)
|
slave.slaveof(masterCfg.Addr, false, false)
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,8 @@ func (i *info) dumpServer(buf *bytes.Buffer) {
|
||||||
i.dumpPairs(buf, infoPair{"os", i.Server.OS},
|
i.dumpPairs(buf, infoPair{"os", i.Server.OS},
|
||||||
infoPair{"process_id", i.Server.ProceessId},
|
infoPair{"process_id", i.Server.ProceessId},
|
||||||
infoPair{"addr", i.app.cfg.Addr},
|
infoPair{"addr", i.app.cfg.Addr},
|
||||||
infoPair{"http_addr", i.app.cfg.HttpAddr})
|
infoPair{"http_addr", i.app.cfg.HttpAddr},
|
||||||
|
infoPair{"readonly", i.app.cfg.Readonly})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *info) dumpClients(buf *bytes.Buffer) {
|
func (i *info) dumpClients(buf *bytes.Buffer) {
|
||||||
|
@ -155,10 +156,10 @@ func (i *info) dumpReplication(buf *bytes.Buffer) {
|
||||||
slaves = append(slaves, s.remoteAddr)
|
slaves = append(slaves, s.remoteAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
p = append(p, infoPair{"readonly", i.app.ldb.IsReadOnly()})
|
p = append(p, infoPair{"slaveof", i.app.cfg.SlaveOf})
|
||||||
|
|
||||||
if len(slaves) > 0 {
|
if len(slaves) > 0 {
|
||||||
p = append(p, infoPair{"slave", strings.Join(slaves, ",")})
|
p = append(p, infoPair{"slaves", strings.Join(slaves, ",")})
|
||||||
}
|
}
|
||||||
|
|
||||||
if s, _ := i.app.ldb.ReplicationStat(); s != nil {
|
if s, _ := i.app.ldb.ReplicationStat(); s != nil {
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (m *master) startReplication(masterAddr string, restart bool) error {
|
||||||
|
|
||||||
m.quit = make(chan struct{}, 1)
|
m.quit = make(chan struct{}, 1)
|
||||||
|
|
||||||
m.app.ldb.SetReadOnly(true)
|
m.app.cfg.Readonly = true
|
||||||
|
|
||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
go m.runReplication(restart)
|
go m.runReplication(restart)
|
||||||
|
@ -238,10 +238,16 @@ func (m *master) sync() error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) slaveof(masterAddr string, restart bool) error {
|
func (app *App) slaveof(masterAddr string, restart bool, readonly bool) error {
|
||||||
app.m.Lock()
|
app.m.Lock()
|
||||||
defer app.m.Unlock()
|
defer app.m.Unlock()
|
||||||
|
|
||||||
|
//in master mode and no slaveof, only set readonly
|
||||||
|
if len(app.cfg.SlaveOf) == 0 && len(masterAddr) == 0 {
|
||||||
|
app.cfg.Readonly = readonly
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if !app.ldb.ReplicationUsed() {
|
if !app.ldb.ReplicationUsed() {
|
||||||
return fmt.Errorf("slaveof must enable replication")
|
return fmt.Errorf("slaveof must enable replication")
|
||||||
}
|
}
|
||||||
|
@ -253,7 +259,7 @@ func (app *App) slaveof(masterAddr string, restart bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.ldb.SetReadOnly(false)
|
app.cfg.Readonly = readonly
|
||||||
} else {
|
} else {
|
||||||
return app.m.startReplication(masterAddr, restart)
|
return app.m.startReplication(masterAddr, restart)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue