github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/kv/local/store.go (about) 1 package local 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "time" 8 9 "github.com/dgraph-io/badger/v4" 10 "github.com/treeverse/lakefs/pkg/kv" 11 "github.com/treeverse/lakefs/pkg/logging" 12 ) 13 14 func partitionRange(partitionKey []byte) []byte { 15 result := make([]byte, len(partitionKey)+1) 16 copy(result, partitionKey) 17 result[len(result)-1] = kv.PathDelimiter[0] 18 return result 19 } 20 21 func composeKey(partitionKey, key []byte) []byte { 22 pr := partitionRange(partitionKey) 23 result := make([]byte, len(pr)+len(key)) 24 copy(result, pr) 25 copy(result[len(pr):], key) 26 return result 27 } 28 29 type Store struct { 30 db *badger.DB 31 logger logging.Logger 32 prefetchSize int 33 refCount int 34 path string 35 } 36 37 func (s *Store) Get(ctx context.Context, partitionKey, key []byte) (*kv.ValueWithPredicate, error) { 38 k := composeKey(partitionKey, key) 39 start := time.Now() 40 log := s.logger.WithField("key", string(k)).WithField("op", "get").WithContext(ctx) 41 log.Trace("performing operation") 42 if len(partitionKey) == 0 { 43 log.WithError(kv.ErrMissingPartitionKey).Warn("got empty partition key") 44 return nil, kv.ErrMissingPartitionKey 45 } 46 if len(key) == 0 { 47 log.WithError(kv.ErrMissingKey).Warn("got empty key") 48 return nil, kv.ErrMissingKey 49 } 50 51 var value []byte 52 err := s.db.View(func(txn *badger.Txn) error { 53 item, err := txn.Get(k) 54 if errors.Is(err, badger.ErrKeyNotFound) { 55 return kv.ErrNotFound 56 } 57 if err != nil { 58 log.WithError(err).Error("error getting key") 59 return err 60 } 61 value, err = item.ValueCopy(nil) 62 if err != nil { 63 log.WithError(err).Error("error getting value for key") 64 return err 65 } 66 return nil 67 }) 68 log.WithField("took", time.Since(start)).WithError(err).WithField("size", len(value)).Trace("operation complete") 69 if err != nil { 70 return nil, err 71 } 72 return &kv.ValueWithPredicate{ 73 Value: value, 74 Predicate: kv.Predicate(value), 75 }, nil 76 } 77 78 func (s *Store) Set(ctx context.Context, partitionKey, key, value []byte) error { 79 k := composeKey(partitionKey, key) 80 start := time.Now() 81 log := s.logger.WithField("key", string(k)).WithField("op", "set").WithContext(ctx) 82 log.Trace("performing operation") 83 if len(partitionKey) == 0 { 84 log.WithError(kv.ErrMissingPartitionKey).Warn("got empty partition key") 85 return kv.ErrMissingPartitionKey 86 } 87 if len(key) == 0 { 88 log.WithError(kv.ErrMissingKey).Warn("got empty key") 89 return kv.ErrMissingKey 90 } 91 if value == nil { 92 log.WithError(kv.ErrMissingValue).Warn("got nil value") 93 return kv.ErrMissingValue 94 } 95 err := s.db.Update(func(txn *badger.Txn) error { 96 return txn.Set(k, value) 97 }) 98 if err != nil { 99 log.WithError(err).Error("error setting value") 100 return err 101 } 102 log.WithField("took", time.Since(start)).Trace("done setting value") 103 return nil 104 } 105 106 func (s *Store) SetIf(ctx context.Context, partitionKey, key, value []byte, valuePredicate kv.Predicate) error { 107 k := composeKey(partitionKey, key) 108 start := time.Now() 109 log := s.logger.WithField("key", string(k)).WithField("op", "set_if").WithContext(ctx) 110 log.Trace("performing operation") 111 if len(partitionKey) == 0 { 112 log.WithError(kv.ErrMissingPartitionKey).Warn("got empty partition key") 113 return kv.ErrMissingPartitionKey 114 } 115 if len(key) == 0 { 116 log.WithError(kv.ErrMissingKey).Warn("got empty key") 117 return kv.ErrMissingKey 118 } 119 if value == nil { 120 log.WithError(kv.ErrMissingValue).Warn("got nil value") 121 return kv.ErrMissingValue 122 } 123 124 err := s.db.Update(func(txn *badger.Txn) error { 125 item, err := txn.Get(k) 126 if err != nil && !errors.Is(err, badger.ErrKeyNotFound) { 127 log.WithError(err).Error("could not get key for predicate") 128 return err 129 } 130 131 if valuePredicate != nil { 132 if errors.Is(err, badger.ErrKeyNotFound) { 133 log.WithField("predicate", nil).Trace("predicate condition failed") 134 return kv.ErrPredicateFailed 135 } 136 if valuePredicate != kv.PrecondConditionalExists { 137 val, err := item.ValueCopy(nil) 138 if err != nil { 139 log.WithError(err).Error("could not get byte value for predicate") 140 return err 141 } 142 if !bytes.Equal(val, valuePredicate.([]byte)) { 143 log.WithField("predicate", valuePredicate).WithField("value", val).Trace("predicate condition failed") 144 return kv.ErrPredicateFailed 145 } 146 } 147 } else if !errors.Is(err, badger.ErrKeyNotFound) { 148 log.WithField("predicate", valuePredicate).Trace("predicate condition failed (key not found)") 149 return kv.ErrPredicateFailed 150 } 151 152 return txn.Set(k, value) 153 }) 154 if errors.Is(err, badger.ErrConflict) { // Return predicate failed on transaction conflict - to retry 155 log.WithError(err).Trace("transaction conflict") 156 err = kv.ErrPredicateFailed 157 } 158 took := time.Since(start) 159 log.WithField("took", took).Trace("operation complete") 160 161 return err 162 } 163 164 func (s *Store) Delete(ctx context.Context, partitionKey, key []byte) error { 165 k := composeKey(partitionKey, key) 166 start := time.Now() 167 log := s.logger. 168 WithField("key", string(k)). 169 WithField("op", "delete"). 170 WithContext(ctx) 171 log.Trace("performing operation") 172 if len(partitionKey) == 0 { 173 log.WithError(kv.ErrMissingPartitionKey).Warn("got empty partition key") 174 return kv.ErrMissingPartitionKey 175 } 176 if len(key) == 0 { 177 log.WithError(kv.ErrMissingKey).Warn("got empty key") 178 return kv.ErrMissingKey 179 } 180 err := s.db.Update(func(txn *badger.Txn) error { 181 return txn.Delete(k) 182 }) 183 took := time.Since(start) 184 log = log.WithField("took", took) 185 if err != nil { 186 log.WithError(err).Trace("operation failed") 187 return err 188 } 189 log.Trace("operation complete") 190 return nil 191 } 192 193 func (s *Store) Scan(ctx context.Context, partitionKey []byte, options kv.ScanOptions) (kv.EntriesIterator, error) { 194 log := s.logger.WithFields(logging.Fields{ 195 "partition_key": string(partitionKey), 196 "start_key": string(options.KeyStart), 197 "op": "scan", 198 }).WithContext(ctx) 199 log.Trace("performing operation") 200 if len(partitionKey) == 0 { 201 log.WithError(kv.ErrMissingPartitionKey).Warn("got empty partition key") 202 return nil, kv.ErrMissingPartitionKey 203 } 204 205 prefix := partitionRange(partitionKey) 206 txn := s.db.NewTransaction(false) 207 opts := badger.DefaultIteratorOptions 208 opts.PrefetchSize = s.prefetchSize 209 if options.BatchSize != 0 && opts.PrefetchSize != 0 && options.BatchSize < opts.PrefetchSize { 210 opts.PrefetchSize = options.BatchSize 211 } 212 if opts.PrefetchSize > 0 { 213 opts.PrefetchValues = true 214 } 215 opts.Prefix = prefix 216 iter := txn.NewIterator(opts) 217 return &EntriesIterator{ 218 iter: iter, 219 partitionKey: partitionKey, 220 start: composeKey(partitionKey, options.KeyStart), 221 logger: log, 222 txn: txn, 223 }, nil 224 } 225 226 func (s *Store) Close() { 227 driverLock.Lock() 228 defer driverLock.Unlock() 229 s.refCount-- 230 if s.refCount <= 0 { 231 _ = s.db.Close() 232 delete(dbMap, s.path) 233 } 234 }