storage: address barak's comments

This commit is contained in:
Xiang Li
2015-05-20 17:32:53 -07:00
parent 845cb61213
commit e332e86b5d
5 changed files with 65 additions and 86 deletions

View File

@ -57,17 +57,13 @@ func (b *backend) BatchTx() BatchTx {
// force commit the current batching tx. // force commit the current batching tx.
func (b *backend) ForceCommit() { func (b *backend) ForceCommit() {
b.batchTx.Lock() b.batchTx.Commit()
b.commitAndBegin()
b.batchTx.Unlock()
} }
func (b *backend) run() { func (b *backend) run() {
defer close(b.donec) defer close(b.donec)
b.batchTx.Lock() b.batchTx.Commit()
b.commitAndBegin()
b.batchTx.Unlock()
b.startc <- struct{}{} b.startc <- struct{}{}
for { for {
@ -76,9 +72,7 @@ func (b *backend) run() {
case <-b.stopc: case <-b.stopc:
return return
} }
b.batchTx.Lock() b.batchTx.Commit()
b.commitAndBegin()
b.batchTx.Unlock()
} }
} }
@ -87,21 +81,3 @@ func (b *backend) Close() error {
<-b.donec <-b.donec
return b.db.Close() return b.db.Close()
} }
// commitAndBegin commits a previous tx and begins a new writable one.
func (b *backend) commitAndBegin() {
var err error
// commit the last batchTx
if b.batchTx.tx != nil {
err = b.batchTx.tx.Commit()
if err != nil {
log.Fatalf("storage: cannot commit tx (%s)", err)
}
}
// begin a new tx
b.batchTx.tx, err = b.db.Begin(true)
if err != nil {
log.Fatalf("storage: cannot begin tx (%s)", err)
}
}

View File

@ -27,35 +27,3 @@ func TestBackendPut(t *testing.T) {
batchTx.Unlock() batchTx.Unlock()
} }
func TestBackendForceCommit(t *testing.T) {
backend := New("test", 10*time.Second, 10000)
defer backend.Close()
defer os.Remove("test")
v := []byte("foo")
batchTx := backend.BatchTx()
batchTx.Lock()
batchTx.UnsafeCreateBucket([]byte("test"))
batchTx.UnsafePut([]byte("test"), []byte("foo"), v)
batchTx.Unlock()
// expect to see nothing that the batch tx created
tx := backend.ReadTnx()
gbucket := tx.Bucket([]byte("test"))
if gbucket != nil {
t.Errorf("readtx.bu = %p, want nil", gbucket)
}
tx.Commit()
// commit batch tx
backend.ForceCommit()
tx = backend.ReadTnx()
gbucket = tx.Bucket([]byte("test"))
if gbucket == nil {
t.Errorf("readtx.bu = nil, want not nil")
}
}

View File

@ -15,23 +15,16 @@ type BatchTx interface {
UnsafePut(bucketName []byte, key []byte, value []byte) UnsafePut(bucketName []byte, key []byte, value []byte)
UnsafeRange(bucketName []byte, key, endKey []byte, limit int64) [][]byte UnsafeRange(bucketName []byte, key, endKey []byte, limit int64) [][]byte
UnsafeDelete(bucketName []byte, key []byte) UnsafeDelete(bucketName []byte, key []byte)
Commit()
} }
type batchTx struct { type batchTx struct {
mu sync.Mutex sync.Mutex
tx *bolt.Tx tx *bolt.Tx
backend *backend backend *backend
pending int pending int
} }
func (t *batchTx) Lock() {
t.mu.Lock()
}
func (t *batchTx) Unlock() {
t.mu.Unlock()
}
func (t *batchTx) UnsafeCreateBucket(name []byte) { func (t *batchTx) UnsafeCreateBucket(name []byte) {
_, err := t.tx.CreateBucket(name) _, err := t.tx.CreateBucket(name)
if err != nil && err != bolt.ErrBucketExists { if err != nil && err != bolt.ErrBucketExists {
@ -50,7 +43,7 @@ func (t *batchTx) UnsafePut(bucketName []byte, key []byte, value []byte) {
} }
t.pending++ t.pending++
if t.pending > t.backend.batchLimit { if t.pending > t.backend.batchLimit {
t.backend.commitAndBegin() t.Commit()
t.pending = 0 t.pending = 0
} }
} }
@ -92,7 +85,28 @@ func (t *batchTx) UnsafeDelete(bucketName []byte, key []byte) {
} }
t.pending++ t.pending++
if t.pending > t.backend.batchLimit { if t.pending > t.backend.batchLimit {
t.backend.commitAndBegin() t.Commit()
t.pending = 0 t.pending = 0
} }
} }
// commitAndBegin commits a previous tx and begins a new writable one.
func (t *batchTx) Commit() {
t.Lock()
defer t.Unlock()
var err error
// commit the last tx
if t.tx != nil {
err = t.tx.Commit()
if err != nil {
log.Fatalf("storage: cannot commit tx (%s)", err)
}
}
// begin a new tx
t.tx, err = t.backend.db.Begin(true)
if err != nil {
log.Fatalf("storage: cannot begin tx (%s)", err)
}
}

