diff --git a/cmd/rqlited/flags.go b/cmd/rqlited/flags.go index e1bb66cf..8fd8de30 100644 --- a/cmd/rqlited/flags.go +++ b/cmd/rqlited/flags.go @@ -143,6 +143,9 @@ type Config struct { // RaftShutdownOnRemove sets whether Raft should be shutdown if the node is removed RaftShutdownOnRemove bool + // RaftNoFreelistSync disables syncing Raft database freelist to disk + RaftNoFreelistSync bool + // CompressionSize sets request query size for compression attempt CompressionSize int @@ -223,6 +226,7 @@ func ParseFlags(name, desc string, build *BuildInfo) (*Config, error) { flag.DurationVar(&config.RaftSnapInterval, "raft-snap-int", 30*time.Second, "Snapshot threshold check interval") flag.DurationVar(&config.RaftLeaderLeaseTimeout, "raft-leader-lease-timeout", 0, "Raft leader lease timeout. Use 0s for Raft default") flag.BoolVar(&config.RaftShutdownOnRemove, "raft-remove-shutdown", false, "Shutdown Raft if node removed") + flag.BoolVar(&config.RaftNoFreelistSync, "raft-no-freelist-sync", false, "Do not syncing Raft database freelist to sync") flag.StringVar(&config.RaftLogLevel, "raft-log-level", "INFO", "Minimum log level for Raft module") flag.IntVar(&config.CompressionSize, "compression-size", 150, "Request query size for compression attempt") flag.IntVar(&config.CompressionBatch, "compression-batch", 5, "Request batch threshold for compression attempt") diff --git a/cmd/rqlited/main.go b/cmd/rqlited/main.go index 48a11525..9979166f 100644 --- a/cmd/rqlited/main.go +++ b/cmd/rqlited/main.go @@ -205,6 +205,7 @@ func createStore(cfg *Config, ln *tcp.Layer) (*store.Store, error) { str.StartupOnDisk = cfg.OnDiskStartup str.SetRequestCompression(cfg.CompressionBatch, cfg.CompressionSize) str.RaftLogLevel = cfg.RaftLogLevel + str.RaftNoFreeListSync = cfg.RaftNoFreelistSync str.ShutdownOnRemove = cfg.RaftShutdownOnRemove str.SnapshotThreshold = cfg.RaftSnapThreshold str.SnapshotInterval = cfg.RaftSnapInterval diff --git a/log/log.go b/log/log.go index 7596c5dc..00579346 100644 --- a/log/log.go +++ b/log/log.go @@ -14,10 +14,15 @@ type Log struct { } // New returns an instantiated Log object. -func New(path string) (*Log, error) { - bs, err := raftboltdb.NewBoltStore(path) +func New(path string, noFreelistSync bool) (*Log, error) { + bs, err := raftboltdb.New(raftboltdb.Options{ + BoltOptions: &bbolt.Options{ + NoFreelistSync: noFreelistSync, + }, + Path: path, + }) if err != nil { - return nil, fmt.Errorf("new bolt store: %s", err) + return nil, fmt.Errorf("new bbolt store: %s", err) } return &Log{bs}, nil } diff --git a/log/log_test.go b/log/log_test.go index e2c39ba9..44198cdb 100644 --- a/log/log_test.go +++ b/log/log_test.go @@ -13,7 +13,7 @@ func Test_LogNewEmpty(t *testing.T) { path := mustTempFile() defer os.Remove(path) - l, err := New(path) + l, err := New(path, false) if err != nil { t.Fatalf("failed to create log: %s", err) } @@ -62,7 +62,7 @@ func Test_LogNewExistNotEmpty(t *testing.T) { t.Fatalf("failed to close bolt db: %s", err) } - l, err := New(path) + l, err := New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } @@ -107,7 +107,109 @@ func Test_LogNewExistNotEmpty(t *testing.T) { t.Fatalf("failed to close bolt db: %s", err) } - l, err = New(path) + l, err = New(path, false) + if err != nil { + t.Fatalf("failed to create new log: %s", err) + } + + fi, err = l.FirstIndex() + if err != nil { + t.Fatalf("failed to get first index: %s", err) + } + if fi != 2 { + t.Fatalf("got wrong value for first index of empty log: %d", fi) + } + + li, err = l.LastIndex() + if err != nil { + t.Fatalf("failed to get last index: %s", err) + } + if li != 4 { + t.Fatalf("got wrong value for last index of empty log: %d", li) + } + + fi, li, err = l.Indexes() + if err != nil { + t.Fatalf("failed to get indexes: %s", err) + } + if fi != 2 { + t.Fatalf("got wrong value for first index of empty log: %d", fi) + } + if li != 4 { + t.Fatalf("got wrong value for last index of empty log: %d", li) + } + + if err := l.Close(); err != nil { + t.Fatalf("failed to close log: %s", err) + } +} + +func Test_LogNewExistNotEmptyNoFreelistSync(t *testing.T) { + path := mustTempFile() + defer os.Remove(path) + + // Write some entries directory to the BoltDB Raft store. + bs, err := raftboltdb.NewBoltStore(path) + if err != nil { + t.Fatalf("failed to create bolt store: %s", err) + } + for i := 4; i > 0; i-- { + if err := bs.StoreLog(&raft.Log{ + Index: uint64(i), + }); err != nil { + t.Fatalf("failed to write entry to raft log: %s", err) + } + } + if err := bs.Close(); err != nil { + t.Fatalf("failed to close bolt db: %s", err) + } + + l, err := New(path, true) + if err != nil { + t.Fatalf("failed to create new log: %s", err) + } + + fi, err := l.FirstIndex() + if err != nil { + t.Fatalf("failed to get first index: %s", err) + } + if fi != 1 { + t.Fatalf("got wrong value for first index of empty log: %d", fi) + } + + li, err := l.LastIndex() + if err != nil { + t.Fatalf("failed to get last index: %s", err) + } + if li != 4 { + t.Fatalf("got wrong value for last index of not empty log: %d", li) + } + + lci, err := l.LastCommandIndex() + if err != nil { + t.Fatalf("failed to get last command index: %s", err) + } + if lci != 4 { + t.Fatalf("got wrong value for last command index of not empty log: %d", lci) + } + + if err := l.Close(); err != nil { + t.Fatalf("failed to close log: %s", err) + } + + // Delete an entry, recheck index functionality. + bs, err = raftboltdb.NewBoltStore(path) + if err != nil { + t.Fatalf("failed to re-open bolt store: %s", err) + } + if err := bs.DeleteRange(1, 1); err != nil { + t.Fatalf("failed to delete range: %s", err) + } + if err := bs.Close(); err != nil { + t.Fatalf("failed to close bolt db: %s", err) + } + + l, err = New(path, true) if err != nil { t.Fatalf("failed to create new log: %s", err) } @@ -165,7 +267,7 @@ func Test_LogLastCommandIndexNotExist(t *testing.T) { t.Fatalf("failed to close bolt db: %s", err) } - l, err := New(path) + l, err := New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } @@ -210,7 +312,7 @@ func Test_LogLastCommandIndexNotExist(t *testing.T) { t.Fatalf("failed to close bolt db: %s", err) } - l, err = New(path) + l, err = New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } diff --git a/store/store.go b/store/store.go index 740e3298..8e961048 100644 --- a/store/store.go +++ b/store/store.go @@ -192,6 +192,7 @@ type Store struct { ElectionTimeout time.Duration ApplyTimeout time.Duration RaftLogLevel string + RaftNoFreeListSync bool numTrailingLogs uint64 @@ -304,7 +305,7 @@ func (s *Store) Open() (retErr error) { s.snapsExistOnOpen = len(snaps) > 0 // Create the log store and stable store. - s.boltStore, err = rlog.New(filepath.Join(s.raftDir, raftDBPath)) + s.boltStore, err = rlog.New(filepath.Join(s.raftDir, raftDBPath), s.RaftNoFreeListSync) if err != nil { return fmt.Errorf("new log store: %s", err) }