From 82e26449fa70c108706b4a9a1f0d3dd5d226423f Mon Sep 17 00:00:00 2001 From: James Mills <1290234+prologic@users.noreply.github.com> Date: Wed, 7 Aug 2019 10:23:10 +1000 Subject: [PATCH] Added the same functional options to the bitcask CLI and persist options to the db store (#40) --- bitcask.go | 34 +++++++++++++++++++++- cmd/bitcask/initdb.go | 67 +++++++++++++++++++++++++++++++++++++++++++ options.go | 44 +++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 cmd/bitcask/initdb.go diff --git a/bitcask.go b/bitcask.go index 68f5ac1..66a3ffb 100644 --- a/bitcask.go +++ b/bitcask.go @@ -1,6 +1,7 @@ package bitcask import ( + "encoding/json" "errors" "hash/crc32" "io" @@ -245,6 +246,28 @@ func (b *Bitcask) put(key string, value []byte) (int64, int64, error) { return b.curr.Write(e) } +func (b *Bitcask) readConfig() error { + if internal.Exists(filepath.Join(b.path, "config.json")) { + data, err := ioutil.ReadFile(filepath.Join(b.path, "config.json")) + if err != nil { + return err + } + + if err := json.Unmarshal(data, &b.config); err != nil { + return err + } + } + return nil +} + +func (b *Bitcask) writeConfig() error { + data, err := json.Marshal(b.config) + if err != nil { + return err + } + return ioutil.WriteFile(filepath.Join(b.path, "config.json"), data, 0600) +} + func (b *Bitcask) reopen() error { b.mu.Lock() defer b.mu.Unlock() @@ -411,9 +434,14 @@ func Open(path string, options ...Option) (*Bitcask, error) { return nil, err } + config, err := getConfig(path) + if err != nil { + return nil, err + } + bitcask := &Bitcask{ Flock: flock.New(filepath.Join(path, "lock")), - config: newDefaultConfig(), + config: config, options: options, path: path, } @@ -435,6 +463,10 @@ func Open(path string, options ...Option) (*Bitcask, error) { return nil, ErrDatabaseLocked } + if err := bitcask.writeConfig(); err != nil { + return nil, err + } + if err := bitcask.reopen(); err != nil { return nil, err } diff --git a/cmd/bitcask/initdb.go b/cmd/bitcask/initdb.go new file mode 100644 index 0000000..2b105e1 --- /dev/null +++ b/cmd/bitcask/initdb.go @@ -0,0 +1,67 @@ +package main + +import ( + "os" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/prologic/bitcask" +) + +var initdbCmd = &cobra.Command{ + Use: "initdb", + Aliases: []string{"create", "init"}, + Short: "Initialize a new database", + Long: `This initializes a new database with persisted options`, + Args: cobra.ExactArgs(0), + PreRun: func(cmd *cobra.Command, args []string) { + viper.BindPFlag("with-max-datafile-size", cmd.Flags().Lookup("with-max-datafile-size")) + viper.SetDefault("with-max-datafile-size", bitcask.DefaultMaxDatafileSize) + + viper.BindPFlag("with-max-key-size", cmd.Flags().Lookup("with-max-key-size")) + viper.SetDefault("with-max-key-size", bitcask.DefaultMaxKeySize) + + viper.BindPFlag("with-max-value-size", cmd.Flags().Lookup("with-max-value-size")) + viper.SetDefault("with-max-value-size", bitcask.DefaultMaxValueSize) + }, + Run: func(cmd *cobra.Command, args []string) { + path := viper.GetString("path") + + maxDatafileSize := viper.GetInt("with-max-datafile-size") + maxKeySize := viper.GetInt("with-max-key-size") + maxValueSize := viper.GetInt("with-max-value-size") + + db, err := bitcask.Open( + path, + bitcask.WithMaxDatafileSize(maxDatafileSize), + bitcask.WithMaxKeySize(maxKeySize), + bitcask.WithMaxValueSize(maxValueSize), + ) + if err != nil { + log.WithError(err).Error("error opening database") + os.Exit(1) + } + defer db.Close() + + os.Exit(0) + }, +} + +func init() { + RootCmd.AddCommand(initdbCmd) + + initdbCmd.PersistentFlags().IntP( + "with-max-datafile-size", "", bitcask.DefaultMaxDatafileSize, + "Maximum size of each datafile", + ) + initdbCmd.PersistentFlags().IntP( + "with-max-key-size", "", bitcask.DefaultMaxKeySize, + "Maximum size of each key", + ) + initdbCmd.PersistentFlags().IntP( + "with-max-value-size", "", bitcask.DefaultMaxValueSize, + "Maximum size of each value", + ) +} diff --git a/options.go b/options.go index 8b32733..d38894d 100644 --- a/options.go +++ b/options.go @@ -1,6 +1,11 @@ package bitcask -import "errors" +import ( + "encoding/json" + "errors" + "io/ioutil" + "path/filepath" +) const ( // DefaultMaxDatafileSize is the default maximum datafile size in bytes @@ -29,6 +34,43 @@ type config struct { maxConcurrency *int } +func (c *config) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + MaxDatafileSize int `json:"max_datafile_size"` + MaxKeySize int `json:"max_key_size"` + MaxValueSize int `json:"max_value_size"` + }{ + MaxDatafileSize: c.maxDatafileSize, + MaxKeySize: c.maxKeySize, + MaxValueSize: c.maxValueSize, + }) +} + +func getConfig(path string) (*config, error) { + type Config struct { + MaxDatafileSize int `json:"max_datafile_size"` + MaxKeySize int `json:"max_key_size"` + MaxValueSize int `json:"max_value_size"` + } + + var cfg Config + + data, err := ioutil.ReadFile(filepath.Join(path, "config.json")) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, err + } + + return &config{ + maxDatafileSize: cfg.MaxDatafileSize, + maxKeySize: cfg.MaxKeySize, + maxValueSize: cfg.MaxValueSize, + }, nil +} + func newDefaultConfig() *config { return &config{ maxDatafileSize: DefaultMaxDatafileSize,