|
|
|
package snapshot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/hashicorp/raft"
|
|
|
|
"github.com/rqlite/rqlite/db"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Snapshot struct {
|
|
|
|
walData []byte
|
|
|
|
files []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewWALSnapshot(b []byte) *Snapshot {
|
|
|
|
return &Snapshot{
|
|
|
|
walData: b,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFullSnapshot(files ...string) *Snapshot {
|
|
|
|
return &Snapshot{
|
|
|
|
files: files,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) Persist(sink raft.SnapshotSink) error {
|
|
|
|
stream, err := s.OpenStream()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer stream.Close()
|
|
|
|
|
|
|
|
_, err = io.Copy(sink, stream)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) Release() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) OpenStream() (*Stream, error) {
|
|
|
|
if len(s.files) > 0 {
|
|
|
|
return NewFullStream(s.files...)
|
|
|
|
}
|
|
|
|
return NewIncrementalStream(s.walData)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReplayDB(fullSnap *FullSnapshot, r io.Reader, path string) error {
|
|
|
|
dbInfo := fullSnap.GetDb()
|
|
|
|
if dbInfo == nil {
|
|
|
|
return fmt.Errorf("got nil DB info")
|
|
|
|
}
|
|
|
|
|
|
|
|
sqliteBaseFD, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating SQLite file: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := io.CopyN(sqliteBaseFD, r, dbInfo.Size); err != nil {
|
|
|
|
return fmt.Errorf("error writing SQLite file data: %v", err)
|
|
|
|
}
|
|
|
|
if err := sqliteBaseFD.Close(); err != nil {
|
|
|
|
return fmt.Errorf("error closing SQLite file: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write out any WALs.
|
|
|
|
var walFiles []string
|
|
|
|
for i, wal := range fullSnap.GetWals() {
|
|
|
|
if wal == nil {
|
|
|
|
return fmt.Errorf("got nil WAL")
|
|
|
|
}
|
|
|
|
|
|
|
|
walName := filepath.Join(filepath.Dir(path), baseSqliteWALFile+fmt.Sprintf("%d", i))
|
|
|
|
walFD, err := os.Create(walName)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating WAL file: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := io.CopyN(walFD, r, wal.Size); err != nil {
|
|
|
|
return fmt.Errorf("error writing WAL file data: %v", err)
|
|
|
|
}
|
|
|
|
if err := walFD.Close(); err != nil {
|
|
|
|
return fmt.Errorf("error closing WAL file: %v", err)
|
|
|
|
}
|
|
|
|
walFiles = append(walFiles, walName)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checkpoint the WAL files into the base SQLite file
|
|
|
|
if err := db.ReplayWAL(path, walFiles, false); err != nil {
|
|
|
|
return fmt.Errorf("error checkpointing WAL: %v", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|