go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/embeddedkvs/kvs.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package embeddedkvs 16 17 import ( 18 "context" 19 20 "github.com/dgraph-io/badger/v3" 21 "golang.org/x/sync/errgroup" 22 23 "go.chromium.org/luci/common/errors" 24 ) 25 26 type KVS struct { 27 db *badger.DB 28 } 29 30 // New instantiates KVS. 31 func New(ctx context.Context, path string) (*KVS, error) { 32 opt := badger.DefaultOptions(path).WithLoggingLevel(badger.WARNING) 33 db, err := badger.Open(opt) 34 if err != nil { 35 return nil, errors.Annotate(err, "failed to open database: %s", path).Err() 36 } 37 38 return &KVS{ 39 db: db, 40 }, nil 41 } 42 43 // Close closes KVS. 44 func (k *KVS) Close() error { 45 if err := k.db.Close(); err != nil { 46 return errors.Annotate(err, "failed to close db").Err() 47 } 48 return nil 49 } 50 51 // Set sets key/value to storage. 52 // 53 // This should be called in parallel for efficient storing. 54 func (k *KVS) Set(key string, value []byte) error { 55 if err := k.db.Update(func(txn *badger.Txn) error { 56 return txn.Set([]byte(key), value) 57 }); err != nil { 58 return errors.Annotate(err, "failed to put %s", key).Err() 59 } 60 return nil 61 } 62 63 // GetMulti calls |fn| in parallel for cached entries. 64 func (k *KVS) GetMulti(ctx context.Context, keys []string, fn func(key string, value []byte) error) error { 65 if err := k.db.View(func(txn *badger.Txn) error { 66 eg, _ := errgroup.WithContext(ctx) 67 for _, key := range keys { 68 key := key 69 eg.Go(func() error { 70 item, err := txn.Get([]byte(key)) 71 if err == badger.ErrKeyNotFound { 72 return nil 73 } 74 if err != nil { 75 return errors.Annotate(err, "failed to get %s", key).Err() 76 } 77 return item.Value(func(val []byte) error { 78 return fn(key, val) 79 }) 80 81 }) 82 } 83 84 return eg.Wait() 85 }); err != nil { 86 return errors.Annotate(err, "failed to get").Err() 87 } 88 89 return nil 90 } 91 92 // SetMulti receives callback that takes function which is used to set a key/value pair. 93 func (k *KVS) SetMulti(fn func(set func(key string, value []byte) error) error) error { 94 wb := k.db.NewWriteBatch() 95 defer wb.Cancel() 96 97 if err := fn(func(key string, value []byte) error { 98 if err := wb.Set([]byte(key), value); err != nil { 99 return errors.Annotate(err, "failed to set key: %s", key).Err() 100 } 101 return nil 102 }); err != nil { 103 return err 104 } 105 106 if err := wb.Flush(); err != nil { 107 return errors.Annotate(err, "failed to call Flush").Err() 108 } 109 return nil 110 } 111 112 // ForEach executes a function for each key/value pair in KVS. 113 func (k *KVS) ForEach(fn func(key string, value []byte) error) error { 114 err := k.db.View(func(txn *badger.Txn) error { 115 it := txn.NewIterator(badger.DefaultIteratorOptions) 116 defer it.Close() 117 for it.Rewind(); it.Valid(); it.Next() { 118 item := it.Item() 119 120 if err := item.Value(func(val []byte) error { 121 return fn(string(item.Key()), val) 122 }); err != nil { 123 return err 124 } 125 } 126 127 return nil 128 }) 129 if err != nil { 130 return errors.Annotate(err, "failed to iterate").Err() 131 } 132 return nil 133 }