mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-22 02:47:14 +00:00
fix(database): create SQLite backup snapshots online (#6137)
* fix(database): snapshot SQLite backups online Use SQLite's online backup API for downloadable backups and SQLite migration exports instead of checkpointing then reading the live database file. The regression test validates a backup made while writes continue. * style(database): group SQLite driver imports * fix(database): bound online backup retries Use a single backup step and a bounded connection-acquisition/retry context. Tighten temporary-file cleanup and regression assertions while removing the unused checkpoint helper. * test(database): cover existing backup destinations * fix(database): harden SQLite snapshot lifecycle Sweep interrupted snapshot directories at SQLite startup, keep rollback-journal backups incremental, and make caller-owned cleanup explicit. Reuse one scheduled Telegram snapshot across administrators and make the direct SQLite driver dependency explicit. --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
This commit is contained in:
@@ -1303,25 +1303,26 @@ func (s *ServerService) GetDb() ([]byte, error) {
|
||||
if database.IsPostgres() {
|
||||
return s.exportPostgresDB()
|
||||
}
|
||||
// Update by manually trigger a checkpoint operation
|
||||
err := database.Checkpoint()
|
||||
backupPath, cleanup, err := s.backupSQLite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Open the file for reading
|
||||
file, err := os.Open(config.GetDBPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
defer cleanup()
|
||||
return os.ReadFile(backupPath)
|
||||
}
|
||||
|
||||
// Read the file contents
|
||||
fileContents, err := io.ReadAll(file)
|
||||
func (s *ServerService) backupSQLite() (string, func(), error) {
|
||||
backupDir, err := os.MkdirTemp(filepath.Dir(config.GetDBPath()), ".x-ui-backup-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return fileContents, nil
|
||||
cleanup := func() { _ = os.RemoveAll(backupDir) }
|
||||
backupPath := filepath.Join(backupDir, "backup.db")
|
||||
if err := database.BackupSQLite(backupPath); err != nil {
|
||||
cleanup()
|
||||
return "", nil, err
|
||||
}
|
||||
return backupPath, cleanup, nil
|
||||
}
|
||||
|
||||
// BackupFilename returns the filename for a database backup, named after the
|
||||
@@ -1421,11 +1422,12 @@ func (s *ServerService) GetMigration() ([]byte, string, error) {
|
||||
return data, "x-ui.db", nil
|
||||
}
|
||||
|
||||
// SQLite panel: checkpoint so the .db reflects the latest writes, then dump.
|
||||
if err := database.Checkpoint(); err != nil {
|
||||
backupPath, cleanup, err := s.backupSQLite()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
data, err := database.DumpSQLiteToBytes(config.GetDBPath())
|
||||
defer cleanup()
|
||||
data, err := database.DumpSQLiteToBytes(backupPath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user