perf(worker): 缩短媒体完成链路

生成媒体在启用直传 OSS 时绕过后端存储通道,异步任务持久化成功后不再在 Worker 内二次水化 Base64。\n\n将任务事件与回调 outbox 合并到同一事务,降低跨地域同步复制提交次数并消除事件已落库但回调未登记的崩溃窗口。文件通道健康遥测改为异步复制提交,避免非关键状态占用执行槽。\n\n验证:Go 全量测试、go vet、gofmt、git diff --check、相对 447e7ed701 的迁移安全检查均通过。
This commit is contained in:
2026-07-31 10:15:53 +08:00
parent 447e7ed701
commit cae97b6f77
6 changed files with 177 additions and 24 deletions
@@ -237,28 +237,48 @@ func (s *Store) MarkFileStorageChannelFailure(ctx context.Context, id string, me
if strings.TrimSpace(id) == "" {
return nil
}
_, err := s.pool.Exec(ctx, `
tx, err := s.pool.Begin(ctx)
if err != nil {
return err
}
defer rollbackTransaction(tx)
if _, err := tx.Exec(ctx, `SET LOCAL synchronous_commit = off`); err != nil {
return err
}
if _, err := tx.Exec(ctx, `
UPDATE file_storage_channels
SET last_error = NULLIF($2, ''),
last_failed_at = now(),
updated_at = now()
WHERE id = $1::uuid
AND deleted_at IS NULL`, id, strings.TrimSpace(message))
return err
AND deleted_at IS NULL`, id, strings.TrimSpace(message)); err != nil {
return err
}
return tx.Commit(ctx)
}
func (s *Store) MarkFileStorageChannelSuccess(ctx context.Context, id string) error {
if strings.TrimSpace(id) == "" {
return nil
}
_, err := s.pool.Exec(ctx, `
tx, err := s.pool.Begin(ctx)
if err != nil {
return err
}
defer rollbackTransaction(tx)
if _, err := tx.Exec(ctx, `SET LOCAL synchronous_commit = off`); err != nil {
return err
}
if _, err := tx.Exec(ctx, `
UPDATE file_storage_channels
SET last_error = NULL,
last_succeeded_at = now(),
updated_at = now()
WHERE id = $1::uuid
AND deleted_at IS NULL`, id)
return err
AND deleted_at IS NULL`, id); err != nil {
return err
}
return tx.Commit(ctx)
}
func scanFileStorageChannel(scanner fileStorageChannelScanner) (FileStorageChannel, error) {
+12 -4
View File
@@ -63,13 +63,21 @@ func TestTaskEventsAreMinimalAndConsecutiveDuplicatesAreSkipped(t *testing.T) {
if err != nil || synthetic.ID != "" || synthetic.SkippedReason != "unknown_type" {
t.Fatalf("synthetic=%+v err=%v", synthetic, err)
}
completed, err := db.AddTaskEvent(ctx, task.ID, "task.completed", "succeeded", "completed", 1, "ignored", map[string]any{"result": map[string]any{"duplicate": true}}, true)
completed, err := db.AddTaskEventWithCallback(
ctx,
task.ID,
"task.completed",
"succeeded",
"completed",
1,
"ignored",
map[string]any{"result": map[string]any{"duplicate": true}},
true,
"https://callback.invalid/task",
)
if err != nil || completed.ID == "" || len(completed.Payload) != 0 {
t.Fatalf("completed=%+v err=%v", completed, err)
}
if err := db.QueueTaskCallback(ctx, completed, "https://callback.invalid/task"); err != nil {
t.Fatal(err)
}
var events int
var nonEmptyPayloads int
+48
View File
@@ -2049,6 +2049,40 @@ func taskEventPlatformID(payload map[string]any) string {
}
func (s *Store) AddTaskEvent(ctx context.Context, taskID string, eventType string, status string, phase string, progress float64, message string, payload map[string]any, simulated bool) (TaskEvent, error) {
return s.addTaskEvent(ctx, taskID, eventType, status, phase, progress, message, payload, simulated, "")
}
// AddTaskEventWithCallback persists the event and its callback outbox item in
// one transaction. This closes the crash window between event creation and
// callback registration and avoids a second synchronous-replication commit for
// every emitted task event.
func (s *Store) AddTaskEventWithCallback(
ctx context.Context,
taskID string,
eventType string,
status string,
phase string,
progress float64,
message string,
payload map[string]any,
simulated bool,
callbackURL string,
) (TaskEvent, error) {
return s.addTaskEvent(ctx, taskID, eventType, status, phase, progress, message, payload, simulated, callbackURL)
}
func (s *Store) addTaskEvent(
ctx context.Context,
taskID string,
eventType string,
status string,
phase string,
progress float64,
message string,
payload map[string]any,
simulated bool,
callbackURL string,
) (TaskEvent, error) {
eventType = strings.TrimSpace(eventType)
if !taskEventAllowed(eventType) {
return TaskEvent{SkippedReason: "unknown_type"}, nil
@@ -2142,6 +2176,20 @@ RETURNING id::text, task_id::text, seq, event_type, COALESCE(status, ''), COALES
return TaskEvent{}, err
}
event.Payload = decodeObject(payloadBytes)
callbackURL = strings.TrimSpace(callbackURL)
if callbackURL != "" {
if _, err := tx.Exec(ctx, `
INSERT INTO gateway_task_callback_outbox (task_id, event_id, seq, callback_url, payload)
VALUES ($1::uuid, $2::uuid, $3, $4, '{}'::jsonb)
ON CONFLICT (task_id, seq, callback_url) DO NOTHING`,
event.TaskID,
event.ID,
event.Seq,
callbackURL,
); err != nil {
return TaskEvent{}, err
}
}
if err := tx.Commit(ctx); err != nil {
return TaskEvent{}, err
}