Skip to content

Commit c1fab7f

Browse files
QTomclaude
andcommitted
feat(backup): 备份/恢复异步化,解决 504 超时
POST /backups 和 POST /backups/:id/restore 改为异步:立即返回 HTTP 202, 后台 goroutine 独立执行 pg_dump → gzip → S3 上传,前端每 2s 轮询状态。 后端: - 新增 StartBackup/StartRestore 方法,后台 goroutine 不依赖 HTTP 连接 - Graceful shutdown 等待活跃操作完成,启动时清理孤立 running 记录 - BackupRecord 新增 progress/restore_status 字段支持进度和恢复状态追踪 前端: - 创建备份/恢复后轮询 GET /backups/:id 直到完成或失败 - 标签页切换暂停/恢复轮询,组件卸载清理定时器 - 正确处理 409(备份进行中)和轮询超时 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f42c8f2 commit c1fab7f

9 files changed

Lines changed: 780 additions & 68 deletions

File tree

backend/internal/handler/admin/backup_handler.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ func (h *BackupHandler) CreateBackup(c *gin.Context) {
9898
expireDays = *req.ExpireDays
9999
}
100100

101-
record, err := h.backupService.CreateBackup(c.Request.Context(), "manual", expireDays)
101+
record, err := h.backupService.StartBackup(c.Request.Context(), "manual", expireDays)
102102
if err != nil {
103103
response.ErrorFrom(c, err)
104104
return
105105
}
106-
response.Success(c, record)
106+
response.Accepted(c, record)
107107
}
108108

109109
func (h *BackupHandler) ListBackups(c *gin.Context) {
@@ -196,9 +196,10 @@ func (h *BackupHandler) RestoreBackup(c *gin.Context) {
196196
return
197197
}
198198

199-
if err := h.backupService.RestoreBackup(c.Request.Context(), backupID); err != nil {
199+
record, err := h.backupService.StartRestore(c.Request.Context(), backupID)
200+
if err != nil {
200201
response.ErrorFrom(c, err)
201202
return
202203
}
203-
response.Success(c, gin.H{"restored": true})
204+
response.Accepted(c, record)
204205
}

backend/internal/pkg/response/response.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ func Created(c *gin.Context, data any) {
4747
})
4848
}
4949

50+
// Accepted 返回异步接受响应 (HTTP 202)
51+
func Accepted(c *gin.Context, data any) {
52+
c.JSON(http.StatusAccepted, Response{
53+
Code: 0,
54+
Message: "accepted",
55+
Data: data,
56+
})
57+
}
58+
5059
// Error 返回错误响应
5160
func Error(c *gin.Context, statusCode int, message string) {
5261
c.JSON(statusCode, Response{

backend/internal/repository/backup_s3_store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func NewS3BackupStoreFactory() service.BackupObjectStoreFactory {
5757

5858
func (s *S3BackupStore) Upload(ctx context.Context, key string, body io.Reader, contentType string) (int64, error) {
5959
// 读取全部内容以获取大小(S3 PutObject 需要知道内容长度)
60+
// 注意:阿里云 OSS 不兼容 s3manager 分片上传的签名方式,因此使用 PutObject
6061
data, err := io.ReadAll(body)
6162
if err != nil {
6263
return 0, fmt.Errorf("read body: %w", err)

0 commit comments

Comments
 (0)