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  }