Files
zeekay a3b53a21b5 feat: make leveldb and pebbledb optional via build tags
- Add //go:build leveldb to all leveldb package files
- Add //go:build pebbledb to all pebbledb package files
- Refactor factory to use registration pattern
- Refactor manager to use registration pattern
- badgerdb is now the only default database
- Build with -tags=leveldb or -tags=pebbledb to enable them
2025-12-21 12:02:37 -08:00

140 lines
2.5 KiB
Go

// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build pebbledb
package pebbledb
import (
"errors"
"fmt"
"slices"
"sync"
"github.com/cockroachdb/pebble"
"github.com/luxfi/database"
)
var (
_ database.Iterator = (*iter)(nil)
errCouldNotGetValue = errors.New("could not get iterator value")
)
type iter struct {
// [lock] ensures that only one goroutine can access [iter] at a time.
// Note that [Database.Close] calls [iter.Release] so we need [lock] to ensure
// that the user and [Database.Close] don't execute [iter.Release] concurrently.
// Invariant: [Database.lock] is never grabbed while holding [lock].
lock sync.Mutex
db *Database
iter *pebble.Iterator
initialized bool
closed bool
err error
hasNext bool
nextKey []byte
nextVal []byte
}
// Must not be called with [db.lock] held.
func (it *iter) Next() bool {
it.lock.Lock()
defer it.lock.Unlock()
switch {
case it.err != nil:
it.hasNext = false
return false
case it.closed:
it.hasNext = false
it.err = database.ErrClosed
return false
case !it.initialized:
it.hasNext = it.iter.First()
it.initialized = true
default:
it.hasNext = it.iter.Next()
}
if !it.hasNext {
return false
}
key := it.iter.Key()
value, err := it.iter.ValueAndErr()
if err != nil {
it.hasNext = false
it.err = fmt.Errorf("%w: %w", errCouldNotGetValue, err)
return false
}
it.nextKey = key
it.nextVal = value
return true
}
func (it *iter) Error() error {
it.lock.Lock()
defer it.lock.Unlock()
if it.err != nil || it.closed {
return it.err
}
return updateError(it.iter.Error())
}
func (it *iter) Key() []byte {
it.lock.Lock()
defer it.lock.Unlock()
if !it.hasNext {
return nil
}
return slices.Clone(it.nextKey)
}
func (it *iter) Value() []byte {
it.lock.Lock()
defer it.lock.Unlock()
if !it.hasNext {
return nil
}
return slices.Clone(it.nextVal)
}
func (it *iter) Release() {
it.db.lock.Lock()
defer it.db.lock.Unlock()
it.lock.Lock()
defer it.lock.Unlock()
it.release()
}
// Assumes [it.lock] and [it.db.lock] are held.
func (it *iter) release() {
if it.closed {
return
}
// Cloning these values ensures that calling it.Key() or it.Value() after
// releasing the iterator will not segfault.
it.nextKey = slices.Clone(it.nextKey)
it.nextVal = slices.Clone(it.nextVal)
// Remove the iterator from the list of open iterators.
it.db.openIterators.Remove(it)
it.closed = true
if err := it.iter.Close(); err != nil {
it.err = updateError(err)
}
}