View File

@ -186,6 +186,11 @@ type generation struct {
func (g *generation) isEmpty() bool { return len(g.cont) == 0 } func (g *generation) isEmpty() bool { return len(g.cont) == 0 }
// walk walks through the (index, version) pairs in the generation in ascending order.
// It passes the (index, version) to the given function.
// walk returns until: 1. it finishs walking all pairs 2. the function returns false.
// walk returns the (index, version) pair at where it stopped. If it stopped after
// finishing walking, (0, -1) will be returned.
func (g *generation) walk(f func(index, ver uint64) bool) (uint64, int) { func (g *generation) walk(f func(index, ver uint64) bool) (uint64, int) {
ver := g.ver ver := g.ver
l := len(g.cont) l := len(g.cont)

View File

@ -3,6 +3,7 @@ package storage
import ( import (
"encoding/binary" "encoding/binary"
"log" "log"
"sync"
"time" "time"
"github.com/coreos/etcd/storage/backend" "github.com/coreos/etcd/storage/backend"
@ -16,19 +17,23 @@ var (
) )
type store struct { type store struct {
// read operation MUST hold read lock
// write opeartion MUST hold write lock
sync.RWMutex
b backend.Backend b backend.Backend
kvindex index kvindex index
now uint64 // current index of the store currentIndex uint64
marshalBuf []byte // buffer for marshal protobuf marshalBuf []byte // buffer for marshal protobuf
} }
func newStore(path string) *store { func newStore(path string) *store {
s := &store{ s := &store{
b: backend.New(path, batchInterval, batchLimit), b: backend.New(path, batchInterval, batchLimit),
kvindex: newTreeIndex(), kvindex: newTreeIndex(),
now: 0, currentIndex: 0,
marshalBuf: make([]byte, 1024*1024), marshalBuf: make([]byte, 1024*1024),
} }
tx := s.b.BatchTx() tx := s.b.BatchTx()
@ -41,16 +46,18 @@ func newStore(path string) *store {
} }
func (s *store) Put(key, value []byte) { func (s *store) Put(key, value []byte) {
now := s.now + 1 s.Lock()
defer s.Unlock()
currentIndex := s.currentIndex + 1
s.kvindex.Put(key, now)
ibytes := make([]byte, 8) ibytes := make([]byte, 8)
binary.BigEndian.PutUint64(ibytes, now) binary.BigEndian.PutUint64(ibytes, currentIndex)
tx := s.b.BatchTx() tx := s.b.BatchTx()
tx.Lock() tx.Lock()
defer tx.Unlock() defer tx.Unlock()
s.now = now s.currentIndex = currentIndex
event := storagepb.Event{ event := storagepb.Event{
Type: storagepb.PUT, Type: storagepb.PUT,
@ -77,10 +84,15 @@ func (s *store) Put(key, value []byte) {
} }
tx.UnsafePut(keyBucketName, ibytes, d) tx.UnsafePut(keyBucketName, ibytes, d)
s.kvindex.Put(key, currentIndex)
} }
func (s *store) Get(key []byte) []byte { func (s *store) Get(key []byte) []byte {
index, err := s.kvindex.Get(key, s.now) s.RLock()
defer s.RUnlock()
index, err := s.kvindex.Get(key, s.currentIndex)
if err != nil { if err != nil {
return nil return nil
} }
@ -97,20 +109,24 @@ func (s *store) Get(key []byte) []byte {
} }
func (s *store) Delete(key []byte) error { func (s *store) Delete(key []byte) error {
now := s.now + 1 s.Lock()
defer s.Unlock()
err := s.kvindex.Tombstone(key, now) _, err := s.kvindex.Get(key, s.currentIndex)
if err != nil { if err != nil {
return err return nil
} }
currentIndex := s.currentIndex + 1
ibytes := make([]byte, 8) ibytes := make([]byte, 8)
binary.BigEndian.PutUint64(ibytes, now) binary.BigEndian.PutUint64(ibytes, currentIndex)
tx := s.b.BatchTx() tx := s.b.BatchTx()
tx.Lock() tx.Lock()
defer tx.Unlock() defer tx.Unlock()
// TODO: the value will be an event type. // TODO: the value will be an event type.
// A tombstone is simple a "Delete" type event. // A tombstone is simple a "Delete" type event.
tx.UnsafePut(keyBucketName, key, []byte("tombstone")) tx.UnsafePut(keyBucketName, key, []byte("tombstone"))
return nil
return s.kvindex.Tombstone(key, currentIndex)
} }