{"record":{"id":"ff422e6861611d40","repo":"multica-ai/multica","slug":"update-codex-task-usage-batch-w","errorCode":null,"errorMessage":"update Codex task_usage batch: %w","messagePattern":"update Codex task_usage batch: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/cmd/backfill_codex_usage_cache/main.go","lineNumber":292,"sourceCode":"     ORDER BY COALESCE(tu.updated_at, tu.created_at), tu.id\n     LIMIT $3\n     FOR UPDATE OF tu SKIP LOCKED\n),\nupdated AS (\n    UPDATE task_usage tu\n       SET input_tokens = GREATEST(tu.input_tokens - tu.cache_read_tokens, 0),\n           updated_at = now()\n      FROM candidates c\n     WHERE tu.id = c.id\n     RETURNING c.removed_tokens\n)\nSELECT COUNT(*)::bigint, COALESCE(SUM(removed_tokens), 0)::bigint FROM updated`\n\n\tvar totalRows, totalRemoved int64\n\tfor {\n\t\tvar rows, removed int64\n\t\tif err := pool.QueryRow(ctx, query, cfg.cutoff, cfg.workspaceID, cfg.batchSize).Scan(&rows, &removed); err != nil {\n\t\t\treturn totalRows, totalRemoved, fmt.Errorf(\"update Codex task_usage batch: %w\", err)\n\t\t}\n\t\tif rows == 0 {\n\t\t\tbreak\n\t\t}\n\t\ttotalRows += rows\n\t\ttotalRemoved += removed\n\t\tslog.Info(\"updated Codex task_usage batch\", \"rows\", rows, \"input_tokens_removed\", removed, \"total_rows\", totalRows)\n\t\tif cfg.sleepBetweenBatches > 0 {\n\t\t\tselect {\n\t\t\tcase <-time.After(cfg.sleepBetweenBatches):\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn totalRows, totalRemoved, ctx.Err()\n\t\t\t}\n\t\t}\n\t}\n\treturn totalRows, totalRemoved, nil\n}\n","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/cmd/backfill_codex_usage_cache/main.go#L274-L310","documentation":"Returned when one UPDATE batch of the correction loop fails during the --execute phase. The query recomputes eligible rows per batch (same predicate as the dry-run), sets input_tokens = GREATEST(input_tokens - cache_read_tokens, 0), and returns counts. Because each batch commits independently and the predicate excludes already-corrected rows, earlier batches stay applied; re-running resumes rather than double-applying.","triggerScenarios":"A dead connection, statement timeout, or lock conflict on task_usage mid-loop; ctx cancelled (SIGINT) during the update; a concurrent writer altering rows the UPDATE targets.","commonSituations":"Long backfills on busy production tables hitting lock_timeout/statement_timeout; postgres failover mid-run; operators interrupting the --execute phase.","solutions":["Re-run the tool with the same --cutoff — completed batches no longer match the eligibility predicate, so it resumes from where it stopped.","Address the wrapped cause: raise statement/lock timeouts for the session, or run during lower write traffic.","Use --sleep-between-batches to reduce contention if lock conflicts recur."],"exampleFix":"# before\n./backfill_codex_usage_cache --cutoff ... --execute\n# update Codex task_usage batch: ... lock timeout\n\n# after: idempotent resume, gentler pacing\n./backfill_codex_usage_cache --cutoff 2026-06-01T12:00:00Z --execute --sleep-between-batches 500ms","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Re-run with the same --cutoff after any mid-batch failure — corrected rows self-exclude, so it resumes.","Use --sleep-between-batches on busy production tables to avoid lock conflicts.","Run during low write traffic or with relaxed lock_timeout for the session."],"tags":["database","backfill","update","timeout","resumable"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}