lnd.xprv/channeldb/kvdb/etcd/bucket.go
Andras Banki-Horvath 63e9d6102f
kvdb+etcd: change flattened bucket key derivation algorithm
This commit changes the key derivation algo we use to emulate buckets
similar to bbolt. The issue with prefixing keys with either a bucket or
a value prefix is that the cursor couldn't effectively iterate trough
all keys in a bucket, as it skipped the bucket keys.
While there are multiple ways to fix that issue (eg. two pointers,
iterating value keys then bucket keys, etc), the cleanest is to instead
of prefixes in keys we use a postfix indicating whether a key is a
bucket or a value. This also simplifies all operations where we
(recursively) iterate a bucket and is equivalent with the prefixing key
derivation with the addition that bucket and value keys are now
continous.
2020-07-28 17:57:29 +02:00

93 lines
2.5 KiB
Go

// +build kvdb_etcd
package etcd
import (
"crypto/sha256"
)
const (
bucketIDLength = 32
)
var (
valuePostfix = []byte{0x00}
bucketPostfix = []byte{0xFF}
sequencePrefix = []byte("$seq$")
)
// makeBucketID returns a deterministic key for the passed byte slice.
// Currently it returns the sha256 hash of the slice.
func makeBucketID(key []byte) [bucketIDLength]byte {
return sha256.Sum256(key)
}
// isValidBucketID checks if the passed slice is the required length to be a
// valid bucket id.
func isValidBucketID(s []byte) bool {
return len(s) == bucketIDLength
}
// makeKey concatenates parent, key and postfix into one byte slice.
// The postfix indicates the use of this key (whether bucket or value), while
// parent refers to the parent bucket.
func makeKey(parent, key, postfix []byte) []byte {
keyBuf := make([]byte, len(parent)+len(key)+len(postfix))
copy(keyBuf, parent)
copy(keyBuf[len(parent):], key)
copy(keyBuf[len(parent)+len(key):], postfix)
return keyBuf
}
// makeBucketKey returns a bucket key from the passed parent bucket id and
// the key.
func makeBucketKey(parent []byte, key []byte) []byte {
return makeKey(parent, key, bucketPostfix)
}
// makeValueKey returns a value key from the passed parent bucket id and
// the key.
func makeValueKey(parent []byte, key []byte) []byte {
return makeKey(parent, key, valuePostfix)
}
// makeSequenceKey returns a sequence key of the passed parent bucket id.
func makeSequenceKey(parent []byte) []byte {
keyBuf := make([]byte, len(sequencePrefix)+len(parent))
copy(keyBuf, sequencePrefix)
copy(keyBuf[len(sequencePrefix):], parent)
return keyBuf
}
// isBucketKey returns true if the passed key is a bucket key, meaning it
// keys a bucket name.
func isBucketKey(key string) bool {
if len(key) < bucketIDLength+1 {
return false
}
return key[len(key)-1] == bucketPostfix[0]
}
// getKey chops out the key from the raw key (by removing the bucket id
// prefixing the key and the postfix indicating whether it is a bucket or
// a value key)
func getKey(rawKey string) []byte {
return []byte(rawKey[bucketIDLength : len(rawKey)-1])
}
// getKeyVal chops out the key from the raw key (by removing the bucket id
// prefixing the key and the postfix indicating whether it is a bucket or
// a value key) and also returns the appropriate value for the key, which is
// nil in case of buckets (or the set value otherwise).
func getKeyVal(kv *KV) ([]byte, []byte) {
var val []byte
if !isBucketKey(kv.key) {
val = []byte(kv.val)
}
return getKey(kv.key), val
}