Start filling out the Store implementation

This commit is contained in:
Philip O'Toole
2026-01-11 19:02:18 -05:00
parent 89dcfd4e9d
commit 4a1246280b

View File

@@ -1,11 +1,13 @@
package snapshot2
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"time"
"github.com/hashicorp/raft"
@@ -46,6 +48,7 @@ func NewStore(dir string) (*Store, error) {
return str, nil
}
// Create creates a new snapshot sink for the given parameters.
func (s *Store) Create(version raft.SnapshotVersion, index, term uint64, configuration raft.Configuration,
configurationIndex uint64, trans raft.Transport) (raft.SnapshotSink, error) {
sink := NewSink(s.dir, &raft.SnapshotMeta{
@@ -63,10 +66,12 @@ func (s *Store) Create(version raft.SnapshotVersion, index, term uint64, configu
return sink, nil
}
// List returns the list of available snapshots.
func (s *Store) List() ([]*raft.SnapshotMeta, error) {
return nil, nil
return getSnapshots(s.dir)
}
// Open opens the snapshot with the given ID for reading.
func (s *Store) Open(id string) (*raft.SnapshotMeta, io.ReadCloser, error) {
return nil, nil, nil
}
@@ -78,6 +83,73 @@ func (s *Store) check() error {
return nil
}
// getSnapshots returns the list of snapshots in the given directory,
// sorted from newest to oldest.
func getSnapshots(dir string) ([]*raft.SnapshotMeta, error) {
// Get the eligible snapshots
snapshots, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
// Populate the metadata
var snapMeta []*raft.SnapshotMeta
for _, snap := range snapshots {
// Snapshots are stored in directories, ignore any files.
if !snap.IsDir() {
continue
}
// Ignore any temporary snapshots
dirName := snap.Name()
if isTmpName(dirName) {
continue
}
// Try to read the meta data
meta, err := readMeta(filepath.Join(dir, dirName))
if err != nil {
return nil, fmt.Errorf("failed to read meta for snapshot %s: %s", dirName, err)
}
snapMeta = append(snapMeta, meta)
}
sort.Sort(snapMetaSlice(snapMeta))
return snapMeta, nil
}
type cmpSnapshotMeta raft.SnapshotMeta
func (c *cmpSnapshotMeta) Less(other *cmpSnapshotMeta) bool {
if c.Term != other.Term {
return c.Term < other.Term
}
if c.Index != other.Index {
return c.Index < other.Index
}
return c.ID < other.ID
}
type snapMetaSlice []*raft.SnapshotMeta
// Len implements the sort interface for snapMetaSlice.
func (s snapMetaSlice) Len() int {
return len(s)
}
// Less implements the sort interface for snapMetaSlice.
func (s snapMetaSlice) Less(i, j int) bool {
si := (*cmpSnapshotMeta)(s[i])
sj := (*cmpSnapshotMeta)(s[j])
return si.Less(sj)
}
// Swap implements the sort interface for snapMetaSlice.
func (s snapMetaSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// snapshotName generates a name for the snapshot.
func snapshotName(term, index uint64) string {
now := time.Now()
@@ -89,3 +161,19 @@ func snapshotName(term, index uint64) string {
func metaPath(dir string) string {
return filepath.Join(dir, metaFileName)
}
// readMeta is used to read the meta data in a given snapshot directory.
func readMeta(dir string) (*raft.SnapshotMeta, error) {
fh, err := os.Open(metaPath(dir))
if err != nil {
return nil, err
}
defer fh.Close()
meta := &raft.SnapshotMeta{}
dec := json.NewDecoder(fh)
if err := dec.Decode(meta); err != nil {
return nil, err
}
return meta, nil
}