|
|
|
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.
|
|
|
|
func NewProvider(s *Store, v bool) *Provider {
|
|
|
|
return &Provider{
|
|
|
|
str: s,
|
|
|
|
vacuum: v,
|
|
|
|
nRetries: 10,
|
|
|
|
retryInterval: 500 * time.Millisecond,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check returns true if the Provider data has changed since the last time
|
|
|
|
// Check() was called with the given value of i. Check() also returns the
|
|
|
|
// current value of i, which should be passed to the next invocation of
|
|
|
|
// Check(). If Check() returns false, the returned int64 can be ignored.
|
|
|
|
func (p *Provider) Check(i int64) (int64, bool) {
|
|
|
|
stats.Add(numProviderChecks, 1)
|
|
|
|
lm, err := p.str.db.LastModified()
|
|
|
|
if err != nil {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
return lm.UnixNano(), lm.UnixNano() > i
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) (retErr error) {
|
|
|
|
stats.Add(numProviderProvides, 1)
|
|
|
|
defer func() {
|
|
|
|
if retErr != nil {
|
|
|
|
stats.Add(numProviderProvidesFail, 1)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
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)
|
|
|
|
return nil
|
|
|
|
}
|