2014-05-20 04:41:24 +04:00
|
|
|
package ledis
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-07-04 09:27:57 +04:00
|
|
|
"github.com/siddontang/go-log/log"
|
2014-08-07 12:49:48 +04:00
|
|
|
"github.com/siddontang/ledisdb/config"
|
2014-09-22 13:50:51 +04:00
|
|
|
"github.com/siddontang/ledisdb/rpl"
|
2014-07-25 13:58:00 +04:00
|
|
|
"github.com/siddontang/ledisdb/store"
|
2014-05-29 11:07:14 +04:00
|
|
|
"sync"
|
2014-06-12 06:51:36 +04:00
|
|
|
"time"
|
2014-05-20 04:41:24 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
type Ledis struct {
|
2014-08-07 12:49:48 +04:00
|
|
|
cfg *config.Config
|
2014-05-20 04:41:24 +04:00
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
ldb *store.DB
|
2014-05-20 04:41:24 +04:00
|
|
|
dbs [MaxDBNumber]*DB
|
2014-05-27 12:05:24 +04:00
|
|
|
|
2014-06-05 11:59:10 +04:00
|
|
|
quit chan struct{}
|
2014-09-22 13:50:51 +04:00
|
|
|
wg sync.WaitGroup
|
2014-08-25 10:18:23 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
r *rpl.Replication
|
|
|
|
rc chan struct{}
|
|
|
|
rbatch store.WriteBatch
|
|
|
|
rwg sync.WaitGroup
|
2014-08-30 13:39:44 +04:00
|
|
|
|
|
|
|
wLock sync.RWMutex //allow one write at same time
|
|
|
|
commitLock sync.Mutex //allow one write commit at same time
|
2014-09-17 13:54:04 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
// for readonly mode, only replication can write
|
|
|
|
readOnly bool
|
2014-05-20 04:41:24 +04:00
|
|
|
}
|
|
|
|
|
2014-08-07 12:49:48 +04:00
|
|
|
func Open(cfg *config.Config) (*Ledis, error) {
|
2014-09-22 13:50:51 +04:00
|
|
|
return Open2(cfg, RDWRMode)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Open2(cfg *config.Config, flags int) (*Ledis, error) {
|
2014-06-06 06:34:57 +04:00
|
|
|
if len(cfg.DataDir) == 0 {
|
2014-08-07 12:49:48 +04:00
|
|
|
cfg.DataDir = config.DefaultDataDir
|
2014-06-06 06:34:57 +04:00
|
|
|
}
|
|
|
|
|
2014-08-07 12:49:48 +04:00
|
|
|
ldb, err := store.Open(cfg)
|
2014-05-20 04:41:24 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
l := new(Ledis)
|
2014-06-05 11:59:10 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
l.readOnly = (flags&ROnlyMode > 0)
|
|
|
|
|
2014-06-05 11:59:10 +04:00
|
|
|
l.quit = make(chan struct{})
|
|
|
|
|
2014-05-20 04:41:24 +04:00
|
|
|
l.ldb = ldb
|
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
if cfg.Replication.Use {
|
|
|
|
if l.r, err = rpl.NewReplication(cfg); err != nil {
|
2014-05-27 12:05:24 +04:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-22 13:50:51 +04:00
|
|
|
|
2014-09-22 18:03:44 +04:00
|
|
|
l.rc = make(chan struct{}, 1)
|
2014-09-22 13:50:51 +04:00
|
|
|
l.rbatch = l.ldb.NewWriteBatch()
|
|
|
|
|
|
|
|
go l.onReplication()
|
2014-05-27 12:05:24 +04:00
|
|
|
} else {
|
2014-09-22 13:50:51 +04:00
|
|
|
l.r = nil
|
2014-05-27 12:05:24 +04:00
|
|
|
}
|
|
|
|
|
2014-05-20 04:41:24 +04:00
|
|
|
for i := uint8(0); i < MaxDBNumber; i++ {
|
2014-08-25 10:18:23 +04:00
|
|
|
l.dbs[i] = l.newDB(i)
|
2014-05-20 04:41:24 +04:00
|
|
|
}
|
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
go l.onDataExpired()
|
2014-06-12 06:51:36 +04:00
|
|
|
|
2014-05-20 04:41:24 +04:00
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Ledis) Close() {
|
2014-06-05 11:59:10 +04:00
|
|
|
close(l.quit)
|
2014-09-22 13:50:51 +04:00
|
|
|
l.wg.Wait()
|
2014-06-05 11:59:10 +04:00
|
|
|
|
2014-05-20 04:41:24 +04:00
|
|
|
l.ldb.Close()
|
2014-06-05 11:59:10 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
if l.r != nil {
|
|
|
|
l.r.Close()
|
|
|
|
l.r = nil
|
2014-06-05 11:59:10 +04:00
|
|
|
}
|
2014-05-20 04:41:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Ledis) Select(index int) (*DB, error) {
|
|
|
|
if index < 0 || index >= int(MaxDBNumber) {
|
|
|
|
return nil, fmt.Errorf("invalid db index %d", index)
|
|
|
|
}
|
|
|
|
|
|
|
|
return l.dbs[index], nil
|
|
|
|
}
|
2014-06-08 12:43:59 +04:00
|
|
|
|
|
|
|
func (l *Ledis) FlushAll() error {
|
|
|
|
for index, db := range l.dbs {
|
|
|
|
if _, err := db.FlushAll(); err != nil {
|
|
|
|
log.Error("flush db %d error %s", index, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2014-06-10 06:41:50 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
func (l *Ledis) IsReadOnly() bool {
|
|
|
|
if l.readOnly {
|
|
|
|
return true
|
|
|
|
} else if l.r != nil {
|
|
|
|
if b, _ := l.r.CommitIDBehind(); b {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Ledis) SetReadOnly(b bool) {
|
|
|
|
l.readOnly = b
|
2014-06-10 06:41:50 +04:00
|
|
|
}
|
2014-06-12 06:51:36 +04:00
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
func (l *Ledis) onDataExpired() {
|
|
|
|
l.wg.Add(1)
|
|
|
|
defer l.wg.Done()
|
|
|
|
|
2014-06-12 06:51:36 +04:00
|
|
|
var executors []*elimination = make([]*elimination, len(l.dbs))
|
|
|
|
for i, db := range l.dbs {
|
|
|
|
executors[i] = db.newEliminator()
|
|
|
|
}
|
|
|
|
|
2014-09-22 13:50:51 +04:00
|
|
|
tick := time.NewTicker(1 * time.Second)
|
|
|
|
defer tick.Stop()
|
|
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-tick.C:
|
|
|
|
if l.IsReadOnly() {
|
2014-06-12 06:51:36 +04:00
|
|
|
break
|
|
|
|
}
|
2014-09-22 13:50:51 +04:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
for _, eli := range executors {
|
|
|
|
eli.active()
|
|
|
|
}
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
<-done
|
|
|
|
case <-l.quit:
|
|
|
|
return
|
2014-06-12 06:51:36 +04:00
|
|
|
}
|
2014-09-22 13:50:51 +04:00
|
|
|
}
|
2014-06-12 06:51:36 +04:00
|
|
|
|
|
|
|
}
|