storage: address barak's comments
This commit is contained in:
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user