github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/wal/kv/lithium.go (about)

     1  package kv
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/cockroachdb/errors"
    10  	"github.com/projecteru2/core/types"
    11  
    12  	"go.etcd.io/bbolt"
    13  )
    14  
    15  // Lithium .
    16  type Lithium struct {
    17  	sync.Mutex
    18  
    19  	// Name of the root bucket.
    20  	RootBucketKey []byte
    21  
    22  	bolt    *bbolt.DB
    23  	path    string
    24  	mode    os.FileMode
    25  	timeout time.Duration
    26  }
    27  
    28  // NewLithium initializes a new Lithium instance.
    29  func NewLithium() *Lithium {
    30  	return &Lithium{
    31  		RootBucketKey: []byte("root"),
    32  	}
    33  }
    34  
    35  // Reopen re-open a kvdb file.
    36  func (l *Lithium) Reopen() error {
    37  	l.Lock()
    38  	defer l.Unlock()
    39  
    40  	if err := l.close(); err != nil {
    41  		return err
    42  	}
    43  
    44  	return l.open()
    45  }
    46  
    47  // Open opens a kvdb file.
    48  func (l *Lithium) Open(path string, mode os.FileMode, timeout time.Duration) (err error) {
    49  	l.Lock()
    50  	defer l.Unlock()
    51  
    52  	l.path = path
    53  	l.mode = mode
    54  	l.timeout = timeout
    55  
    56  	return l.open()
    57  }
    58  
    59  // Close closes the kvdb file.
    60  func (l *Lithium) Close() error {
    61  	l.Lock()
    62  	defer l.Unlock()
    63  	return l.close()
    64  }
    65  
    66  // Put creates/updates a key/value pair.
    67  func (l *Lithium) Put(key []byte, value []byte) (err error) {
    68  	return l.update(func(bkt *bbolt.Bucket) error {
    69  		return bkt.Put(key, value)
    70  	})
    71  }
    72  
    73  // Get read a key's value.
    74  func (l *Lithium) Get(key []byte) (dst []byte, err error) {
    75  	err = l.view(func(bkt *bbolt.Bucket) error {
    76  		src := bkt.Get(key)
    77  		dst = make([]byte, len(src))
    78  
    79  		for n := 0; n < len(dst); {
    80  			n += copy(dst, src)
    81  		}
    82  
    83  		return nil
    84  	})
    85  
    86  	return
    87  }
    88  
    89  // Delete deletes a key.
    90  func (l *Lithium) Delete(key []byte) error {
    91  	return l.update(func(bkt *bbolt.Bucket) error {
    92  		return bkt.Delete(key)
    93  	})
    94  }
    95  
    96  // Scan scans all the key/value pairs.
    97  func (l *Lithium) Scan(prefix []byte) (<-chan ScanEntry, func()) {
    98  	ch := make(chan ScanEntry)
    99  
   100  	exit := make(chan struct{})
   101  	abort := func() {
   102  		close(exit)
   103  	}
   104  
   105  	go func() {
   106  		defer close(ch)
   107  
   108  		scan := func(bkt *bbolt.Bucket) error {
   109  			c := bkt.Cursor()
   110  			for key, value := c.Seek(prefix); key != nil && bytes.HasPrefix(key, prefix); key, value = c.Next() {
   111  				select {
   112  				case <-exit:
   113  					return nil
   114  				case ch <- LithiumScanEntry{key: key, value: value}:
   115  				}
   116  			}
   117  			return nil
   118  		}
   119  
   120  		if err := l.view(scan); err != nil {
   121  			select {
   122  			case <-exit:
   123  			case ch <- LithiumScanEntry{err: err}:
   124  			}
   125  		}
   126  	}()
   127  
   128  	return ch, abort
   129  }
   130  
   131  // NextSequence generates a new sequence.
   132  func (l *Lithium) NextSequence() (uint64, error) {
   133  	var seq uint64
   134  	err := l.update(func(bkt *bbolt.Bucket) (ue error) {
   135  		seq, ue = bkt.NextSequence()
   136  		return
   137  	})
   138  
   139  	return seq, err
   140  }
   141  
   142  func (l *Lithium) open() (err error) {
   143  	if l.bolt, err = bbolt.Open(l.path, l.mode, &bbolt.Options{Timeout: l.timeout}); err != nil {
   144  		return
   145  	}
   146  
   147  	err = l.bolt.Update(func(tx *bbolt.Tx) error {
   148  		_, ce := tx.CreateBucketIfNotExists(l.RootBucketKey)
   149  		return ce
   150  	})
   151  
   152  	return
   153  }
   154  
   155  func (l *Lithium) close() error {
   156  	return l.bolt.Close()
   157  }
   158  
   159  func (l *Lithium) view(fn func(*bbolt.Bucket) error) error {
   160  	return l.bolt.Update(func(tx *bbolt.Tx) error {
   161  		bkt, err := l.getBucket(tx, l.RootBucketKey)
   162  		if err != nil {
   163  			return err
   164  		}
   165  		return fn(bkt)
   166  	})
   167  }
   168  
   169  func (l *Lithium) update(fn func(*bbolt.Bucket) error) error {
   170  	return l.bolt.Update(func(tx *bbolt.Tx) error {
   171  		bkt, err := l.getBucket(tx, l.RootBucketKey)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		return fn(bkt)
   176  	})
   177  }
   178  
   179  func (l *Lithium) getBucket(tx *bbolt.Tx, key []byte) (bkt *bbolt.Bucket, err error) {
   180  	bkt = tx.Bucket(l.RootBucketKey)
   181  	if bkt == nil {
   182  		err = errors.Wrapf(types.ErrInvalidWALBucket, "%+v", key)
   183  	}
   184  	return
   185  }
   186  
   187  // LithiumScanEntry indicates an entry of scanning.
   188  type LithiumScanEntry struct {
   189  	err   error
   190  	key   []byte
   191  	value []byte
   192  }
   193  
   194  // Pair means a pair of key/value.
   195  func (e LithiumScanEntry) Pair() ([]byte, []byte) {
   196  	return e.key, e.value
   197  }
   198  
   199  // Error .
   200  func (e LithiumScanEntry) Error() error {
   201  	return e.err
   202  }