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 }