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

     1  //go:build kvdb_etcd
     2  // +build kvdb_etcd
     3  
     4  package etcd
     5  
     6  import (
     7  	"strconv"
     8  
     9  	"github.com/btcsuite/btcwallet/walletdb"
    10  )
    11  
    12  // readWriteBucket stores the bucket id and the buckets transaction.
    13  type readWriteBucket struct {
    14  	// id is used to identify the bucket and is created by
    15  	// hashing the parent id with the bucket key. For each key/value,
    16  	// sub-bucket or the bucket sequence the bucket id is used with the
    17  	// appropriate prefix to prefix the key.
    18  	id []byte
    19  
    20  	// tx holds the parent transaction.
    21  	tx *readWriteTx
    22  }
    23  
    24  // newReadWriteBucket creates a new rw bucket with the passed transaction
    25  // and bucket id.
    26  func newReadWriteBucket(tx *readWriteTx, key, id []byte) *readWriteBucket {
    27  	return &readWriteBucket{
    28  		id: id,
    29  		tx: tx,
    30  	}
    31  }
    32  
    33  // NestedReadBucket retrieves a nested read bucket with the given key.
    34  // Returns nil if the bucket does not exist.
    35  func (b *readWriteBucket) NestedReadBucket(key []byte) walletdb.ReadBucket {
    36  	return b.NestedReadWriteBucket(key)
    37  }
    38  
    39  // ForEach invokes the passed function with every key/value pair in
    40  // the bucket. This includes nested buckets, in which case the value
    41  // is nil, but it does not include the key/value pairs within those
    42  // nested buckets.
    43  func (b *readWriteBucket) ForEach(cb func(k, v []byte) error) error {
    44  	prefix := string(b.id)
    45  
    46  	// Get the first matching key that is in the bucket.
    47  	kv, err := b.tx.stm.First(prefix)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	for kv != nil {
    53  		key, val := getKeyVal(kv)
    54  
    55  		if err := cb(key, val); err != nil {
    56  			return err
    57  		}
    58  
    59  		// Step to the next key.
    60  		kv, err = b.tx.stm.Next(prefix, kv.key)
    61  		if err != nil {
    62  			return err
    63  		}
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  // Get returns the value for the given key. Returns nil if the key does
    70  // not exist in this bucket.
    71  func (b *readWriteBucket) Get(key []byte) []byte {
    72  	// Return nil if the key is empty.
    73  	if len(key) == 0 {
    74  		return nil
    75  	}
    76  
    77  	// Fetch the associated value.
    78  	val, err := b.tx.stm.Get(string(makeValueKey(b.id, key)))
    79  	if err != nil {
    80  		// TODO: we should return the error once the
    81  		// kvdb inteface is extended.
    82  		return nil
    83  	}
    84  
    85  	if val == nil {
    86  		return nil
    87  	}
    88  
    89  	return val
    90  }
    91  
    92  func (b *readWriteBucket) ReadCursor() walletdb.ReadCursor {
    93  	return newReadWriteCursor(b)
    94  }
    95  
    96  // NestedReadWriteBucket retrieves a nested bucket with the given key.
    97  // Returns nil if the bucket does not exist.
    98  func (b *readWriteBucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
    99  	if len(key) == 0 {
   100  		return nil
   101  	}
   102  
   103  	// Get the bucket id (and return nil if bucket doesn't exist).
   104  	bucketKey := makeBucketKey(b.id, key)
   105  	bucketVal, err := b.tx.stm.Get(string(bucketKey))
   106  	if err != nil {
   107  		// TODO: we should return the error once the
   108  		// kvdb inteface is extended.
   109  		return nil
   110  	}
   111  
   112  	if !isValidBucketID(bucketVal) {
   113  		return nil
   114  	}
   115  
   116  	// Return the bucket with the fetched bucket id.
   117  	return newReadWriteBucket(b.tx, bucketKey, bucketVal)
   118  }
   119  
   120  // assertNoValue checks if the value for the passed key exists.
   121  func (b *readWriteBucket) assertNoValue(key []byte) error {
   122  	if !etcdDebug {
   123  		return nil
   124  	}
   125  
   126  	val, err := b.tx.stm.Get(string(makeValueKey(b.id, key)))
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	if val != nil {
   132  		return walletdb.ErrIncompatibleValue
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // assertNoBucket checks if the bucket for the passed key exists.
   139  func (b *readWriteBucket) assertNoBucket(key []byte) error {
   140  	if !etcdDebug {
   141  		return nil
   142  	}
   143  
   144  	val, err := b.tx.stm.Get(string(makeBucketKey(b.id, key)))
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	if val != nil {
   150  		return walletdb.ErrIncompatibleValue
   151  	}
   152  
   153  	return nil
   154  }
   155  
   156  // CreateBucket creates and returns a new nested bucket with the given
   157  // key. Returns ErrBucketExists if the bucket already exists,
   158  // ErrBucketNameRequired if the key is empty, or ErrIncompatibleValue
   159  // if the key value is otherwise invalid for the particular database
   160  // implementation.  Other errors are possible depending on the
   161  // implementation.
   162  func (b *readWriteBucket) CreateBucket(key []byte) (
   163  	walletdb.ReadWriteBucket, error) {
   164  
   165  	if len(key) == 0 {
   166  		return nil, walletdb.ErrBucketNameRequired
   167  	}
   168  
   169  	// Check if the bucket already exists.
   170  	bucketKey := makeBucketKey(b.id, key)
   171  
   172  	bucketVal, err := b.tx.stm.Get(string(bucketKey))
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	if isValidBucketID(bucketVal) {
   178  		return nil, walletdb.ErrBucketExists
   179  	}
   180  
   181  	if err := b.assertNoValue(key); err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	// Create a deterministic bucket id from the bucket key.
   186  	newID := makeBucketID(bucketKey)
   187  
   188  	// Create the bucket.
   189  	b.tx.stm.Put(string(bucketKey), string(newID[:]))
   190  
   191  	return newReadWriteBucket(b.tx, bucketKey, newID[:]), nil
   192  }
   193  
   194  // CreateBucketIfNotExists creates and returns a new nested bucket with
   195  // the given key if it does not already exist.  Returns
   196  // ErrBucketNameRequired if the key is empty or ErrIncompatibleValue
   197  // if the key value is otherwise invalid for the particular database
   198  // backend.  Other errors are possible depending on the implementation.
   199  func (b *readWriteBucket) CreateBucketIfNotExists(key []byte) (
   200  	walletdb.ReadWriteBucket, error) {
   201  
   202  	if len(key) == 0 {
   203  		return nil, walletdb.ErrBucketNameRequired
   204  	}
   205  
   206  	// Check for the bucket and create if it doesn't exist.
   207  	bucketKey := makeBucketKey(b.id, key)
   208  
   209  	bucketVal, err := b.tx.stm.Get(string(bucketKey))
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	if !isValidBucketID(bucketVal) {
   215  		if err := b.assertNoValue(key); err != nil {
   216  			return nil, err
   217  		}
   218  
   219  		newID := makeBucketID(bucketKey)
   220  		b.tx.stm.Put(string(bucketKey), string(newID[:]))
   221  
   222  		return newReadWriteBucket(b.tx, bucketKey, newID[:]), nil
   223  	}
   224  
   225  	// Otherwise return the bucket with the fetched bucket id.
   226  	return newReadWriteBucket(b.tx, bucketKey, bucketVal), nil
   227  }
   228  
   229  // DeleteNestedBucket deletes the nested bucket and its sub-buckets
   230  // pointed to by the passed key. All values in the bucket and sub-buckets
   231  // will be deleted as well.
   232  func (b *readWriteBucket) DeleteNestedBucket(key []byte) error {
   233  	// TODO shouldn't empty key return ErrBucketNameRequired ?
   234  	if len(key) == 0 {
   235  		return walletdb.ErrIncompatibleValue
   236  	}
   237  
   238  	// Get the bucket first.
   239  	bucketKey := string(makeBucketKey(b.id, key))
   240  
   241  	bucketVal, err := b.tx.stm.Get(bucketKey)
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	if !isValidBucketID(bucketVal) {
   247  		return walletdb.ErrBucketNotFound
   248  	}
   249  
   250  	// Enqueue the top level bucket id.
   251  	queue := [][]byte{bucketVal}
   252  
   253  	// Traverse the buckets breadth first.
   254  	for len(queue) != 0 {
   255  		if !isValidBucketID(queue[0]) {
   256  			return walletdb.ErrBucketNotFound
   257  		}
   258  
   259  		id := queue[0]
   260  		queue = queue[1:]
   261  
   262  		kv, err := b.tx.stm.First(string(id))
   263  		if err != nil {
   264  			return err
   265  		}
   266  
   267  		for kv != nil {
   268  			b.tx.stm.Del(kv.key)
   269  
   270  			if isBucketKey(kv.key) {
   271  				queue = append(queue, []byte(kv.val))
   272  			}
   273  
   274  			kv, err = b.tx.stm.Next(string(id), kv.key)
   275  			if err != nil {
   276  				return err
   277  			}
   278  		}
   279  
   280  		// Finally delete the sequence key for the bucket.
   281  		b.tx.stm.Del(string(makeSequenceKey(id)))
   282  	}
   283  
   284  	// Delete the top level bucket and sequence key.
   285  	b.tx.stm.Del(bucketKey)
   286  	b.tx.stm.Del(string(makeSequenceKey(bucketVal)))
   287  
   288  	return nil
   289  }
   290  
   291  // Put updates the value for the passed key.
   292  // Returns ErrKeyRequred if te passed key is empty.
   293  func (b *readWriteBucket) Put(key, value []byte) error {
   294  	if len(key) == 0 {
   295  		return walletdb.ErrKeyRequired
   296  	}
   297  
   298  	if err := b.assertNoBucket(key); err != nil {
   299  		return err
   300  	}
   301  
   302  	// Update the transaction with the new value.
   303  	b.tx.stm.Put(string(makeValueKey(b.id, key)), string(value))
   304  
   305  	return nil
   306  }
   307  
   308  // Delete deletes the key/value pointed to by the passed key.
   309  // Returns ErrKeyRequred if the passed key is empty.
   310  func (b *readWriteBucket) Delete(key []byte) error {
   311  	if key == nil {
   312  		return nil
   313  	}
   314  	if len(key) == 0 {
   315  		return walletdb.ErrKeyRequired
   316  	}
   317  
   318  	// Update the transaction to delete the key/value.
   319  	b.tx.stm.Del(string(makeValueKey(b.id, key)))
   320  
   321  	return nil
   322  }
   323  
   324  // ReadWriteCursor returns a new read-write cursor for this bucket.
   325  func (b *readWriteBucket) ReadWriteCursor() walletdb.ReadWriteCursor {
   326  	return newReadWriteCursor(b)
   327  }
   328  
   329  // Tx returns the buckets transaction.
   330  func (b *readWriteBucket) Tx() walletdb.ReadWriteTx {
   331  	return b.tx
   332  }
   333  
   334  // NextSequence returns an autoincrementing sequence number for this bucket.
   335  // Note that this is not a thread safe function and as such it must not be used
   336  // for synchronization.
   337  func (b *readWriteBucket) NextSequence() (uint64, error) {
   338  	seq := b.Sequence() + 1
   339  
   340  	return seq, b.SetSequence(seq)
   341  }
   342  
   343  // SetSequence updates the sequence number for the bucket.
   344  func (b *readWriteBucket) SetSequence(v uint64) error {
   345  	// Convert the number to string.
   346  	val := strconv.FormatUint(v, 10)
   347  
   348  	// Update the transaction with the new value for the sequence key.
   349  	b.tx.stm.Put(string(makeSequenceKey(b.id)), val)
   350  
   351  	return nil
   352  }
   353  
   354  // Sequence returns the current sequence number for this bucket without
   355  // incrementing it.
   356  func (b *readWriteBucket) Sequence() uint64 {
   357  	val, err := b.tx.stm.Get(string(makeSequenceKey(b.id)))
   358  	if err != nil {
   359  		// TODO: This update kvdb interface such that error
   360  		// may be returned here.
   361  		return 0
   362  	}
   363  
   364  	if val == nil {
   365  		// If the sequence number is not yet
   366  		// stored, then take the default value.
   367  		return 0
   368  	}
   369  
   370  	// Otherwise try to parse a 64 bit unsigned integer from the value.
   371  	num, _ := strconv.ParseUint(string(val), 10, 64)
   372  
   373  	return num
   374  }
   375  
   376  func flattenMap(m map[string]struct{}) []string {
   377  	result := make([]string, len(m))
   378  	i := 0
   379  
   380  	for key := range m {
   381  		result[i] = key
   382  		i++
   383  	}
   384  
   385  	return result
   386  }
   387  
   388  // Prefetch will prefetch all keys in the passed paths as well as all bucket
   389  // keys along the paths.
   390  func (b *readWriteBucket) Prefetch(paths ...[]string) {
   391  	keys := make(map[string]struct{})
   392  	ranges := make(map[string]struct{})
   393  
   394  	for _, path := range paths {
   395  		parent := b.id
   396  		for _, bucket := range path {
   397  			bucketKey := makeBucketKey(parent, []byte(bucket))
   398  			keys[string(bucketKey[:])] = struct{}{}
   399  
   400  			id := makeBucketID(bucketKey)
   401  			parent = id[:]
   402  		}
   403  
   404  		ranges[string(parent)] = struct{}{}
   405  	}
   406  
   407  	b.tx.stm.Prefetch(flattenMap(keys), flattenMap(ranges))
   408  }
   409  
   410  // ForAll is an optimized version of ForEach with the limitation that no
   411  // additional queries can be executed within the callback.
   412  func (b *readWriteBucket) ForAll(cb func(k, v []byte) error) error {
   413  	return b.ForEach(cb)
   414  }