ledisdb/ledis/tx.go

210 lines
3.2 KiB
Go
Raw Normal View History

2014-05-09 10:49:22 +04:00
package ledis
2014-05-04 15:02:55 +04:00
import (
2014-08-25 10:18:23 +04:00
"errors"
2014-07-25 13:58:00 +04:00
"github.com/siddontang/ledisdb/store"
2014-05-06 09:32:38 +04:00
"sync"
2014-05-04 15:02:55 +04:00
)
2014-08-25 10:18:23 +04:00
var (
ErrNestTx = errors.New("nest transaction not supported")
ErrTxDone = errors.New("Transaction has already been committed or rolled back")
)
type batch struct {
l *Ledis
store.WriteBatch
sync.Locker
logs [][]byte
tx *Tx
}
2014-05-06 09:32:38 +04:00
2014-08-25 10:18:23 +04:00
type dbBatchLocker struct {
2014-08-30 13:39:44 +04:00
l *sync.Mutex
wrLock *sync.RWMutex
2014-08-25 10:18:23 +04:00
}
2014-05-27 12:05:24 +04:00
2014-08-30 13:39:44 +04:00
func (l *dbBatchLocker) Lock() {
l.wrLock.RLock()
l.l.Lock()
2014-08-25 10:18:23 +04:00
}
2014-05-27 12:05:24 +04:00
2014-08-30 13:39:44 +04:00
func (l *dbBatchLocker) Unlock() {
l.l.Unlock()
l.wrLock.RUnlock()
2014-08-25 10:18:23 +04:00
}
2014-05-27 12:05:24 +04:00
2014-08-30 13:39:44 +04:00
type txBatchLocker struct {
2014-05-04 15:02:55 +04:00
}
2014-08-30 13:39:44 +04:00
func (l *txBatchLocker) Lock() {}
func (l *txBatchLocker) Unlock() {}
2014-05-06 09:32:38 +04:00
2014-08-30 13:39:44 +04:00
func (l *Ledis) newBatch(wb store.WriteBatch, tx *Tx) *batch {
2014-08-25 10:18:23 +04:00
b := new(batch)
2014-08-30 13:39:44 +04:00
b.l = l
b.WriteBatch = wb
2014-05-27 12:05:24 +04:00
2014-08-30 13:39:44 +04:00
b.tx = tx
if tx == nil {
b.Locker = &dbBatchLocker{l: &sync.Mutex{}, wrLock: &l.wLock}
} else {
b.Locker = &txBatchLocker{}
}
2014-08-25 10:18:23 +04:00
2014-08-30 13:39:44 +04:00
b.logs = [][]byte{}
2014-08-25 10:18:23 +04:00
return b
}
2014-08-30 13:39:44 +04:00
func (db *DB) newBatch() *batch {
return db.l.newBatch(db.bucket.NewWriteBatch(), nil)
}
2014-08-25 10:18:23 +04:00
func (b *batch) Commit() error {
2014-08-30 13:39:44 +04:00
b.l.commitLock.Lock()
defer b.l.commitLock.Unlock()
2014-08-25 10:18:23 +04:00
err := b.WriteBatch.Commit()
if b.l.binlog != nil {
if err == nil {
if b.tx == nil {
b.l.binlog.Log(b.logs...)
} else {
b.tx.logs = append(b.tx.logs, b.logs...)
}
}
b.logs = [][]byte{}
}
return err
}
func (b *batch) Lock() {
b.Locker.Lock()
}
func (b *batch) Unlock() {
if b.l.binlog != nil {
b.logs = [][]byte{}
2014-05-27 12:05:24 +04:00
}
2014-08-30 13:39:44 +04:00
b.WriteBatch.Rollback()
2014-08-25 10:18:23 +04:00
b.Locker.Unlock()
2014-05-04 15:02:55 +04:00
}
2014-08-25 10:18:23 +04:00
func (b *batch) Put(key []byte, value []byte) {
if b.l.binlog != nil {
buf := encodeBinLogPut(key, value)
b.logs = append(b.logs, buf)
}
b.WriteBatch.Put(key, value)
}
2014-05-27 12:05:24 +04:00
2014-08-25 10:18:23 +04:00
func (b *batch) Delete(key []byte) {
if b.l.binlog != nil {
2014-05-29 11:07:14 +04:00
buf := encodeBinLogDelete(key)
2014-08-25 10:18:23 +04:00
b.logs = append(b.logs, buf)
2014-05-27 12:05:24 +04:00
}
2014-08-25 10:18:23 +04:00
b.WriteBatch.Delete(key)
2014-05-04 15:02:55 +04:00
}
2014-08-25 10:18:23 +04:00
type Tx struct {
*DB
tx *store.Tx
logs [][]byte
2014-08-25 12:19:28 +04:00
index uint8
2014-05-06 09:32:38 +04:00
}
2014-08-25 10:18:23 +04:00
func (db *DB) IsTransaction() bool {
return db.isTx
2014-05-06 09:32:38 +04:00
}
2014-08-25 10:18:23 +04:00
// Begin a transaction, it will block all other write operations before calling Commit or Rollback.
// You must be very careful to prevent long-time transaction.
func (db *DB) Begin() (*Tx, error) {
if db.isTx {
return nil, ErrNestTx
}
tx := new(Tx)
tx.DB = new(DB)
2014-08-30 13:39:44 +04:00
tx.DB.l = db.l
2014-08-25 10:18:23 +04:00
2014-08-30 13:39:44 +04:00
tx.l.wLock.Lock()
2014-08-25 10:18:23 +04:00
2014-08-25 12:19:28 +04:00
tx.index = db.index
2014-08-25 10:18:23 +04:00
tx.DB.sdb = db.sdb
2014-05-27 12:05:24 +04:00
var err error
2014-08-25 10:18:23 +04:00
tx.tx, err = db.sdb.Begin()
if err != nil {
2014-08-30 13:39:44 +04:00
tx.l.wLock.Unlock()
2014-08-25 10:18:23 +04:00
return nil, err
}
tx.DB.bucket = tx.tx
tx.DB.isTx = true
tx.DB.index = db.index
2014-08-30 13:39:44 +04:00
tx.DB.kvBatch = tx.newTxBatch()
tx.DB.listBatch = tx.newTxBatch()
tx.DB.hashBatch = tx.newTxBatch()
tx.DB.zsetBatch = tx.newTxBatch()
tx.DB.binBatch = tx.newTxBatch()
tx.DB.setBatch = tx.newTxBatch()
2014-05-27 12:05:24 +04:00
2014-08-25 10:18:23 +04:00
return tx, nil
}
func (tx *Tx) Commit() error {
if tx.tx == nil {
return ErrTxDone
}
2014-08-30 13:39:44 +04:00
tx.l.commitLock.Lock()
2014-08-25 10:18:23 +04:00
err := tx.tx.Commit()
tx.tx = nil
if len(tx.logs) > 0 {
tx.l.binlog.Log(tx.logs...)
}
2014-08-30 13:39:44 +04:00
tx.l.commitLock.Unlock()
2014-05-27 12:05:24 +04:00
2014-08-30 13:39:44 +04:00
tx.l.wLock.Unlock()
2014-08-25 10:18:23 +04:00
tx.DB = nil
return err
}
func (tx *Tx) Rollback() error {
if tx.tx == nil {
return ErrTxDone
2014-05-27 12:05:24 +04:00
}
2014-08-25 10:18:23 +04:00
err := tx.tx.Rollback()
tx.tx = nil
2014-08-30 13:39:44 +04:00
tx.l.wLock.Unlock()
2014-08-25 10:18:23 +04:00
tx.DB = nil
2014-05-04 15:02:55 +04:00
return err
}
2014-08-30 13:39:44 +04:00
func (tx *Tx) newTxBatch() *batch {
return tx.l.newBatch(tx.tx.NewWriteBatch(), tx)
2014-05-04 15:02:55 +04:00
}
2014-08-25 12:19:28 +04:00
func (tx *Tx) Index() int {
return int(tx.index)
}