database/bitcask/bitcask.go

224 lines
5.3 KiB
Go
Raw Normal View History

2022-01-09 00:59:10 +00:00
package bitcask
import (
2022-07-26 04:36:46 +00:00
"errors"
"strings"
"sync"
"git.tcp.direct/Mirrors/bitcask-mirror"
2022-01-09 03:01:15 +00:00
2022-02-23 15:53:55 +00:00
"git.tcp.direct/tcp.direct/database"
)
2022-01-09 03:01:15 +00:00
// Store is an implmentation of a Filer and a Searcher using Bitcask.
type Store struct {
2022-01-01 22:22:08 +00:00
*bitcask.Bitcask
2022-01-09 03:01:15 +00:00
database.Searcher
2022-07-26 04:36:46 +00:00
closed bool
}
// Backend returns the underlying bitcask instance.
func (s Store) Backend() any {
return s.Bitcask
2022-01-01 22:22:08 +00:00
}
// DB is a mapper of a Filer and Searcher implementation using Bitcask.
type DB struct {
2022-01-09 03:01:15 +00:00
store map[string]Store
2022-01-01 21:08:59 +00:00
path string
2022-01-01 20:06:56 +00:00
mu *sync.RWMutex
}
// AllStores returns a map of the names of all bitcask datastores and the corresponding Filers.
func (db *DB) AllStores() map[string]database.Filer {
2022-07-26 04:36:46 +00:00
db.mu.RLock()
defer db.mu.RUnlock()
var stores = make(map[string]database.Filer)
for n, s := range db.store {
stores[n] = s
2022-07-26 04:36:46 +00:00
}
return stores
}
2022-01-01 22:22:08 +00:00
// OpenDB will either open an existing set of bitcask datastores at the given directory, or it will create a new one.
func OpenDB(path string) *DB {
2022-01-01 21:08:59 +00:00
return &DB{
2022-01-09 03:01:15 +00:00
store: make(map[string]Store),
2022-01-01 21:08:59 +00:00
path: path,
mu: &sync.RWMutex{},
}
}
2022-01-19 09:46:51 +00:00
// Path returns the base path where we store our bitcask "stores".
2022-01-01 21:08:59 +00:00
func (db *DB) Path() string {
return db.path
}
2022-07-24 01:07:09 +00:00
var defaultBitcaskOptions []bitcask.Option
2022-05-21 23:17:21 +00:00
2022-07-24 01:07:09 +00:00
// SetDefaultBitcaskOptions options will set the options used for all subsequent bitcask stores that are initialized.
2022-05-21 23:17:21 +00:00
func SetDefaultBitcaskOptions(bitcaskopts ...bitcask.Option) {
2022-07-24 01:07:09 +00:00
defaultBitcaskOptions = append(defaultBitcaskOptions, bitcaskopts...)
2022-05-21 23:17:21 +00:00
}
2022-07-24 01:07:09 +00:00
// WithMaxDatafileSize is a shim for bitcask's WithMaxDataFileSize function.
2022-05-21 23:17:21 +00:00
func WithMaxDatafileSize(size int) bitcask.Option {
return bitcask.WithMaxDatafileSize(size)
}
2022-07-24 01:07:09 +00:00
// WithMaxKeySize is a shim for bitcask's WithMaxKeySize function.
2022-05-21 23:17:21 +00:00
func WithMaxKeySize(size uint32) bitcask.Option {
return bitcask.WithMaxKeySize(size)
}
2022-07-24 01:07:09 +00:00
// WithMaxValueSize is a shim for bitcask's WithMaxValueSize function.
2022-05-21 23:17:21 +00:00
func WithMaxValueSize(size uint64) bitcask.Option {
return bitcask.WithMaxValueSize(size)
}
2022-01-19 09:46:51 +00:00
// Init opens a bitcask store at the given path to be referenced by storeName.
2022-07-26 04:36:46 +00:00
func (db *DB) Init(storeName string, opts ...any) error {
var bitcaskopts []bitcask.Option
for _, opt := range opts {
if _, ok := opt.(bitcask.Option); !ok {
return errors.New("invalid bitcask option type")
}
bitcaskopts = append(bitcaskopts, opt.(bitcask.Option))
}
db.mu.Lock()
defer db.mu.Unlock()
2022-05-21 23:17:21 +00:00
2022-07-24 01:07:09 +00:00
if len(defaultBitcaskOptions) > 0 {
bitcaskopts = append(bitcaskopts, defaultBitcaskOptions...)
2022-05-21 23:17:21 +00:00
}
2022-01-19 09:46:51 +00:00
if _, ok := db.store[storeName]; ok {
2022-08-29 08:07:16 +00:00
return ErrStoreExists
}
2022-01-01 21:08:59 +00:00
path := db.Path()
2022-02-12 08:11:09 +00:00
if !strings.HasSuffix(db.Path(), "/") {
2022-01-01 21:08:59 +00:00
path = db.Path() + "/"
}
c, e := bitcask.Open(path+storeName, bitcaskopts...)
if e != nil {
return e
}
2022-01-19 09:46:51 +00:00
db.store[storeName] = Store{Bitcask: c}
return nil
}
// With calls the given underlying bitcask instance.
2022-07-26 04:36:46 +00:00
func (db *DB) With(storeName string) database.Store {
db.mu.RLock()
defer db.mu.RUnlock()
2022-01-19 09:46:51 +00:00
d, ok := db.store[storeName]
2022-05-21 23:17:21 +00:00
if ok {
return d
}
2022-07-26 04:36:46 +00:00
return nil
2022-05-21 23:17:21 +00:00
}
// WithNew calls the given underlying bitcask instance, if it doesn't exist, it creates it.
2022-07-26 04:36:46 +00:00
func (db *DB) WithNew(storeName string) database.Filer {
2022-05-21 23:17:21 +00:00
db.mu.RLock()
defer db.mu.RUnlock()
d, ok := db.store[storeName]
if ok {
return d
}
db.mu.RUnlock()
err := db.Init(storeName)
db.mu.RLock()
if err == nil {
return db.store[storeName]
2022-01-01 21:08:59 +00:00
}
2022-05-21 23:17:21 +00:00
return Store{Bitcask: nil}
}
// Close is a simple shim for bitcask's Close function.
2022-01-19 09:46:51 +00:00
func (db *DB) Close(storeName string) error {
db.mu.Lock()
defer db.mu.Unlock()
2022-01-19 09:46:51 +00:00
st, ok := db.store[storeName]
if !ok {
2022-08-29 08:07:16 +00:00
return ErrBogusStore
}
err := st.Close()
if err != nil {
return err
}
2022-01-19 09:46:51 +00:00
delete(db.store, storeName)
return nil
}
// Sync is a simple shim for bitcask's Sync function.
2022-01-19 09:46:51 +00:00
func (db *DB) Sync(storeName string) error {
db.mu.RLock()
defer db.mu.RUnlock()
2022-01-19 09:46:51 +00:00
return db.store[storeName].Sync()
}
2022-01-01 22:22:08 +00:00
// withAllAction
type withAllAction uint8
const (
2022-01-01 22:22:08 +00:00
// dclose
2022-01-01 21:08:59 +00:00
dclose withAllAction = iota
2022-01-01 22:22:08 +00:00
// dsync
2022-01-01 21:08:59 +00:00
dsync
)
// withAll performs an action on all bitcask stores that we have open.
// In the case of an error, withAll will continue and return a compound form of any errors that occurred.
// For now this is just for Close and Sync, thusly it does a hard lock on the Keeper.
func (db *DB) withAll(action withAllAction) error {
2022-08-29 10:30:39 +00:00
if db == nil || db.store == nil || len(db.store) < 1 {
2022-08-29 10:02:14 +00:00
return ErrNoStores
}
2022-02-12 07:52:42 +00:00
var errs = make([]error, len(db.store))
for name, store := range db.store {
var err error
2022-01-01 22:22:08 +00:00
if store.Bitcask == nil {
2022-08-29 08:07:16 +00:00
errs = append(errs, namedErr(name, ErrBogusStore))
2022-01-01 21:08:59 +00:00
continue
}
switch action {
2022-01-01 21:08:59 +00:00
case dclose:
2022-07-26 04:36:46 +00:00
err = namedErr(name, store.Close())
2022-01-01 21:08:59 +00:00
case dsync:
2022-07-26 04:36:46 +00:00
err = namedErr(name, store.Sync())
default:
2022-08-29 08:07:16 +00:00
return ErrUnknownAction
}
if err == nil {
continue
}
errs = append(errs, err)
}
return compoundErrors(errs)
}
2022-07-24 01:07:09 +00:00
// SyncAndCloseAll implements the method from Keeper to sync and close all bitcask stores.
2022-01-01 22:22:08 +00:00
func (db *DB) SyncAndCloseAll() error {
2022-02-12 07:52:42 +00:00
var errs = make([]error, len(db.store))
2022-01-01 22:22:08 +00:00
errSync := namedErr("sync", db.SyncAll())
if errSync != nil {
2022-01-01 22:22:08 +00:00
errs = append(errs, errSync)
}
errClose := namedErr("close", db.CloseAll())
if errClose != nil {
errs = append(errs, errClose)
}
return compoundErrors(errs)
}
// CloseAll closes all bitcask datastores.
func (db *DB) CloseAll() error {
return db.withAll(dclose)
}
2022-01-01 22:22:08 +00:00
// SyncAll syncs all bitcask datastores.
func (db *DB) SyncAll() error {
return db.withAll(dsync)
}