diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..6f6b203 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +* @hashicorp/consul-core-reviewers + +/.release/ @hashicorp/release-engineering +/.github/workflows/ci.yml @hashicorp/release-engineering diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..efba123 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 + +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e29e8d9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: ci + +on: + pull_request: + branches: ["master"] + push: + branches: ["master"] + tags: ["*"] + +jobs: + go-fmt-and-vet: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # pin@v3.3.0 + - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@v3.5.0 + with: + go-version-file: "go.mod" + cache: true + - run: | + files=$(go fmt ./...) + if [ -n "$files" ]; then + echo "The following file(s) do not conform to go fmt:" + echo "$files" + exit 1 + fi + - run: go vet ./... + + go-test: + needs: go-fmt-and-vet + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # pin@v3.3.0 + - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@v3.5.0 + with: + go-version-file: "go.mod" + cache: true + - run: go test -race ./... diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5835741..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go - -go: - - 1.6 - - 1.7 - - tip - -install: make deps -script: - - make test diff --git a/README.md b/README.md index 46e386a..d06a2f7 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,9 @@ -raft-boltdb +raft-boltdb/v3 =========== -This repository provides the `raftboltdb` package. The package exports the -`BoltStore` which is an implementation of both a `LogStore` and `StableStore`. +This implementation uses the maintained version of BoltDB, [BBolt](https://github.com/etcd-io/bbolt). This is the primary version of `raft-boltdb` and should be used whenever possible. -It is meant to be used as a backend for the `raft` [package -here](https://github.com/hashicorp/raft). - -This implementation uses [BoltDB](https://github.com/boltdb/bolt). BoltDB is -a simple key/value store implemented in pure Go, and inspired by LMDB. +There is no breaking API change with v2 of the library outside of removing the v1 to v2 migration code. V3 does change the module used for metrics from github.com/armon/go-metrics to github.com/hashicorp/go-metrics. You should ensure that this version of the library is only used with a version of Raft and your application that are using github.com/hashicorp/go-metrics. If not, then the metrics emission will not work properly. ## Metrics diff --git a/bolt_store.go b/bolt_store.go index e624051..2b69dc7 100644 --- a/bolt_store.go +++ b/bolt_store.go @@ -4,9 +4,9 @@ import ( "errors" "time" - metrics "github.com/armon/go-metrics" - "github.com/boltdb/bolt" + metrics "github.com/hashicorp/go-metrics" "github.com/hashicorp/raft" + "go.etcd.io/bbolt" ) const ( @@ -24,25 +24,25 @@ var ( ErrKeyNotFound = errors.New("not found") ) -// BoltStore provides access to BoltDB for Raft to store and retrieve +// BoltStore provides access to Bbolt for Raft to store and retrieve // log entries. It also provides key/value storage, and can be used as // a LogStore and StableStore. type BoltStore struct { // conn is the underlying handle to the db. - conn *bolt.DB + conn *bbolt.DB // The path to the Bolt database file path string } -// Options contains all the configuration used to open the BoltDB +// Options contains all the configuration used to open the Bbolt type Options struct { - // Path is the file path to the BoltDB to use + // Path is the file path to the Bbolt to use Path string - // BoltOptions contains any specific BoltDB options you might + // BoltOptions contains any specific Bbolt options you might // want to specify [e.g. open timeout] - BoltOptions *bolt.Options + BoltOptions *bbolt.Options // NoSync causes the database to skip fsync calls after each // write to the log. This is unsafe, so it should be used @@ -62,10 +62,10 @@ func NewBoltStore(path string) (*BoltStore, error) { return New(Options{Path: path}) } -// New uses the supplied options to open the BoltDB and prepare it for use as a raft backend. +// New uses the supplied options to open the Bbolt and prepare it for use as a raft backend. func New(options Options) (*BoltStore, error) { // Try to connect - handle, err := bolt.Open(options.Path, dbFileMode, options.BoltOptions) + handle, err := bbolt.Open(options.Path, dbFileMode, options.BoltOptions) if err != nil { return nil, err } @@ -107,6 +107,10 @@ func (b *BoltStore) initialize() error { return tx.Commit() } +func (b *BoltStore) Stats() bbolt.Stats { + return b.conn.Stats() +} + // Close is used to gracefully close the DB connection. func (b *BoltStore) Close() error { return b.conn.Close() @@ -144,8 +148,10 @@ func (b *BoltStore) LastIndex() (uint64, error) { } } -// GetLog is used to retrieve a log from BoltDB at a given index. +// GetLog is used to retrieve a log from Bbolt at a given index. func (b *BoltStore) GetLog(idx uint64, log *raft.Log) error { + defer metrics.MeasureSince([]string{"raft", "boltdb", "getLog"}, time.Now()) + tx, err := b.conn.Begin(false) if err != nil { return err @@ -169,6 +175,7 @@ func (b *BoltStore) StoreLog(log *raft.Log) error { // StoreLogs is used to store a set of raft logs func (b *BoltStore) StoreLogs(logs []*raft.Log) error { now := time.Now() + tx, err := b.conn.Begin(true) if err != nil { return err diff --git a/bolt_store_test.go b/bolt_store_test.go index 12b09b2..52b74fe 100644 --- a/bolt_store_test.go +++ b/bolt_store_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/boltdb/bolt" "github.com/hashicorp/raft" + "go.etcd.io/bbolt" ) func testBoltStore(t testing.TB) *BoltStore { @@ -54,7 +54,7 @@ func TestBoltOptionsTimeout(t *testing.T) { defer os.Remove(fh.Name()) options := Options{ Path: fh.Name(), - BoltOptions: &bolt.Options{ + BoltOptions: &bbolt.Options{ Timeout: time.Second / 10, }, } @@ -102,7 +102,7 @@ func TestBoltOptionsReadOnly(t *testing.T) { store.Close() options := Options{ Path: fh.Name(), - BoltOptions: &bolt.Options{ + BoltOptions: &bbolt.Options{ Timeout: time.Second / 10, ReadOnly: true, }, @@ -123,8 +123,8 @@ func TestBoltOptionsReadOnly(t *testing.T) { } // Attempt to store the log, should fail on a read-only store err = roStore.StoreLog(log) - if err != bolt.ErrDatabaseReadOnly { - t.Errorf("expecting error %v, but got %v", bolt.ErrDatabaseReadOnly, err) + if err != bbolt.ErrDatabaseReadOnly { + t.Errorf("expecting error %v, but got %v", bbolt.ErrDatabaseReadOnly, err) } } @@ -156,7 +156,7 @@ func TestNewBoltStore(t *testing.T) { } // Ensure our tables were created - db, err := bolt.Open(fh.Name(), dbFileMode, nil) + db, err := bbolt.Open(fh.Name(), dbFileMode, nil) if err != nil { t.Fatalf("err: %s", err) } @@ -164,10 +164,10 @@ func TestNewBoltStore(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if _, err := tx.CreateBucket([]byte(dbLogs)); err != bolt.ErrBucketExists { + if _, err := tx.CreateBucket([]byte(dbLogs)); err != bbolt.ErrBucketExists { t.Fatalf("bad: %v", err) } - if _, err := tx.CreateBucket([]byte(dbConf)); err != bolt.ErrBucketExists { + if _, err := tx.CreateBucket([]byte(dbConf)); err != bbolt.ErrBucketExists { t.Fatalf("bad: %v", err) } } diff --git a/go.mod b/go.mod index 126cf1f..cd78fae 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ -module github.com/hashicorp/raft-boltdb +module github.com/hashicorp/raft-boltdb/v3 go 1.16 require ( - github.com/armon/go-metrics v0.3.8 // indirect github.com/boltdb/bolt v1.3.1 + github.com/hashicorp/go-metrics v0.5.1 github.com/hashicorp/go-msgpack v0.5.5 github.com/hashicorp/raft v1.1.0 + go.etcd.io/bbolt v1.3.5 ) diff --git a/go.sum b/go.sum index 1abf693..8aaa406 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-metrics v0.3.8 h1:oOxq3KPj0WhCuy50EhzwiyMyG2ovRQZpZLXQuOh2a/M= -github.com/armon/go-metrics v0.3.8/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -36,6 +34,8 @@ github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxu github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/Hqck/U= +github.com/hashicorp/go-metrics v0.5.1/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -84,10 +84,12 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -101,6 +103,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -109,4 +113,5 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/metrics.go b/metrics.go index 9cb7eb8..8b7bb8f 100644 --- a/metrics.go +++ b/metrics.go @@ -4,8 +4,8 @@ import ( "context" "time" - metrics "github.com/armon/go-metrics" - "github.com/boltdb/bolt" + metrics "github.com/hashicorp/go-metrics" + "go.etcd.io/bbolt" ) const ( @@ -33,7 +33,7 @@ func (b *BoltStore) RunMetrics(ctx context.Context, interval time.Duration) { } } -func (b *BoltStore) emitMetrics(prev *bolt.Stats) *bolt.Stats { +func (b *BoltStore) emitMetrics(prev *bbolt.Stats) *bbolt.Stats { newStats := b.conn.Stats() stats := newStats diff --git a/v2/README.md b/v2/README.md deleted file mode 100644 index 22bad34..0000000 --- a/v2/README.md +++ /dev/null @@ -1,37 +0,0 @@ -raft-boltdb/v2 -=========== - -This implementation uses the maintained version of BoltDB, [BBolt](https://github.com/etcd-io/bbolt). This is the primary version of `raft-boltdb` and should be used whenever possible. - -There is no breaking API change to the library. However, there is the potential for disk format incompatibilities so it was decided to be conservative and making it a separate import path. This separate import path will allow both versions (original and v2) to be imported to perform a safe in-place upgrade of old files read with the old version and written back out with the new one. - -## Metrics - -The raft-boldb library emits a number of metrics utilizing github.com/armon/go-metrics. Those metrics are detailed in the following table. One note is that the application which pulls in this library may add its own prefix to the metric names. For example within [Consul](https://github.com/hashicorp/consul), the metrics will be prefixed with `consul.`. - -| Metric | Unit | Type | Description | -| ----------------------------------- | ------------:| -------:|:--------------------- | -| `raft.boltdb.freelistBytes` | bytes | gauge | Represents the number of bytes necessary to encode the freelist metadata. When [`raft_boltdb.NoFreelistSync`](/docs/agent/options#NoFreelistSync) is set to `false` these metadata bytes must also be written to disk for each committed log. | -| `raft.boltdb.freePageBytes` | bytes | gauge | Represents the number of bytes of free space within the raft.db file. | -| `raft.boltdb.getLog` | ms | timer | Measures the amount of time spent reading logs from the db. | -| `raft.boltdb.logBatchSize` | bytes | sample | Measures the total size in bytes of logs being written to the db in a single batch. | -| `raft.boltdb.logsPerBatch` | logs | sample | Measures the number of logs being written per batch to the db. | -| `raft.boltdb.logSize` | bytes | sample | Measures the size of logs being written to the db. | -| `raft.boltdb.numFreePages` | pages | gauge | Represents the number of free pages within the raft.db file. | -| `raft.boltdb.numPendingPages` | pages | gauge | Represents the number of pending pages within the raft.db that will soon become free. | -| `raft.boltdb.openReadTxn` | transactions | gauge | Represents the number of open read transactions against the db | -| `raft.boltdb.storeLogs` | ms | timer | Measures the amount of time spent writing logs to the db. | -| `raft.boltdb.totalReadTxn` | transactions | gauge | Represents the total number of started read transactions against the db | -| `raft.boltdb.txstats.cursorCount` | cursors | counter | Counts the number of cursors created since Consul was started. | -| `raft.boltdb.txstats.nodeCount` | allocations | counter | Counts the number of node allocations within the db since Consul was started. | -| `raft.boltdb.txstats.nodeDeref` | dereferences | counter | Counts the number of node dereferences in the db since Consul was started. | -| `raft.boltdb.txstats.pageAlloc` | bytes | gauge | Represents the number of bytes allocated within the db since Consul was started. Note that this does not take into account space having been freed and reused. In that case, the value of this metric will still increase. | -| `raft.boltdb.txstats.pageCount` | pages | gauge | Represents the number of pages allocated since Consul was started. Note that this does not take into account space having been freed and reused. In that case, the value of this metric will still increase. | -| `raft.boltdb.txstats.rebalance` | rebalances | counter | Counts the number of node rebalances performed in the db since Consul was started. | -| `raft.boltdb.txstats.rebalanceTime` | ms | timer | Measures the time spent rebalancing nodes in the db. | -| `raft.boltdb.txstats.spill` | spills | counter | Counts the number of nodes spilled in the db since Consul was started. | -| `raft.boltdb.txstats.spillTime` | ms | timer | Measures the time spent spilling nodes in the db. | -| `raft.boltdb.txstats.split` | splits | counter | Counts the number of nodes split in the db since Consul was started. | -| `raft.boltdb.txstats.write` | writes | counter | Counts the number of writes to the db since Consul was started. | -| `raft.boltdb.txstats.writeTime` | ms | timer | Measures the amount of time spent performing writes to the db. | -| `raft.boltdb.writeCapacity` | logs/second | sample | Theoretical write capacity in terms of the number of logs that can be written per second. Each sample outputs what the capacity would be if future batched log write operations were similar to this one. This similarity encompasses 4 things: batch size, byte size, disk performance and boltdb performance. While none of these will be static and its highly likely individual samples of this metric will vary, aggregating this metric over a larger time window should provide a decent picture into how this BoltDB store can perform | diff --git a/v2/bench_test.go b/v2/bench_test.go deleted file mode 100644 index b860706..0000000 --- a/v2/bench_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package raftboltdb - -import ( - "os" - "testing" - - "github.com/hashicorp/raft/bench" -) - -func BenchmarkBoltStore_FirstIndex(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.FirstIndex(b, store) -} - -func BenchmarkBoltStore_LastIndex(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.LastIndex(b, store) -} - -func BenchmarkBoltStore_GetLog(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.GetLog(b, store) -} - -func BenchmarkBoltStore_StoreLog(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.StoreLog(b, store) -} - -func BenchmarkBoltStore_StoreLogs(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.StoreLogs(b, store) -} - -func BenchmarkBoltStore_DeleteRange(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.DeleteRange(b, store) -} - -func BenchmarkBoltStore_Set(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.Set(b, store) -} - -func BenchmarkBoltStore_Get(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.Get(b, store) -} - -func BenchmarkBoltStore_SetUint64(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.SetUint64(b, store) -} - -func BenchmarkBoltStore_GetUint64(b *testing.B) { - store := testBoltStore(b) - defer store.Close() - defer os.Remove(store.path) - - raftbench.GetUint64(b, store) -} diff --git a/v2/bolt_store.go b/v2/bolt_store.go deleted file mode 100644 index 5414b29..0000000 --- a/v2/bolt_store.go +++ /dev/null @@ -1,363 +0,0 @@ -package raftboltdb - -import ( - "errors" - "fmt" - "os" - "time" - - metrics "github.com/armon/go-metrics" - v1 "github.com/boltdb/bolt" - "github.com/hashicorp/raft" - "go.etcd.io/bbolt" -) - -const ( - // Permissions to use on the db file. This is only used if the - // database file does not exist and needs to be created. - dbFileMode = 0600 -) - -var ( - // Bucket names we perform transactions in - dbLogs = []byte("logs") - dbConf = []byte("conf") - - // An error indicating a given key does not exist - ErrKeyNotFound = errors.New("not found") -) - -// BoltStore provides access to Bbolt for Raft to store and retrieve -// log entries. It also provides key/value storage, and can be used as -// a LogStore and StableStore. -type BoltStore struct { - // conn is the underlying handle to the db. - conn *bbolt.DB - - // The path to the Bolt database file - path string -} - -// Options contains all the configuration used to open the Bbolt -type Options struct { - // Path is the file path to the Bbolt to use - Path string - - // BoltOptions contains any specific Bbolt options you might - // want to specify [e.g. open timeout] - BoltOptions *bbolt.Options - - // NoSync causes the database to skip fsync calls after each - // write to the log. This is unsafe, so it should be used - // with caution. - NoSync bool -} - -// readOnly returns true if the contained bolt options say to open -// the DB in readOnly mode [this can be useful to tools that want -// to examine the log] -func (o *Options) readOnly() bool { - return o != nil && o.BoltOptions != nil && o.BoltOptions.ReadOnly -} - -// NewBoltStore takes a file path and returns a connected Raft backend. -func NewBoltStore(path string) (*BoltStore, error) { - return New(Options{Path: path}) -} - -// New uses the supplied options to open the Bbolt and prepare it for use as a raft backend. -func New(options Options) (*BoltStore, error) { - // Try to connect - handle, err := bbolt.Open(options.Path, dbFileMode, options.BoltOptions) - if err != nil { - return nil, err - } - handle.NoSync = options.NoSync - - // Create the new store - store := &BoltStore{ - conn: handle, - path: options.Path, - } - - // If the store was opened read-only, don't try and create buckets - if !options.readOnly() { - // Set up our buckets - if err := store.initialize(); err != nil { - store.Close() - return nil, err - } - } - return store, nil -} - -// initialize is used to set up all of the buckets. -func (b *BoltStore) initialize() error { - tx, err := b.conn.Begin(true) - if err != nil { - return err - } - defer tx.Rollback() - - // Create all the buckets - if _, err := tx.CreateBucketIfNotExists(dbLogs); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists(dbConf); err != nil { - return err - } - - return tx.Commit() -} - -func (b *BoltStore) Stats() bbolt.Stats { - return b.conn.Stats() -} - -// Close is used to gracefully close the DB connection. -func (b *BoltStore) Close() error { - return b.conn.Close() -} - -// FirstIndex returns the first known index from the Raft log. -func (b *BoltStore) FirstIndex() (uint64, error) { - tx, err := b.conn.Begin(false) - if err != nil { - return 0, err - } - defer tx.Rollback() - - curs := tx.Bucket(dbLogs).Cursor() - if first, _ := curs.First(); first == nil { - return 0, nil - } else { - return bytesToUint64(first), nil - } -} - -// LastIndex returns the last known index from the Raft log. -func (b *BoltStore) LastIndex() (uint64, error) { - tx, err := b.conn.Begin(false) - if err != nil { - return 0, err - } - defer tx.Rollback() - - curs := tx.Bucket(dbLogs).Cursor() - if last, _ := curs.Last(); last == nil { - return 0, nil - } else { - return bytesToUint64(last), nil - } -} - -// GetLog is used to retrieve a log from Bbolt at a given index. -func (b *BoltStore) GetLog(idx uint64, log *raft.Log) error { - defer metrics.MeasureSince([]string{"raft", "boltdb", "getLog"}, time.Now()) - - tx, err := b.conn.Begin(false) - if err != nil { - return err - } - defer tx.Rollback() - - bucket := tx.Bucket(dbLogs) - val := bucket.Get(uint64ToBytes(idx)) - - if val == nil { - return raft.ErrLogNotFound - } - return decodeMsgPack(val, log) -} - -// StoreLog is used to store a single raft log -func (b *BoltStore) StoreLog(log *raft.Log) error { - return b.StoreLogs([]*raft.Log{log}) -} - -// StoreLogs is used to store a set of raft logs -func (b *BoltStore) StoreLogs(logs []*raft.Log) error { - now := time.Now() - - tx, err := b.conn.Begin(true) - if err != nil { - return err - } - defer tx.Rollback() - - batchSize := 0 - for _, log := range logs { - key := uint64ToBytes(log.Index) - val, err := encodeMsgPack(log) - if err != nil { - return err - } - - logLen := val.Len() - bucket := tx.Bucket(dbLogs) - if err := bucket.Put(key, val.Bytes()); err != nil { - return err - } - batchSize += logLen - metrics.AddSample([]string{"raft", "boltdb", "logSize"}, float32(logLen)) - } - - metrics.AddSample([]string{"raft", "boltdb", "logsPerBatch"}, float32(len(logs))) - metrics.AddSample([]string{"raft", "boltdb", "logBatchSize"}, float32(batchSize)) - // Both the deferral and the inline function are important for this metrics - // accuracy. Deferral allows us to calculate the metric after the tx.Commit - // has finished and thus account for all the processing of the operation. - // The inlined function ensures that we do not calculate the time.Since(now) - // at the time of deferral but rather when the go runtime executes the - // deferred function. - defer func() { - metrics.AddSample([]string{"raft", "boltdb", "writeCapacity"}, (float32(1_000_000_000)/float32(time.Since(now).Nanoseconds()))*float32(len(logs))) - metrics.MeasureSince([]string{"raft", "boltdb", "storeLogs"}, now) - }() - - return tx.Commit() -} - -// DeleteRange is used to delete logs within a given range inclusively. -func (b *BoltStore) DeleteRange(min, max uint64) error { - minKey := uint64ToBytes(min) - - tx, err := b.conn.Begin(true) - if err != nil { - return err - } - defer tx.Rollback() - - curs := tx.Bucket(dbLogs).Cursor() - for k, _ := curs.Seek(minKey); k != nil; k, _ = curs.Next() { - // Handle out-of-range log index - if bytesToUint64(k) > max { - break - } - - // Delete in-range log index - if err := curs.Delete(); err != nil { - return err - } - } - - return tx.Commit() -} - -// Set is used to set a key/value set outside of the raft log -func (b *BoltStore) Set(k, v []byte) error { - tx, err := b.conn.Begin(true) - if err != nil { - return err - } - defer tx.Rollback() - - bucket := tx.Bucket(dbConf) - if err := bucket.Put(k, v); err != nil { - return err - } - - return tx.Commit() -} - -// Get is used to retrieve a value from the k/v store by key -func (b *BoltStore) Get(k []byte) ([]byte, error) { - tx, err := b.conn.Begin(false) - if err != nil { - return nil, err - } - defer tx.Rollback() - - bucket := tx.Bucket(dbConf) - val := bucket.Get(k) - - if val == nil { - return nil, ErrKeyNotFound - } - return append([]byte(nil), val...), nil -} - -// SetUint64 is like Set, but handles uint64 values -func (b *BoltStore) SetUint64(key []byte, val uint64) error { - return b.Set(key, uint64ToBytes(val)) -} - -// GetUint64 is like Get, but handles uint64 values -func (b *BoltStore) GetUint64(key []byte) (uint64, error) { - val, err := b.Get(key) - if err != nil { - return 0, err - } - return bytesToUint64(val), nil -} - -// Sync performs an fsync on the database file handle. This is not necessary -// under normal operation unless NoSync is enabled, in which this forces the -// database file to sync against the disk. -func (b *BoltStore) Sync() error { - return b.conn.Sync() -} - -// MigrateToV2 reads in the source file path of a BoltDB file -// and outputs all the data migrated to a Bbolt destination file -func MigrateToV2(source, destination string) (*BoltStore, error) { - _, err := os.Stat(destination) - if err == nil { - return nil, fmt.Errorf("file exists in destination %v", destination) - } - - srcDb, err := v1.Open(source, dbFileMode, &v1.Options{ - ReadOnly: true, - Timeout: 1 * time.Minute, - }) - if err != nil { - return nil, fmt.Errorf("failed opening source database: %v", err) - } - - //Start a connection to the source - srctx, err := srcDb.Begin(false) - if err != nil { - return nil, fmt.Errorf("failed connecting to source database: %v", err) - } - defer srctx.Rollback() - - //Create the destination - destDb, err := New(Options{Path: destination}) - if err != nil { - return nil, fmt.Errorf("failed creating destination database: %v", err) - } - //Start a connection to the new - desttx, err := destDb.conn.Begin(true) - if err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed connecting to destination database: %v", err) - } - - defer desttx.Rollback() - - //Loop over both old buckets and set them in the new - buckets := [][]byte{dbConf, dbLogs} - for _, b := range buckets { - srcB := srctx.Bucket(b) - destB := desttx.Bucket(b) - err = srcB.ForEach(func(k, v []byte) error { - return destB.Put(k, v) - }) - if err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed to copy %v bucket: %v", string(b), err) - } - } - - //If the commit fails, clean up - if err := desttx.Commit(); err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed commiting data to destination: %v", err) - } - - return destDb, nil - -} diff --git a/v2/bolt_store_test.go b/v2/bolt_store_test.go deleted file mode 100644 index 7a55de0..0000000 --- a/v2/bolt_store_test.go +++ /dev/null @@ -1,473 +0,0 @@ -package raftboltdb - -import ( - "bytes" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "testing" - "time" - - "github.com/hashicorp/raft" - v1 "github.com/hashicorp/raft-boltdb" - "go.etcd.io/bbolt" -) - -func testBoltStore(t testing.TB) *BoltStore { - fh, err := ioutil.TempFile("", "bolt") - if err != nil { - t.Fatalf("err: %s", err) - } - os.Remove(fh.Name()) - - // Successfully creates and returns a store - store, err := NewBoltStore(fh.Name()) - if err != nil { - t.Fatalf("err: %s", err) - } - - return store -} - -func testRaftLog(idx uint64, data string) *raft.Log { - return &raft.Log{ - Data: []byte(data), - Index: idx, - } -} - -func TestBoltStore_Implements(t *testing.T) { - var store interface{} = &BoltStore{} - if _, ok := store.(raft.StableStore); !ok { - t.Fatalf("BoltStore does not implement raft.StableStore") - } - if _, ok := store.(raft.LogStore); !ok { - t.Fatalf("BoltStore does not implement raft.LogStore") - } -} - -func TestBoltOptionsTimeout(t *testing.T) { - fh, err := ioutil.TempFile("", "bolt") - if err != nil { - t.Fatalf("err: %s", err) - } - os.Remove(fh.Name()) - defer os.Remove(fh.Name()) - options := Options{ - Path: fh.Name(), - BoltOptions: &bbolt.Options{ - Timeout: time.Second / 10, - }, - } - store, err := New(options) - if err != nil { - t.Fatalf("err: %v", err) - } - defer store.Close() - // trying to open it again should timeout - doneCh := make(chan error, 1) - go func() { - _, err := New(options) - doneCh <- err - }() - select { - case err := <-doneCh: - if err == nil || err.Error() != "timeout" { - t.Errorf("Expected timeout error but got %v", err) - } - case <-time.After(5 * time.Second): - t.Errorf("Gave up waiting for timeout response") - } -} - -func TestBoltOptionsReadOnly(t *testing.T) { - fh, err := ioutil.TempFile("", "bolt") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.Remove(fh.Name()) - store, err := NewBoltStore(fh.Name()) - if err != nil { - t.Fatalf("err: %s", err) - } - // Create the log - log := &raft.Log{ - Data: []byte("log1"), - Index: 1, - } - // Attempt to store the log - if err := store.StoreLog(log); err != nil { - t.Fatalf("err: %s", err) - } - - store.Close() - options := Options{ - Path: fh.Name(), - BoltOptions: &bbolt.Options{ - Timeout: time.Second / 10, - ReadOnly: true, - }, - } - roStore, err := New(options) - if err != nil { - t.Fatalf("err: %s", err) - } - defer roStore.Close() - result := new(raft.Log) - if err := roStore.GetLog(1, result); err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure the log comes back the same - if !reflect.DeepEqual(log, result) { - t.Errorf("bad: %v", result) - } - // Attempt to store the log, should fail on a read-only store - err = roStore.StoreLog(log) - if err != bbolt.ErrDatabaseReadOnly { - t.Errorf("expecting error %v, but got %v", bbolt.ErrDatabaseReadOnly, err) - } -} - -func TestNewBoltStore(t *testing.T) { - fh, err := ioutil.TempFile("", "bolt") - if err != nil { - t.Fatalf("err: %s", err) - } - os.Remove(fh.Name()) - defer os.Remove(fh.Name()) - - // Successfully creates and returns a store - store, err := NewBoltStore(fh.Name()) - if err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure the file was created - if store.path != fh.Name() { - t.Fatalf("unexpected file path %q", store.path) - } - if _, err := os.Stat(fh.Name()); err != nil { - t.Fatalf("err: %s", err) - } - - // Close the store so we can open again - if err := store.Close(); err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure our tables were created - db, err := bbolt.Open(fh.Name(), dbFileMode, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - tx, err := db.Begin(true) - if err != nil { - t.Fatalf("err: %s", err) - } - if _, err := tx.CreateBucket([]byte(dbLogs)); err != bbolt.ErrBucketExists { - t.Fatalf("bad: %v", err) - } - if _, err := tx.CreateBucket([]byte(dbConf)); err != bbolt.ErrBucketExists { - t.Fatalf("bad: %v", err) - } -} - -func TestBoltStore_FirstIndex(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Should get 0 index on empty log - idx, err := store.FirstIndex() - if err != nil { - t.Fatalf("err: %s", err) - } - if idx != 0 { - t.Fatalf("bad: %v", idx) - } - - // Set a mock raft log - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - testRaftLog(3, "log3"), - } - if err := store.StoreLogs(logs); err != nil { - t.Fatalf("bad: %s", err) - } - - // Fetch the first Raft index - idx, err = store.FirstIndex() - if err != nil { - t.Fatalf("err: %s", err) - } - if idx != 1 { - t.Fatalf("bad: %d", idx) - } -} - -func TestBoltStore_LastIndex(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Should get 0 index on empty log - idx, err := store.LastIndex() - if err != nil { - t.Fatalf("err: %s", err) - } - if idx != 0 { - t.Fatalf("bad: %v", idx) - } - - // Set a mock raft log - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - testRaftLog(3, "log3"), - } - if err := store.StoreLogs(logs); err != nil { - t.Fatalf("bad: %s", err) - } - - // Fetch the last Raft index - idx, err = store.LastIndex() - if err != nil { - t.Fatalf("err: %s", err) - } - if idx != 3 { - t.Fatalf("bad: %d", idx) - } -} - -func TestBoltStore_GetLog(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - log := new(raft.Log) - - // Should return an error on non-existent log - if err := store.GetLog(1, log); err != raft.ErrLogNotFound { - t.Fatalf("expected raft log not found error, got: %v", err) - } - - // Set a mock raft log - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - testRaftLog(3, "log3"), - } - if err := store.StoreLogs(logs); err != nil { - t.Fatalf("bad: %s", err) - } - - // Should return the proper log - if err := store.GetLog(2, log); err != nil { - t.Fatalf("err: %s", err) - } - if !reflect.DeepEqual(log, logs[1]) { - t.Fatalf("bad: %#v", log) - } -} - -func TestBoltStore_SetLog(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Create the log - log := &raft.Log{ - Data: []byte("log1"), - Index: 1, - } - - // Attempt to store the log - if err := store.StoreLog(log); err != nil { - t.Fatalf("err: %s", err) - } - - // Retrieve the log again - result := new(raft.Log) - if err := store.GetLog(1, result); err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure the log comes back the same - if !reflect.DeepEqual(log, result) { - t.Fatalf("bad: %v", result) - } -} - -func TestBoltStore_SetLogs(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Create a set of logs - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - } - - // Attempt to store the logs - if err := store.StoreLogs(logs); err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure we stored them all - result1, result2 := new(raft.Log), new(raft.Log) - if err := store.GetLog(1, result1); err != nil { - t.Fatalf("err: %s", err) - } - if !reflect.DeepEqual(logs[0], result1) { - t.Fatalf("bad: %#v", result1) - } - if err := store.GetLog(2, result2); err != nil { - t.Fatalf("err: %s", err) - } - if !reflect.DeepEqual(logs[1], result2) { - t.Fatalf("bad: %#v", result2) - } -} - -func TestBoltStore_DeleteRange(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Create a set of logs - log1 := testRaftLog(1, "log1") - log2 := testRaftLog(2, "log2") - log3 := testRaftLog(3, "log3") - logs := []*raft.Log{log1, log2, log3} - - // Attempt to store the logs - if err := store.StoreLogs(logs); err != nil { - t.Fatalf("err: %s", err) - } - - // Attempt to delete a range of logs - if err := store.DeleteRange(1, 2); err != nil { - t.Fatalf("err: %s", err) - } - - // Ensure the logs were deleted - if err := store.GetLog(1, new(raft.Log)); err != raft.ErrLogNotFound { - t.Fatalf("should have deleted log1") - } - if err := store.GetLog(2, new(raft.Log)); err != raft.ErrLogNotFound { - t.Fatalf("should have deleted log2") - } -} - -func TestBoltStore_Set_Get(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Returns error on non-existent key - if _, err := store.Get([]byte("bad")); err != ErrKeyNotFound { - t.Fatalf("expected not found error, got: %q", err) - } - - k, v := []byte("hello"), []byte("world") - - // Try to set a k/v pair - if err := store.Set(k, v); err != nil { - t.Fatalf("err: %s", err) - } - - // Try to read it back - val, err := store.Get(k) - if err != nil { - t.Fatalf("err: %s", err) - } - if !bytes.Equal(val, v) { - t.Fatalf("bad: %v", val) - } -} - -func TestBoltStore_SetUint64_GetUint64(t *testing.T) { - store := testBoltStore(t) - defer store.Close() - defer os.Remove(store.path) - - // Returns error on non-existent key - if _, err := store.GetUint64([]byte("bad")); err != ErrKeyNotFound { - t.Fatalf("expected not found error, got: %q", err) - } - - k, v := []byte("abc"), uint64(123) - - // Attempt to set the k/v pair - if err := store.SetUint64(k, v); err != nil { - t.Fatalf("err: %s", err) - } - - // Read back the value - val, err := store.GetUint64(k) - if err != nil { - t.Fatalf("err: %s", err) - } - if val != v { - t.Fatalf("bad: %v", val) - } -} - -func TestBoltStore_MigrateToV2(t *testing.T) { - - dir, err := ioutil.TempDir("", t.Name()) - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.RemoveAll(dir) - - srcFile := filepath.Join(dir, "/sourcepath") - destFile := filepath.Join(dir, "/destpath") - - // Successfully creates and returns a store - srcDb, err := v1.NewBoltStore(srcFile) - if err != nil { - t.Fatalf("failed creating source database: %s", err) - } - defer srcDb.Close() - - // Set a mock raft log - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - testRaftLog(3, "log3"), - } - - //Store logs source - if err := srcDb.StoreLogs(logs); err != nil { - t.Fatalf("failed storing logs in source database: %s", err) - } - srcResult := new(raft.Log) - if err := srcDb.GetLog(2, srcResult); err != nil { - t.Fatalf("failed getting log from source database: %s", err) - } - - if err := srcDb.Close(); err != nil { - t.Fatalf("failed closing source database: %s", err) - } - - destDb, err := MigrateToV2(srcFile, destFile) - if err != nil { - t.Fatalf("did not migrate successfully, err %v", err) - } - defer destDb.Close() - - destResult := new(raft.Log) - if err := destDb.GetLog(2, destResult); err != nil { - t.Fatalf("failed getting log from destination database: %s", err) - } - - if !reflect.DeepEqual(srcResult, destResult) { - t.Errorf("BoltDB log did not equal Bbolt log, Boltdb %v, Bbolt: %v", srcResult, destResult) - } - -} diff --git a/v2/go.mod b/v2/go.mod deleted file mode 100644 index 2828c1a..0000000 --- a/v2/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module github.com/hashicorp/raft-boltdb/v2 - -go 1.16 - -require ( - github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 - github.com/boltdb/bolt v1.3.1 - github.com/hashicorp/go-msgpack v0.5.5 - github.com/hashicorp/raft v1.1.0 - github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea - go.etcd.io/bbolt v1.3.5 -) diff --git a/v2/go.sum b/v2/go.sum deleted file mode 100644 index ac5005f..0000000 --- a/v2/go.sum +++ /dev/null @@ -1,49 +0,0 @@ -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/raft v1.1.0 h1:qPMePEczgbkiQsqCsRfuHRqvDUO+zmAInDaD5ptXlq0= -github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= -github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea h1:RxcPJuutPRM8PUOyiweMmkuNO+RJyfy2jds2gfvgNmU= -github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea/go.mod h1:qRd6nFJYYS6Iqnc/8HcUmko2/2Gw8qTFEmxDLii6W5I= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/v2/metrics.go b/v2/metrics.go deleted file mode 100644 index 1480ff7..0000000 --- a/v2/metrics.go +++ /dev/null @@ -1,68 +0,0 @@ -package raftboltdb - -import ( - "context" - "time" - - metrics "github.com/armon/go-metrics" - "go.etcd.io/bbolt" -) - -const ( - defaultMetricsInterval = 5 * time.Second -) - -// RunMetrics should be executed in a go routine and will periodically emit -// metrics on the given interval until the context has been cancelled. -func (b *BoltStore) RunMetrics(ctx context.Context, interval time.Duration) { - if interval == 0 { - interval = defaultMetricsInterval - } - - tick := time.NewTicker(interval) - defer tick.Stop() - - stats := b.emitMetrics(nil) - for { - select { - case <-ctx.Done(): - return - case <-tick.C: - stats = b.emitMetrics(stats) - } - } -} - -func (b *BoltStore) emitMetrics(prev *bbolt.Stats) *bbolt.Stats { - newStats := b.conn.Stats() - - stats := newStats - if prev != nil { - stats = newStats.Sub(prev) - } - - // freelist metrics - metrics.SetGauge([]string{"raft", "boltdb", "numFreePages"}, float32(newStats.FreePageN)) - metrics.SetGauge([]string{"raft", "boltdb", "numPendingPages"}, float32(newStats.PendingPageN)) - metrics.SetGauge([]string{"raft", "boltdb", "freePageBytes"}, float32(newStats.FreeAlloc)) - metrics.SetGauge([]string{"raft", "boltdb", "freelistBytes"}, float32(newStats.FreelistInuse)) - - // txn metrics - metrics.IncrCounter([]string{"raft", "boltdb", "totalReadTxn"}, float32(stats.TxN)) - metrics.SetGauge([]string{"raft", "boltdb", "openReadTxn"}, float32(newStats.OpenTxN)) - - // tx stats - metrics.SetGauge([]string{"raft", "boltdb", "txstats", "pageCount"}, float32(newStats.TxStats.PageCount)) - metrics.SetGauge([]string{"raft", "boltdb", "txstats", "pageAlloc"}, float32(newStats.TxStats.PageAlloc)) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "cursorCount"}, float32(stats.TxStats.CursorCount)) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "nodeCount"}, float32(stats.TxStats.NodeCount)) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "nodeDeref"}, float32(stats.TxStats.NodeDeref)) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "rebalance"}, float32(stats.TxStats.Rebalance)) - metrics.AddSample([]string{"raft", "boltdb", "txstats", "rebalanceTime"}, float32(stats.TxStats.RebalanceTime.Nanoseconds())/1000000) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "split"}, float32(stats.TxStats.Split)) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "spill"}, float32(stats.TxStats.Spill)) - metrics.AddSample([]string{"raft", "boltdb", "txstats", "spillTime"}, float32(stats.TxStats.SpillTime.Nanoseconds())/1000000) - metrics.IncrCounter([]string{"raft", "boltdb", "txstats", "write"}, float32(stats.TxStats.Write)) - metrics.AddSample([]string{"raft", "boltdb", "txstats", "writeTime"}, float32(stats.TxStats.WriteTime.Nanoseconds())/1000000) - return &newStats -} diff --git a/v2/util.go b/v2/util.go deleted file mode 100644 index 68dd786..0000000 --- a/v2/util.go +++ /dev/null @@ -1,37 +0,0 @@ -package raftboltdb - -import ( - "bytes" - "encoding/binary" - - "github.com/hashicorp/go-msgpack/codec" -) - -// Decode reverses the encode operation on a byte slice input -func decodeMsgPack(buf []byte, out interface{}) error { - r := bytes.NewBuffer(buf) - hd := codec.MsgpackHandle{} - dec := codec.NewDecoder(r, &hd) - return dec.Decode(out) -} - -// Encode writes an encoded object to a new bytes buffer -func encodeMsgPack(in interface{}) (*bytes.Buffer, error) { - buf := bytes.NewBuffer(nil) - hd := codec.MsgpackHandle{} - enc := codec.NewEncoder(buf, &hd) - err := enc.Encode(in) - return buf, err -} - -// Converts bytes to an integer -func bytesToUint64(b []byte) uint64 { - return binary.BigEndian.Uint64(b) -} - -// Converts a uint to a byte slice -func uint64ToBytes(u uint64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, u) - return buf -}