package nodb import ( "errors" "fmt" "gitea.com/lunny/nodb/store" ) var ( ErrNestTx = errors.New("nest transaction not supported") ErrTxDone = errors.New("Transaction has already been committed or rolled back") ) type Tx struct { *DB tx *store.Tx logs [][]byte } func (db *DB) IsTransaction() bool { return db.status == DBInTransaction } // 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.IsTransaction() { return nil, ErrNestTx } tx := new(Tx) tx.DB = new(DB) tx.DB.l = db.l tx.l.wLock.Lock() tx.DB.sdb = db.sdb var err error tx.tx, err = db.sdb.Begin() if err != nil { tx.l.wLock.Unlock() return nil, err } tx.DB.bucket = tx.tx tx.DB.status = DBInTransaction tx.DB.index = db.index tx.DB.kvBatch = tx.newBatch() tx.DB.listBatch = tx.newBatch() tx.DB.hashBatch = tx.newBatch() tx.DB.zsetBatch = tx.newBatch() tx.DB.binBatch = tx.newBatch() tx.DB.setBatch = tx.newBatch() return tx, nil } func (tx *Tx) Commit() error { if tx.tx == nil { return ErrTxDone } tx.l.commitLock.Lock() err := tx.tx.Commit() tx.tx = nil if len(tx.logs) > 0 { tx.l.binlog.Log(tx.logs...) } tx.l.commitLock.Unlock() tx.l.wLock.Unlock() tx.DB.bucket = nil return err } func (tx *Tx) Rollback() error { if tx.tx == nil { return ErrTxDone } err := tx.tx.Rollback() tx.tx = nil tx.l.wLock.Unlock() tx.DB.bucket = nil return err } func (tx *Tx) newBatch() *batch { return tx.l.newBatch(tx.tx.NewWriteBatch(), &txBatchLocker{}, tx) } func (tx *Tx) Select(index int) error { if index < 0 || index >= int(MaxDBNumber) { return fmt.Errorf("invalid db index %d", index) } tx.DB.index = uint8(index) return nil }