diff --git a/backup.go b/backup.go new file mode 100644 index 0000000..e5d0658 --- /dev/null +++ b/backup.go @@ -0,0 +1,22 @@ +package main + +import ( + "qurl/storage" + "time" + "fmt" + "path" + "os" +) + +func manageBackup(stor storage.Storage, dir string, interval int){ + for { + time.Sleep(time.Duration(interval) * time.Second) + fname := fmt.Sprintf("qurl-%s.backup", + time.Now().Format("20060102150405")) + + err := stor.Backup(path.Join(dir, fname)) + if err != nil { + fmt.Fprintf(os.Stderr, "Backup failure: %s\n", err.Error()) + } + } +} diff --git a/main.go b/main.go index c678d93..1a016bb 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "os" "qurl/pages" "qurl/storage" - "runtime" ) //go:generate bindata -m Assets -r assets -p static -o static/assets.go assets @@ -17,18 +16,10 @@ func main() { dburl := flag.String("u", "bolt:./qurl.db", "url to database") lsaddr := flag.String("l", "127.0.0.1:8080", "listen address/port") jsonfile := flag.String("j", "", "path to json to load into database") - maxpro := flag.Int("m", runtime.NumCPU()+2, - "maximum number of threads to use") + backupint := flag.Int("i", 86400, "seconds between database backups") + backupdir := flag.String("b", "", "destination directory for database backups") flag.Parse() - if *maxpro < 3 { - fmt.Fprintf(os.Stderr, "Thread limit too low: %d (min 3)\n", *maxpro) - return - } - - // Limit max processes - runtime.GOMAXPROCS(*maxpro) - // Open storage backend stor, err := storage.NewStorage(*dburl) if err != nil { @@ -37,6 +28,22 @@ func main() { } defer stor.Shutdown() + // If there's a backup dir specified, do backups + // at a specific interval + if *backupdir != "" { + stat, err := os.Stat(*backupdir) + if err != nil { + fmt.Fprintf(os.Stderr, "Directory stat error: %s\n", err.Error()) + return + } + if !stat.IsDir() { + fmt.Fprintf(os.Stderr, "Backup directory does not exist: %s\n", *backupdir) + return + } + + go manageBackup(stor, *backupdir, *backupint) + } + // Load data if asked if *jsonfile != "" { err := loadjson(stor, *jsonfile) diff --git a/storage/bolt/bolt.go b/storage/bolt/bolt.go index 29c41cc..42914ce 100644 --- a/storage/bolt/bolt.go +++ b/storage/bolt/bolt.go @@ -4,6 +4,7 @@ import ( bolt "go.etcd.io/bbolt" "net/url" "time" + "os" ) type BoltStorage struct { @@ -26,3 +27,17 @@ func New(u *url.URL) (*BoltStorage, error) { } return &BoltStorage{DB: db}, nil } + +func (stor *BoltStorage) Backup(bpath string) error { + tx, err := stor.DB.Begin(false) + if err != nil { + return err + } + defer tx.Rollback() + + err = tx.CopyFile(bpath, os.FileMode(0600)) + if err != nil { + return err + } + return nil +} diff --git a/storage/storage.go b/storage/storage.go index 9a9fed5..5c9d81d 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -13,6 +13,7 @@ type Storage interface { // GetQURL(uint64) (*qurl.QURL, error) GetQURLByURL(string) (*qurl.QURL, error) SetQURLSequence(uint64) error + Backup(string) error Shutdown() }