1
0
Fork 0

List the newest snapshot, not oldest

If rqlite was killed after snapshot-creation, but before reaping took
place, Snapshot List() would return the oldest snapshot, not the newest.
This is not compliant with Hashicorp Raft requirements.
master
Philip O'Toole 8 months ago
parent 477453eaef
commit eda281ea48

@ -75,6 +75,8 @@ type Store struct {
fullNeededPath string
sinkMu sync.Mutex
logger *log.Logger
reapDisabled bool // For testing purposes
}
// NewStore returns a new Snapshot Store.
@ -124,21 +126,22 @@ func (s *Store) Create(version raft.SnapshotVersion, index, term uint64, configu
return &LockingSink{sink, s}, nil
}
// List returns a list of all the snapshots in the Store. It returns the snapshots
// in newest to oldest order.
// List returns a list of all the snapshots in the Store. In pratice, this will at most be
// a list of 1, and that will be the newest snapshot available.
func (s *Store) List() ([]*raft.SnapshotMeta, error) {
snapshots, err := s.getSnapshots()
if err != nil {
return nil, err
}
var snapMeta []*raft.SnapshotMeta
if len(snapshots) > 0 {
snapshotDir := filepath.Join(s.dir, snapshots[0].ID)
snapshotDir := filepath.Join(s.dir, snapshots[len(snapshots)-1].ID)
meta, err := readMeta(snapshotDir)
if err != nil {
return nil, err
}
snapMeta = append(snapMeta, meta)
snapMeta = append(snapMeta, meta) // Insert it.
}
return snapMeta, nil
}
@ -204,6 +207,9 @@ func (s *Store) Stats() (map[string]interface{}, error) {
// Reap reaps all snapshots, except the most recent one. Returns the number of
// snapshots reaped.
func (s *Store) Reap() (retN int, retErr error) {
if s.reapDisabled {
return 0, nil
}
defer func() {
stats.Add(snapshotsReaped, int64(retN))
}()

@ -1,12 +1,44 @@
package snapshot
import (
"io"
"os"
"sort"
"testing"
"github.com/hashicorp/raft"
)
func Test_SnapshotMetaSort(t *testing.T) {
metas := []*raft.SnapshotMeta{
{
ID: "2-1017-1704807719996",
Index: 1017,
Term: 2,
},
{
ID: "2-1131-1704807720976",
Index: 1131,
Term: 2,
},
}
sort.Sort(snapMetaSlice(metas))
if metas[0].ID != "2-1017-1704807719996" {
t.Errorf("Expected first snapshot ID to be 2-1017-1704807719996, got %s", metas[0].ID)
}
if metas[1].ID != "2-1131-1704807720976" {
t.Errorf("Expected second snapshot ID to be 2-1131-1704807720976, got %s", metas[1].ID)
}
sort.Sort(sort.Reverse(snapMetaSlice(metas)))
if metas[0].ID != "2-1131-1704807720976" {
t.Errorf("Expected first snapshot ID to be 2-1131-1704807720976, got %s", metas[0].ID)
}
if metas[1].ID != "2-1017-1704807719996" {
t.Errorf("Expected second snapshot ID to be 2-1017-1704807719996, got %s", metas[1].ID)
}
}
func Test_RemoveAllTmpSnapshotData(t *testing.T) {
dir := t.TempDir()
if err := RemoveAllTmpSnapshotData(dir); err != nil {
@ -154,7 +186,61 @@ func Test_StoreCreateCancel(t *testing.T) {
if pathExists(dir + "/" + sink.ID() + tmpSuffix) {
t.Errorf("Expected directory with name %s to not exist, but it does", sink.ID())
}
}
func Test_StoreList(t *testing.T) {
dir := t.TempDir()
store, err := NewStore(dir)
if err != nil {
t.Fatalf("Failed to create new store: %v", err)
}
store.reapDisabled = true
snaps, err := store.List()
if err != nil {
t.Fatalf("Failed to list snapshots: %v", err)
}
if len(snaps) != 0 {
t.Errorf("Expected 0 snapshots, got %d", len(snaps))
}
createSnapshot := func(id string, index, term, cfgIndex uint64, file string) {
sink := NewSink(store, makeRaftMeta(id, index, term, cfgIndex))
if sink == nil {
t.Fatalf("Failed to create new sink")
}
if err := sink.Open(); err != nil {
t.Fatalf("Failed to open sink: %v", err)
}
wal := mustOpenFile(t, file)
defer wal.Close()
_, err := io.Copy(sink, wal)
if err != nil {
t.Fatalf("Failed to copy WAL file: %v", err)
}
if err := sink.Close(); err != nil {
t.Fatalf("Failed to close sink: %v", err)
}
if fn, err := store.FullNeeded(); err != nil {
t.Fatalf("Failed to check if full snapshot needed: %v", err)
} else if fn {
t.Errorf("Expected full snapshot not to be needed, but it is")
}
}
createSnapshot("2-1017-1704807719996", 1017, 2, 1, "testdata/db-and-wals/backup.db")
createSnapshot("2-1131-1704807720976", 1131, 2, 1, "testdata/db-and-wals/wal-00")
snaps, err = store.List()
if err != nil {
t.Fatalf("Failed to list snapshots: %v", err)
}
if len(snaps) != 1 {
t.Errorf("Expected 1 snapshot, got %d", len(snaps))
}
if snaps[0].ID != "2-1131-1704807720976" {
t.Errorf("Expected snapshot ID to be 2-1131-1704807720976, got %s", snaps[0].ID)
}
}
func mustTouchFile(t *testing.T, path string) {

Loading…
Cancel
Save