mirror of
https://github.com/taigrr/bitcask
synced 2025-01-18 04:03:17 -08:00
Add ErrBadConfig and ErrBadMetadata as errors that consumers can check and use (#241)
cc @taigrr This PR will _hopefully_ help to fix some critical isseus in the real world with several or more [Yarn.social](https://yarn.social) pods running [yarnd](https://git.mills.io/yarnsocial/yarn) where starting back up after a power failure or crash can sometimes result in an empty `config.json` or empty `meta.json` or both! I'm not actually sure how this can arise, and as yet I haven't been able to reproduce it (_I can only assume this has to be failures cases outside of our control_); but in any case the application and database is recoverable by simply `rm config.json` and/or `rm meta.json`. So this PR makes errors loading the config and metadata first-class errors and exported error types that consumers of the library can use to perform automated recovery without requiring human intervention. Basiclaly in this case it's no big deal we lost the database config of metadata, we can simply carry on. Co-authored-by: James Mills <prologic@shortcircuit.net.au> Reviewed-on: https://git.mills.io/prologic/bitcask/pulls/241 Co-authored-by: James Mills <james@mills.io> Co-committed-by: James Mills <james@mills.io>
This commit is contained in:
parent
2c57c950f8
commit
5429693cc8
40
bitcask.go
40
bitcask.go
@ -2,7 +2,6 @@ package bitcask
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
@ -33,41 +32,6 @@ const (
|
|||||||
ttlIndexFile = "ttl_index"
|
ttlIndexFile = "ttl_index"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrKeyNotFound is the error returned when a key is not found
|
|
||||||
ErrKeyNotFound = errors.New("error: key not found")
|
|
||||||
|
|
||||||
// ErrKeyTooLarge is the error returned for a key that exceeds the
|
|
||||||
// maximum allowed key size (configured with WithMaxKeySize).
|
|
||||||
ErrKeyTooLarge = errors.New("error: key too large")
|
|
||||||
|
|
||||||
// ErrKeyExpired is the error returned when a key is queried which has
|
|
||||||
// already expired (due to ttl)
|
|
||||||
ErrKeyExpired = errors.New("error: key expired")
|
|
||||||
|
|
||||||
// ErrEmptyKey is the error returned for a value with an empty key.
|
|
||||||
ErrEmptyKey = errors.New("error: empty key")
|
|
||||||
|
|
||||||
// ErrValueTooLarge is the error returned for a value that exceeds the
|
|
||||||
// maximum allowed value size (configured with WithMaxValueSize).
|
|
||||||
ErrValueTooLarge = errors.New("error: value too large")
|
|
||||||
|
|
||||||
// ErrChecksumFailed is the error returned if a key/value retrieved does
|
|
||||||
// not match its CRC checksum
|
|
||||||
ErrChecksumFailed = errors.New("error: checksum failed")
|
|
||||||
|
|
||||||
// ErrDatabaseLocked is the error returned if the database is locked
|
|
||||||
// (typically opened by another process)
|
|
||||||
ErrDatabaseLocked = errors.New("error: database locked")
|
|
||||||
|
|
||||||
ErrInvalidRange = errors.New("error: invalid range")
|
|
||||||
ErrInvalidVersion = errors.New("error: invalid db version")
|
|
||||||
|
|
||||||
// ErrMergeInProgress is the error returned if merge is called when already a merge
|
|
||||||
// is in progress
|
|
||||||
ErrMergeInProgress = errors.New("error: merge already in progress")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Bitcask is a struct that represents a on-disk LSM and WAL data structure
|
// Bitcask is a struct that represents a on-disk LSM and WAL data structure
|
||||||
// and in-memory hash of key/value pairs as per the Bitcask paper and seen
|
// and in-memory hash of key/value pairs as per the Bitcask paper and seen
|
||||||
// in the Riak database.
|
// in the Riak database.
|
||||||
@ -864,7 +828,7 @@ func Open(path string, options ...Option) (*Bitcask, error) {
|
|||||||
if internal.Exists(configPath) {
|
if internal.Exists(configPath) {
|
||||||
cfg, err = config.Load(configPath)
|
cfg, err = config.Load(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ErrBadConfig{err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cfg = newDefaultConfig()
|
cfg = newDefaultConfig()
|
||||||
@ -886,7 +850,7 @@ func Open(path string, options ...Option) (*Bitcask, error) {
|
|||||||
|
|
||||||
meta, err = loadMetadata(path)
|
meta, err = loadMetadata(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ErrBadMetadata{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitcask := &Bitcask{
|
bitcask := &Bitcask{
|
||||||
|
77
errors.go
Normal file
77
errors.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package bitcask
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrKeyNotFound is the error returned when a key is not found
|
||||||
|
ErrKeyNotFound = errors.New("error: key not found")
|
||||||
|
|
||||||
|
// ErrKeyTooLarge is the error returned for a key that exceeds the
|
||||||
|
// maximum allowed key size (configured with WithMaxKeySize).
|
||||||
|
ErrKeyTooLarge = errors.New("error: key too large")
|
||||||
|
|
||||||
|
// ErrKeyExpired is the error returned when a key is queried which has
|
||||||
|
// already expired (due to ttl)
|
||||||
|
ErrKeyExpired = errors.New("error: key expired")
|
||||||
|
|
||||||
|
// ErrEmptyKey is the error returned for a value with an empty key.
|
||||||
|
ErrEmptyKey = errors.New("error: empty key")
|
||||||
|
|
||||||
|
// ErrValueTooLarge is the error returned for a value that exceeds the
|
||||||
|
// maximum allowed value size (configured with WithMaxValueSize).
|
||||||
|
ErrValueTooLarge = errors.New("error: value too large")
|
||||||
|
|
||||||
|
// ErrChecksumFailed is the error returned if a key/value retrieved does
|
||||||
|
// not match its CRC checksum
|
||||||
|
ErrChecksumFailed = errors.New("error: checksum failed")
|
||||||
|
|
||||||
|
// ErrDatabaseLocked is the error returned if the database is locked
|
||||||
|
// (typically opened by another process)
|
||||||
|
ErrDatabaseLocked = errors.New("error: database locked")
|
||||||
|
|
||||||
|
// ErrInvalidRange is the error returned when the range scan is invalid
|
||||||
|
ErrInvalidRange = errors.New("error: invalid range")
|
||||||
|
|
||||||
|
// ErrInvalidVersion is the error returned when the database version is invalid
|
||||||
|
ErrInvalidVersion = errors.New("error: invalid db version")
|
||||||
|
|
||||||
|
// ErrMergeInProgress is the error returned if merge is called when already a merge
|
||||||
|
// is in progress
|
||||||
|
ErrMergeInProgress = errors.New("error: merge already in progress")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrBadConfig is the error returned on failure to load the database config
|
||||||
|
type ErrBadConfig struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrBadConfig) Is(target error) bool {
|
||||||
|
if _, ok := target.(*ErrBadConfig); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return errors.Is(e.Err, target)
|
||||||
|
}
|
||||||
|
func (e *ErrBadConfig) Unwrap() error { return e.Err }
|
||||||
|
func (e *ErrBadConfig) Error() string {
|
||||||
|
return fmt.Sprintf("error reading config.json: %s", e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadMetadata is the error returned on failure to load the database metadata
|
||||||
|
type ErrBadMetadata struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrBadMetadata) Is(target error) bool {
|
||||||
|
if _, ok := target.(*ErrBadMetadata); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return errors.Is(e.Err, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrBadMetadata) Unwrap() error { return e.Err }
|
||||||
|
func (e *ErrBadMetadata) Error() string {
|
||||||
|
return fmt.Sprintf("error reading meta.json: %s", e.Err)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user