{"record":{"id":"48c1f238fc96a933","repo":"Billionmail/BillionMail","slug":"failed-to-update-task-process-status-w","errorCode":null,"errorMessage":"failed to update task process status: %w","messagePattern":"failed to update task process status: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/batch_mail.go","lineNumber":571,"sourceCode":"\t\tUpdate()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update task pause status: %w\", err)\n\t}\n\n\tg.Log().Info(ctx, \"Updated task %d pause status: isPaused=%v, task_process=%d\", taskId, isPaused, processValue)\n\treturn nil\n}\n\n// UpdateTaskProcessStatus update task process status\nfunc UpdateTaskProcessStatus(ctx context.Context, taskId int, status int) error {\n\t_, err := g.DB().Model(\"email_tasks\").\n\t\tWhere(\"id\", taskId).\n\t\tData(g.Map{\"task_process\": status}).\n\t\tUpdate()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update task process status: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// GetTaskSendingStats get task sending stats (success count and failed count)\nfunc GetTaskSendingStats(ctx context.Context, taskID int) (int, int, error) {\n\tif taskID <= 0 {\n\t\treturn 0, 0, nil\n\t}\n\n\t// query success count\n\tsuccessQuery := g.DB().Model(\"mailstat_send_mails sm\")\n\tsuccessQuery = successQuery.LeftJoin(\"mailstat_message_ids mid\", \"sm.postfix_message_id=mid.postfix_message_id\")\n\tsuccessQuery = successQuery.LeftJoin(\"recipient_info ri\", \"mid.message_id=ri.message_id\")\n\tsuccessQuery = successQuery.Where(\"ri.task_id\", taskID)\n\tsuccessQuery = successQuery.Where(\"sm.status\", \"sent\")\n\tsuccessQuery = successQuery.Where(\"sm.dsn LIKE '2.%'\")","sourceCodeStart":553,"sourceCodeEnd":589,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/batch_mail.go#L553-L589","documentation":"UpdateTaskProcessStatus sets task_process on an email_tasks row during task processing. Any UPDATE error is wrapped as \"failed to update task process status: %w\". ProcessTask relies on this to track progress, so failures here leave task state stale even if sending continued.","triggerScenarios":"ProcessTask updating the process status while the DB UPDATE fails: connection drop mid-send, statement timeout, UPDATE permission denied, or trigger/constraint rejection on email_tasks.","commonSituations":"Long campaigns where the DB connection goes stale; failover during a large batch send; DB user restricted to read-only; table locks from analytics queries scanning email_tasks.","solutions":["Check the wrapped underlying error and DB connectivity","Verify UPDATE permissions on email_tasks and primary-instance routing","Increase statement/idle timeouts for long-running send workers","Retry the status update (it is idempotent for a given status value)"],"exampleFix":"// before: single attempt, fire and forget failure path\n_, err := g.DB().Model(\"email_tasks\").Where(\"id\", taskId).Data(g.Map{\"task_process\": status}).Update()\nif err != nil {\n    return fmt.Errorf(\"failed to update task process status: %w\", err)\n}\n// after: retry idempotent status update\nvar err error\nfor i := 0; i < 3; i++ {\n    if _, err = g.DB().Model(\"email_tasks\").Where(\"id\", taskId).Data(g.Map{\"task_process\": status}).Update(); err == nil {\n        return nil\n    }\n    time.Sleep(time.Duration(i+1) * 500 * time.Millisecond)\n}\nreturn fmt.Errorf(\"failed to update task process status: %w\", err)","handlingStrategy":"retry","validationCode":"if taskId <= 0 {\n    return errors.New(\"invalid taskId\")\n}\nif status < 0 {\n    return errors.New(\"invalid process status\")\n}","typeGuard":null,"tryCatchPattern":"if err := UpdateTaskProcessStatus(ctx, taskId, status); err != nil {\n    if isTransientDBError(err) {\n        // retry once or twice; the update is idempotent\n    }\n    log.Printf(\"task %d status update failed: %v\", taskId, err)\n    return err\n}","preventionTips":["Treat status updates as idempotent and retry transient failures","Keep DB sessions alive for long send workers (heartbeat/pool pings)","Check for table locks from analytics queries on email_tasks","Alert on repeated status-update failures during campaigns"],"tags":["database","update","postgresql"],"backgroundTag":"database-update-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}