1
0
Fork 0

Merge remote-tracking branch 'origin' into backup-vacuum-fast-path

master
Philip O'Toole 9 months ago
commit f3ba52c749

@ -1,6 +1,7 @@
## 8.16.1 (unreleased)
### Implementation changes and bug fixes
- [PR #1574](https://github.com/rqlite/rqlite/pull/1574): Use "GZIP best speed" for internode traffic compression.
- [PR #1575](https://github.com/rqlite/rqlite/pull/1575): Upload Provider uses Snapshot-locking backup.
## 8.16.0 (January 6th 2024)
### New features

@ -217,6 +217,21 @@ func IsDELETEModeEnabled(b []byte) bool {
return len(b) >= 20 && b[18] == 1 && b[19] == 1
}
// EnsureDeleteMode ensures the database at the given path is in DELETE mode.
func EnsureDeleteMode(path string) error {
if IsDELETEModeEnabledSQLiteFile(path) {
return nil
}
rwDSN := fmt.Sprintf("file:%s", path)
conn, err := sql.Open("sqlite3", rwDSN)
if err != nil {
return fmt.Errorf("open: %s", err.Error())
}
defer conn.Close()
_, err = conn.Exec("PRAGMA journal_mode=DELETE")
return err
}
// RemoveFiles removes the SQLite database file, and any associated WAL and SHM files.
func RemoveFiles(path string) error {
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {

@ -500,6 +500,26 @@ func Test_IsWALModeEnabledOnDiskWAL(t *testing.T) {
t.Fatalf("WAL data marked as DELETE")
}
}
func Test_EnsureDelete(t *testing.T) {
path := mustTempFile()
defer os.Remove(path)
db, err := Open(path, false, true)
if err != nil {
t.Fatalf("failed to open database in WAL mode: %s", err.Error())
}
defer db.Close()
if !IsWALModeEnabledSQLiteFile(path) {
t.Fatalf("WAL file marked as non-WAL")
}
if err := EnsureDeleteMode(path); err != nil {
t.Fatalf("failed to ensure DELETE mode: %s", err.Error())
}
if !IsDELETEModeEnabledSQLiteFile(path) {
t.Fatalf("database not marked as DELETE mode")
}
}
// Test_WALDatabaseCreatedOK tests that a WAL file is created, and that
// a checkpoint succeeds

@ -1,10 +1,21 @@
package store
import (
"os"
"time"
"github.com/rqlite/rqlite/v8/command/proto"
"github.com/rqlite/rqlite/v8/db"
)
// Provider implements the uploader Provider interface, allowing the
// Store to be used as a DataProvider for an uploader.
type Provider struct {
str *Store
vacuum bool
nRetries int
retryInterval time.Duration
}
// NewProvider returns a new instance of Provider.
@ -12,12 +23,42 @@ func NewProvider(s *Store, v bool) *Provider {
return &Provider{
str: s,
vacuum: v,
nRetries: 10,
retryInterval: 500 * time.Millisecond,
}
}
// Provider writes the SQLite database to the given path.
// Provider writes the SQLite database to the given path, ensuring the database
// is in DELETE mode. If path exists, it will be overwritten.
func (p *Provider) Provide(path string) error {
if err := p.str.db.Backup(path, p.vacuum); err != nil {
fd, err := os.Create(path)
if err != nil {
return err
}
defer fd.Close()
br := &proto.BackupRequest{
Format: proto.BackupRequest_BACKUP_REQUEST_FORMAT_BINARY,
Vacuum: p.vacuum,
}
nRetries := 0
for {
err := p.str.Backup(br, fd)
if err == nil {
break
}
time.Sleep(p.retryInterval)
nRetries++
if nRetries > p.nRetries {
return err
}
}
// Switch database to DELETE mode, to keep existing behaviour.
if err := fd.Close(); err != nil {
return err
}
if db.EnsureDeleteMode(path) != nil {
return err
}
stats.Add(numProvides, 1)

@ -1178,6 +1178,10 @@ func (s *Store) Request(eqr *proto.ExecuteQueryRequest) ([]*proto.ExecuteQueryRe
//
// The backup may fail if the system is actively snapshotting. The client can just
// retry in this case.
// can be called while writes are being made to the system. If vacuum is true,
// then a VACUUM is performed on the database before the backup is made. The backup
// may fail if the system is actively snapshotting however. The client should
// just retry in this case.
func (s *Store) Backup(br *proto.BackupRequest, dst io.Writer) (retErr error) {
if !s.open {
return ErrNotOpen

Loading…
Cancel
Save