{"record":{"id":"8359a9f89dee01ff","repo":"Billionmail/BillionMail","slug":"task-d-update-database-threads-failed-w","errorCode":null,"errorMessage":"task %d: update database threads failed: %w","messagePattern":"task (.+?): update database threads failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/task_executor.go","lineNumber":1562,"sourceCode":"\t\t\t\t\treleaseMsg := fmt.Sprintf(\"task %d: old pool released\", taskId)\n\t\t\t\t\tg.Log().Info(context.Background(), releaseMsg)\n\t\t\t\t}(oldPool, oldPoolSize, runningWorkers)\n\t\t\t}\n\t\t} else {\n\t\t\tkeepMsg := fmt.Sprintf(\"task %d: pool size keep unchanged (%d), only adjust rate controller\", taskId, oldPoolSize)\n\t\t\tg.Log().Info(context.Background(), keepMsg)\n\t\t}\n\t}\n\n\t// update threads in database\n\t_, err = g.DB().Model(\"email_tasks\").\n\t\tWhere(\"id\", taskId).\n\t\tData(g.Map{\"threads\": newThreads}).\n\t\tUpdate()\n\n\tif err != nil {\n\n\t\treturn fmt.Errorf(\"task %d: update database threads failed: %w\", taskId, err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":1544,"sourceCodeEnd":1567,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/task_executor.go#L1544-L1567","documentation":"The final step of UpdateTaskThreads writes the new thread count to the database (g.DB update on the task row). If the Update() call errors, it is wrapped with %w as \"task %d: update database threads failed: <cause>\". The task lookup succeeded but the persistence step failed, so the in-memory pool state may have changed while the DB did not.","triggerScenarios":"The ORM Update() fails: DB connection dropped mid-operation, unique/lock contention on the task row, table schema mismatch, or context cancellation during the write.","commonSituations":"Postgres connection pool exhaustion under bulk-send load; deadlock on the tasks row with a concurrent update; migration drift (missing column or changed type) after a version upgrade.","solutions":["Inspect the wrapped cause via errors.Is/As to distinguish connectivity, lock-timeout, or schema errors.","Check DB health and re-run the update once connectivity is restored.","Retry with backoff for transient errors (deadlock/serialization); consider optimistic locking if contention is common.","Verify the tasks table schema matches the entity (run pending migrations)."],"exampleFix":"// before: fire and forget\n_ = e.UpdateTaskThreads(taskId, threads)\n// after\nif err := e.UpdateTaskThreads(taskId, threads); err != nil {\n    log.Printf(\"persist threads for task %d failed: %v\", taskId, err)\n    // reconcile pool size from DB or retry\n}","handlingStrategy":"try-catch","validationCode":"// Go: pre-check the row is updatable before pool resize\ncount, err := g.DB().Model(\"tasks\").Where(\"id\", taskId).Count()\nif err != nil || count == 0 { return fmt.Errorf(\"task %d not updatable\", taskId) }","typeGuard":"func isTransientDBErr(err error) bool {\n    msg := err.Error()\n    return strings.Contains(msg, \"deadlock\") || strings.Contains(msg, \"connection reset\") || strings.Contains(msg, \"i/o timeout\")\n}","tryCatchPattern":"if err := executor.UpdateTaskThreads(taskId, threads); err != nil {\n    var wrapped error\n    if strings.Contains(err.Error(), \"update database threads failed\") && errors.As(err, &wrapped) {\n        log.Printf(\"db write failed for task %d: %v\", taskId, wrapped)\n        if isTransientDBErr(wrapped) { /* retry with backoff */ }\n    }\n    return err\n}","preventionTips":["Verify the in-memory pool and DB threads value agree after any failed update (reconcile on error).","Keep schema migrations in sync with the entity definitions.","Size the DB connection pool for bulk-send workloads to avoid pool exhaustion.","Wrap ORM writes in a helper with uniform logging and retry for transient errors."],"tags":["database","write-failure","wrapped-error","batch-mail"],"backgroundTag":"database-write-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}