kubo/plugin/plugins/pebbleds/pebbleds.go
Andrew Gillis e41dc120f7
Some checks are pending
CodeQL / codeql (push) Waiting to run
Docker Build / docker-build (push) Waiting to run
Gateway Conformance / gateway-conformance (push) Waiting to run
Gateway Conformance / gateway-conformance-libp2p-experiment (push) Waiting to run
Go Build / go-build (push) Waiting to run
Go Check / go-check (push) Waiting to run
Go Lint / go-lint (push) Waiting to run
Go Test / go-test (push) Waiting to run
Interop / interop-prep (push) Waiting to run
Interop / helia-interop (push) Blocked by required conditions
Interop / ipfs-webui (push) Blocked by required conditions
Sharness / sharness-test (push) Waiting to run
Spell Check / spellcheck (push) Waiting to run
Update pebble db to latest format by default (#10720)
* Update pebble db to latest format by default

If the pebble database format is not explicitly set in the config, then set it to the latest format version by default. This will ensure that the database format is sufficiently up-to-date to be compatible with a major version upgrade of pebble.
2025-02-17 15:32:53 -08:00

191 lines
5.0 KiB
Go

package pebbleds
import (
"fmt"
"path/filepath"
"time"
"github.com/cockroachdb/pebble"
pebbleds "github.com/ipfs/go-ds-pebble"
"github.com/ipfs/kubo/misc/fsutil"
"github.com/ipfs/kubo/plugin"
"github.com/ipfs/kubo/repo"
"github.com/ipfs/kubo/repo/fsrepo"
)
// Plugins is exported list of plugins that will be loaded.
var Plugins = []plugin.Plugin{
&pebbledsPlugin{},
}
type pebbledsPlugin struct{}
var _ plugin.PluginDatastore = (*pebbledsPlugin)(nil)
func (*pebbledsPlugin) Name() string {
return "ds-pebble"
}
func (*pebbledsPlugin) Version() string {
return "0.1.0"
}
func (*pebbledsPlugin) Init(_ *plugin.Environment) error {
return nil
}
func (*pebbledsPlugin) DatastoreTypeName() string {
return "pebbleds"
}
type datastoreConfig struct {
path string
cacheSize int64
// Documentation of these values: https://pkg.go.dev/github.com/cockroachdb/pebble@v1.1.2#Options
pebbleOpts *pebble.Options
}
// PebbleDatastoreConfig returns a configuration stub for a pebble datastore
// from the given parameters.
func (*pebbledsPlugin) DatastoreConfigParser() fsrepo.ConfigFromMap {
return func(params map[string]any) (fsrepo.DatastoreConfig, error) {
var c datastoreConfig
var ok bool
c.path, ok = params["path"].(string)
if !ok {
return nil, fmt.Errorf("'path' field is missing or not string")
}
cacheSize, err := getConfigInt("cacheSize", params)
if err != nil {
return nil, err
}
c.cacheSize = int64(cacheSize)
bytesPerSync, err := getConfigInt("bytesPerSync", params)
if err != nil {
return nil, err
}
disableWAL, err := getConfigBool("disableWAL", params)
if err != nil {
return nil, err
}
fmv, err := getConfigInt("formatMajorVersion", params)
if err != nil {
return nil, err
}
formatMajorVersion := pebble.FormatMajorVersion(fmv)
l0CompactionThreshold, err := getConfigInt("l0CompactionThreshold", params)
if err != nil {
return nil, err
}
l0StopWritesThreshold, err := getConfigInt("l0StopWritesThreshold", params)
if err != nil {
return nil, err
}
lBaseMaxBytes, err := getConfigInt("lBaseMaxBytes", params)
if err != nil {
return nil, err
}
maxConcurrentCompactions, err := getConfigInt("maxConcurrentCompactions", params)
if err != nil {
return nil, err
}
memTableSize, err := getConfigInt("memTableSize", params)
if err != nil {
return nil, err
}
memTableStopWritesThreshold, err := getConfigInt("memTableStopWritesThreshold", params)
if err != nil {
return nil, err
}
walBytesPerSync, err := getConfigInt("walBytesPerSync", params)
if err != nil {
return nil, err
}
walMinSyncSec, err := getConfigInt("walMinSyncIntervalSeconds", params)
if err != nil {
return nil, err
}
// Use latest version by default. This will ensure that format is
// compatible across database upgrades.
if formatMajorVersion == 0 {
formatMajorVersion = pebble.FormatNewest
}
if bytesPerSync != 0 || disableWAL || formatMajorVersion != 0 || l0CompactionThreshold != 0 || l0StopWritesThreshold != 0 || lBaseMaxBytes != 0 || maxConcurrentCompactions != 0 || memTableSize != 0 || memTableStopWritesThreshold != 0 || walBytesPerSync != 0 || walMinSyncSec != 0 {
c.pebbleOpts = &pebble.Options{
BytesPerSync: bytesPerSync,
DisableWAL: disableWAL,
FormatMajorVersion: formatMajorVersion,
L0CompactionThreshold: l0CompactionThreshold,
L0StopWritesThreshold: l0StopWritesThreshold,
LBaseMaxBytes: int64(lBaseMaxBytes),
MemTableSize: uint64(memTableSize),
MemTableStopWritesThreshold: memTableStopWritesThreshold,
WALBytesPerSync: walBytesPerSync,
}
if maxConcurrentCompactions != 0 {
c.pebbleOpts.MaxConcurrentCompactions = func() int { return maxConcurrentCompactions }
}
if walMinSyncSec != 0 {
c.pebbleOpts.WALMinSyncInterval = func() time.Duration { return time.Duration(walMinSyncSec) * time.Second }
}
}
return &c, nil
}
}
func getConfigBool(name string, params map[string]any) (bool, error) {
val, ok := params[name]
if ok {
bval, ok := val.(bool)
if !ok {
return false, fmt.Errorf("%q field was not a bool", name)
}
return bval, nil
}
return false, nil
}
func getConfigInt(name string, params map[string]any) (int, error) {
val, ok := params[name]
if ok {
// TODO: see why val may be an int or a float64.
ival, ok := val.(int)
if !ok {
fval, ok := val.(float64)
if !ok {
return 0, fmt.Errorf("%q field was not an integer or a float64", name)
}
return int(fval), nil
}
return ival, nil
}
return 0, nil
}
func (c *datastoreConfig) DiskSpec() fsrepo.DiskSpec {
return map[string]interface{}{
"type": "pebbleds",
"path": c.path,
}
}
func (c *datastoreConfig) Create(path string) (repo.Datastore, error) {
p := c.path
if !filepath.IsAbs(p) {
p = filepath.Join(path, p)
}
if err := fsutil.DirWritable(p); err != nil {
return nil, err
}
return pebbleds.NewDatastore(p, pebbleds.WithCacheSize(c.cacheSize), pebbleds.WithPebbleOpts(c.pebbleOpts))
}