Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/tendermint/tm-db
go 1.12

require (
github.com/coreos/etcd v3.3.10+incompatible
github.com/dgraph-io/badger/v2 v2.2007.2
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
Expand Down
105 changes: 105 additions & 0 deletions transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package db

import (
"errors"
)

var errFailedAtomicCheck = errors.New("Failed to atomically write to the database rollback completed")
var errFailedToDeleteOnRollback = errors.New("Failed to delete during transaction rollback")
var errDataBaseNotImplemented = errors.New("Database given is not implemented")
var errFailedToTransact = errors.New("Failed to transact")

type T interface {
Transact(DB) error
}

// Transaction represents one transaction that abides by ACID properties
type Transaction struct {
state int // the amount of transactions that have been completed
txs []func() error // txs is a list of all txs to execute for this transaction
keys [][]byte // is a list of the keys that have been used during this transaction

atomic bool
}

func NewTransaction(db DB) Transaction {
return Transaction{state: 0, txs: make([]func() error, 0), keys: make([][]byte, 0), atomic: needsAtomic(db)}
}

func (t Transaction) Append(tx func() error, k []byte) {
t.txs = append(t.txs, tx)
t.saveKey(k)
}

// saveKey saves a key incase deletion needs to occur
func (t Transaction) saveKey(k []byte) {
t.keys = append(t.keys, k)
}

func (t Transaction) Transact(db DB) error {
if !t.atomic {
return t.transact(db)
}

return t.transactAtomic(db)
}

// transact transacts
func (t Transaction) transact(db DB) error {
for i := range t.txs {
if err := t.txs[i](); err != nil {
return errFailedToTransact
}

return nil
}

// shouldn't happen
return nil
}

// transactAtomic transacts atomically
func (t Transaction) transactAtomic(db DB) error {
for i := range t.txs {
if err := t.txs[i](); err != nil {
t.rollBack(db)
return errFailedAtomicCheck
}

// increment the state so if a rollback occurs the transaction knows how many times to roll back
t.state++
}

return nil
}

// rollBack rolls the database back to what the database was before this transaction
func (t Transaction) rollBack(db DB) {
// do use atomic Transaction features if the database is already atomic
if !t.atomic {
return
}

for i := t.state; t.state > 0 || t.state == 0; t.state-- {
if err := db.Delete(t.keys[i]); err != nil {
panic(err)
}

if t.state == 0 {
return
}
}
}

// needsAtomic checks if the database provided needs bolted on atomicity
func needsAtomic(db DB) bool {
switch db.(type) {
case *BoltDB, ClevelDB, *PrefixDB, BadgerDB:
return true
case *GoLevelDB:
// atomic already set to false but explicility define here for readability
return false
default:
panic(errDataBaseNotImplemented)
}
}