github.com/decred/dcrlnd@v0.7.6/kvdb/etcd/readwrite_tx.go (about)

     1  //go:build kvdb_etcd
     2  // +build kvdb_etcd
     3  
     4  package etcd
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/btcsuite/btcwallet/walletdb"
    10  )
    11  
    12  // readWriteTx holds a reference to the STM transaction.
    13  type readWriteTx struct {
    14  	// stm is the reference to the parent STM.
    15  	stm STM
    16  
    17  	// rootBucketID holds the sha256 hash of the root bucket id, which is used
    18  	// for key space spearation.
    19  	rootBucketID [bucketIDLength]byte
    20  
    21  	// active is true if the transaction hasn't been committed yet.
    22  	active bool
    23  
    24  	// lock is passed on for manual txns when the backend is instantiated
    25  	// such that we read/write lock transactions to ensure a single writer.
    26  	lock sync.Locker
    27  }
    28  
    29  // newReadWriteTx creates an rw transaction with the passed STM.
    30  func newReadWriteTx(stm STM, prefix string, lock sync.Locker) *readWriteTx {
    31  	return &readWriteTx{
    32  		stm:          stm,
    33  		active:       true,
    34  		lock:         lock,
    35  		rootBucketID: makeBucketID([]byte(prefix)),
    36  	}
    37  }
    38  
    39  // rooBucket is a helper function to return the always present
    40  // pseudo root bucket.
    41  func rootBucket(tx *readWriteTx) *readWriteBucket {
    42  	return newReadWriteBucket(tx, tx.rootBucketID[:], tx.rootBucketID[:])
    43  }
    44  
    45  // RootBucket will return a handle to the root bucket. This is not a real handle
    46  // but just a wrapper around the root bucket ID to allow derivation of child
    47  // keys.
    48  func (tx *readWriteTx) RootBucket() walletdb.ReadBucket {
    49  	return rootBucket(tx)
    50  }
    51  
    52  // ReadBucket opens the root bucket for read only access.  If the bucket
    53  // described by the key does not exist, nil is returned.
    54  func (tx *readWriteTx) ReadBucket(key []byte) walletdb.ReadBucket {
    55  	return rootBucket(tx).NestedReadWriteBucket(key)
    56  }
    57  
    58  // ForEachBucket iterates through all top level buckets.
    59  func (tx *readWriteTx) ForEachBucket(fn func(key []byte) error) error {
    60  	root := rootBucket(tx)
    61  	// We can safely use ForEach here since on the top level there are
    62  	// no values, only buckets.
    63  	return root.ForEach(func(key []byte, val []byte) error {
    64  		if val != nil {
    65  			// A non-nil value would mean that we have a non
    66  			// walletdb/kvdb compatibel database containing
    67  			// arbitrary key/values.
    68  			return walletdb.ErrInvalid
    69  
    70  		}
    71  
    72  		return fn(key)
    73  	})
    74  }
    75  
    76  // Rollback closes the transaction, discarding changes (if any) if the
    77  // database was modified by a write transaction.
    78  func (tx *readWriteTx) Rollback() error {
    79  	// If the transaction has been closed roolback will fail.
    80  	if !tx.active {
    81  		return walletdb.ErrTxClosed
    82  	}
    83  
    84  	if tx.lock != nil {
    85  		defer tx.lock.Unlock()
    86  	}
    87  
    88  	// Rollback the STM and set the tx to inactive.
    89  	tx.stm.Rollback()
    90  	tx.active = false
    91  
    92  	return nil
    93  }
    94  
    95  // ReadWriteBucket opens the root bucket for read/write access.  If the
    96  // bucket described by the key does not exist, nil is returned.
    97  func (tx *readWriteTx) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
    98  	return rootBucket(tx).NestedReadWriteBucket(key)
    99  }
   100  
   101  // CreateTopLevelBucket creates the top level bucket for a key if it
   102  // does not exist.  The newly-created bucket it returned.
   103  func (tx *readWriteTx) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) {
   104  	return rootBucket(tx).CreateBucketIfNotExists(key)
   105  }
   106  
   107  // DeleteTopLevelBucket deletes the top level bucket for a key.  This
   108  // errors if the bucket can not be found or the key keys a single value
   109  // instead of a bucket.
   110  func (tx *readWriteTx) DeleteTopLevelBucket(key []byte) error {
   111  	return rootBucket(tx).DeleteNestedBucket(key)
   112  }
   113  
   114  // Commit commits the transaction if not already committed. Will return
   115  // error if the underlying STM fails.
   116  func (tx *readWriteTx) Commit() error {
   117  	// Commit will fail if the transaction is already committed.
   118  	if !tx.active {
   119  		return walletdb.ErrTxClosed
   120  	}
   121  
   122  	if tx.lock != nil {
   123  		defer tx.lock.Unlock()
   124  	}
   125  
   126  	// Try committing the transaction.
   127  	if err := tx.stm.Commit(); err != nil {
   128  		return err
   129  	}
   130  
   131  	// Mark the transaction as not active after commit.
   132  	tx.active = false
   133  
   134  	return nil
   135  }
   136  
   137  // OnCommit sets the commit callback (overriding if already set).
   138  func (tx *readWriteTx) OnCommit(cb func()) {
   139  	tx.stm.OnCommit(cb)
   140  }