etcd/storage/index.go

172 lines
3.6 KiB
Go
Raw Normal View History

2015-05-15 03:53:41 +03:00
package storage
import (
"log"
"sync"
2015-05-15 03:59:55 +03:00
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree"
2015-05-15 03:53:41 +03:00
)
type index interface {
2015-06-29 22:47:17 +03:00
Get(key []byte, atRev int64) (rev, created reversion, ver int64, err error)
2015-05-31 08:56:33 +03:00
Range(key, end []byte, atRev int64) ([][]byte, []reversion)
Put(key []byte, rev reversion)
Restore(key []byte, created, modified reversion, ver int64)
2015-05-31 08:56:33 +03:00
Tombstone(key []byte, rev reversion) error
Compact(rev int64) map[reversion]struct{}
Equal(b index) bool
2015-05-22 18:11:43 +03:00
}
2015-05-15 03:53:41 +03:00
type treeIndex struct {
sync.RWMutex
tree *btree.BTree
}
func newTreeIndex() index {
return &treeIndex{
tree: btree.New(32),
}
}
2015-05-31 08:56:33 +03:00
func (ti *treeIndex) Put(key []byte, rev reversion) {
2015-05-15 03:53:41 +03:00
keyi := &keyIndex{key: key}
ti.Lock()
defer ti.Unlock()
item := ti.tree.Get(keyi)
if item == nil {
2015-05-31 08:56:33 +03:00
keyi.put(rev.main, rev.sub)
2015-05-15 03:53:41 +03:00
ti.tree.ReplaceOrInsert(keyi)
return
}
okeyi := item.(*keyIndex)
2015-05-31 08:56:33 +03:00
okeyi.put(rev.main, rev.sub)
2015-05-15 03:53:41 +03:00
}
func (ti *treeIndex) Restore(key []byte, created, modified reversion, ver int64) {
keyi := &keyIndex{key: key}
ti.Lock()
defer ti.Unlock()
item := ti.tree.Get(keyi)
if item == nil {
keyi.restore(created, modified, ver)
ti.tree.ReplaceOrInsert(keyi)
return
}
okeyi := item.(*keyIndex)
okeyi.put(modified.main, modified.sub)
}
2015-06-29 22:47:17 +03:00
func (ti *treeIndex) Get(key []byte, atRev int64) (modified, created reversion, ver int64, err error) {
2015-05-15 03:53:41 +03:00
keyi := &keyIndex{key: key}
ti.RLock()
defer ti.RUnlock()
item := ti.tree.Get(keyi)
if item == nil {
2015-06-29 22:47:17 +03:00
return reversion{}, reversion{}, 0, ErrReversionNotFound
2015-05-15 03:53:41 +03:00
}
keyi = item.(*keyIndex)
2015-05-31 08:56:33 +03:00
return keyi.get(atRev)
2015-05-15 03:53:41 +03:00
}
2015-05-31 08:56:33 +03:00
func (ti *treeIndex) Range(key, end []byte, atRev int64) (keys [][]byte, revs []reversion) {
2015-05-22 18:11:43 +03:00
if end == nil {
2015-06-29 22:47:17 +03:00
rev, _, _, err := ti.Get(key, atRev)
2015-05-22 18:11:43 +03:00
if err != nil {
2015-05-31 08:56:33 +03:00
return nil, nil
2015-05-22 18:11:43 +03:00
}
2015-05-31 08:56:33 +03:00
return [][]byte{key}, []reversion{rev}
2015-05-22 18:11:43 +03:00
}
keyi := &keyIndex{key: key}
endi := &keyIndex{key: end}
ti.RLock()
defer ti.RUnlock()
ti.tree.AscendGreaterOrEqual(keyi, func(item btree.Item) bool {
if !item.Less(endi) {
return false
}
curKeyi := item.(*keyIndex)
2015-06-29 22:47:17 +03:00
rev, _, _, err := curKeyi.get(atRev)
2015-05-22 18:11:43 +03:00
if err != nil {
return true
}
2015-05-31 08:56:33 +03:00
revs = append(revs, rev)
keys = append(keys, curKeyi.key)
2015-05-22 18:11:43 +03:00
return true
})
2015-05-31 08:56:33 +03:00
return keys, revs
2015-05-22 18:11:43 +03:00
}
2015-05-31 08:56:33 +03:00
func (ti *treeIndex) Tombstone(key []byte, rev reversion) error {
2015-05-15 03:53:41 +03:00
keyi := &keyIndex{key: key}
ti.Lock()
defer ti.Unlock()
item := ti.tree.Get(keyi)
if item == nil {
2015-05-31 08:56:33 +03:00
return ErrReversionNotFound
2015-05-15 03:53:41 +03:00
}
ki := item.(*keyIndex)
2015-05-31 08:56:33 +03:00
ki.tombstone(rev.main, rev.sub)
2015-05-15 03:53:41 +03:00
return nil
}
2015-05-31 08:56:33 +03:00
func (ti *treeIndex) Compact(rev int64) map[reversion]struct{} {
available := make(map[reversion]struct{})
2015-05-15 03:53:41 +03:00
emptyki := make([]*keyIndex, 0)
2015-05-31 08:56:33 +03:00
log.Printf("store.index: compact %d", rev)
2015-05-15 03:55:54 +03:00
// TODO: do not hold the lock for long time?
// This is probably OK. Compacting 10M keys takes O(10ms).
2015-05-15 03:53:41 +03:00
ti.Lock()
defer ti.Unlock()
2015-05-31 08:56:33 +03:00
ti.tree.Ascend(compactIndex(rev, available, &emptyki))
2015-05-15 03:53:41 +03:00
for _, ki := range emptyki {
item := ti.tree.Delete(ki)
if item == nil {
log.Panic("store.index: unexpected delete failure during compaction")
}
}
return available
}
2015-05-31 08:56:33 +03:00
func compactIndex(rev int64, available map[reversion]struct{}, emptyki *[]*keyIndex) func(i btree.Item) bool {
2015-05-15 03:53:41 +03:00
return func(i btree.Item) bool {
keyi := i.(*keyIndex)
2015-05-31 08:56:33 +03:00
keyi.compact(rev, available)
2015-05-15 03:53:41 +03:00
if keyi.isEmpty() {
*emptyki = append(*emptyki, keyi)
}
return true
}
}
func (a *treeIndex) Equal(bi index) bool {
b := bi.(*treeIndex)
if a.tree.Len() != b.tree.Len() {
return false
}
equal := true
a.tree.Ascend(func(item btree.Item) bool {
aki := item.(*keyIndex)
bki := b.tree.Get(item).(*keyIndex)
if !aki.equal(bki) {
equal = false
return false
}
return true
})
return equal
}