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 }