CreateBackup creates a new backup of the current app pb_data directory. If name is empty, it will be autogenerated. If backup with the same name exists, the new backup file will replace it. The backup is executed within a transaction, meaning that new writes will be temporary "blocked" until the b
(ctx context.Context, name string)
| 42 | // |
| 43 | // Backups can be stored on S3 if it is configured in app.Settings().Backups. |
| 44 | func (app *BaseApp) CreateBackup(ctx context.Context, name string) error { |
| 45 | if app.Store().Has(StoreKeyActiveBackup) { |
| 46 | return errors.New("try again later - another backup/restore operation has already been started") |
| 47 | } |
| 48 | |
| 49 | app.Store().Set(StoreKeyActiveBackup, name) |
| 50 | defer app.Store().Remove(StoreKeyActiveBackup) |
| 51 | |
| 52 | event := new(BackupEvent) |
| 53 | event.App = app |
| 54 | event.Context = ctx |
| 55 | event.Name = name |
| 56 | // default root dir entries to exclude from the backup generation |
| 57 | event.Exclude = []string{ |
| 58 | LocalBackupsDirName, |
| 59 | LocalTempDirName, |
| 60 | LocalNotifyDirName, |
| 61 | LocalAutocertCacheDirName, |
| 62 | lostFoundDirName, |
| 63 | } |
| 64 | |
| 65 | return app.OnBackupCreate().Trigger(event, func(e *BackupEvent) error { |
| 66 | // generate a default name if missing |
| 67 | if e.Name == "" { |
| 68 | e.Name = generateBackupName(e.App, "pb_backup_") |
| 69 | } |
| 70 | |
| 71 | // make sure that the special temp directory exists |
| 72 | // note: it needs to be inside the current pb_data to avoid "cross-device link" errors |
| 73 | localTempDir := filepath.Join(e.App.DataDir(), LocalTempDirName) |
| 74 | if err := os.MkdirAll(localTempDir, os.ModePerm); err != nil { |
| 75 | return fmt.Errorf("failed to create a temp dir: %w", err) |
| 76 | } |
| 77 | |
| 78 | // archive pb_data in a temp directory, excluding the "backups" and the temp dirs |
| 79 | // |
| 80 | // run in transaction to temporary block other writes (transactions uses the NonconcurrentDB connection) |
| 81 | // --- |
| 82 | tempPath := filepath.Join(localTempDir, "pb_backup_"+security.PseudorandomString(6)) |
| 83 | createErr := e.App.RunInTransaction(func(txApp App) error { |
| 84 | return txApp.AuxRunInTransaction(func(txApp App) error { |
| 85 | // run manual checkpoint and truncate the WAL files |
| 86 | // (errors are ignored because it is not that important and the PRAGMA may not be supported by the used driver) |
| 87 | txApp.DB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute() |
| 88 | txApp.AuxDB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute() |
| 89 | |
| 90 | return archive.Create(txApp.DataDir(), tempPath, e.Exclude...) |
| 91 | }) |
| 92 | }) |
| 93 | if createErr != nil { |
| 94 | return createErr |
| 95 | } |
| 96 | defer os.Remove(tempPath) |
| 97 | |
| 98 | // persist the backup in the backups filesystem |
| 99 | // --- |
| 100 | fsys, err := e.App.NewBackupsFilesystem() |
| 101 | if err != nil { |
no test coverage detected