1
0
Fork 0

Allow WAL files for /boot

master
Philip O'Toole 9 months ago
parent 18e04dd079
commit a5f2bd8d74

@ -762,12 +762,6 @@ func (s *Service) handleBoot(w http.ResponseWriter, r *http.Request, qp QueryPar
http.Error(w, "invalid SQLite data", http.StatusBadRequest)
return
}
if !db.IsDELETEModeEnabled(peek) {
http.Error(w,
"SQLite database is in WAL mode - enable DELETE mode via 'PRAGMA journal_mode=DELETE'",
http.StatusBadRequest)
return
}
s.logger.Printf("starting boot process")
_, err = s.store.ReadFrom(bufReader)

@ -1340,9 +1340,6 @@ func (s *Store) ReadFrom(r io.Reader) (int64, error) {
if !sql.IsValidSQLiteFile(f.Name()) {
return n, fmt.Errorf("invalid SQLite data")
}
if !sql.IsDELETEModeEnabledSQLiteFile(f.Name()) {
return n, fmt.Errorf("SQLite file does not have DELETE mode enabled")
}
// Raft won't snapshot unless there is at least one unsnappshotted log entry,
// so prep that now before we do anything destructive.

@ -197,6 +197,26 @@ func (n *Node) Load(filename string) (string, error) {
return n.postFile("/db/load", filename)
}
// Backup backs up the node's database to the given file.
func (n *Node) Backup(filename string) error {
v, _ := url.Parse("http://" + n.APIAddr + "/db/backup")
resp, err := http.Get(v.String())
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("backup returned: %s", resp.Status)
}
defer resp.Body.Close()
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
// Load loads a SQLite database file into the node.
func (n *Node) Boot(filename string) (string, error) {
return n.postFile("/boot", filename)
@ -485,13 +505,13 @@ func (n *Node) postFile(url, filename string) (string, error) {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("file endpoint returned: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("file endpoint returned: %s %s", resp.Status, body)
}
return string(body), nil
}

@ -965,6 +965,44 @@ func Test_SingleNodeUpgrades_Snapshots(t *testing.T) {
}
}
func Test_SingleNodeBackup(t *testing.T) {
node := mustNewLeaderNode("leader1")
defer node.Deprovision()
// create a table and insert a row
_, err := node.Execute(`CREATE TABLE foo (id integer not null primary key, name text)`)
if err != nil {
t.Fatalf(`CREATE TABLE failed: %s`, err.Error())
}
_, err = node.Execute(`INSERT INTO foo(name) VALUES("fiona")`)
if err != nil {
t.Fatalf(`INSERT failed: %s`, err.Error())
}
backup := mustTempFile()
defer os.Remove(backup)
if err := node.Backup(backup); err != nil {
t.Fatalf(`backup failed: %s`, err.Error())
}
newNode := mustNewLeaderNode("leader2")
defer newNode.Deprovision()
_, err = newNode.Boot(backup)
if err != nil {
t.Fatalf(`boot failed: %s`, err.Error())
}
// check that the row is present in the new node
r, err := newNode.Query(`SELECT * FROM foo`)
if err != nil {
t.Fatalf(`query failed: %s`, err.Error())
}
if r != `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"]]}]}` {
t.Fatalf("test received wrong result got %s", r)
}
}
func Test_SingleNodeNodes(t *testing.T) {
node := mustNewLeaderNode("leader1")
defer node.Deprovision()

Loading…
Cancel
Save