{"record":{"id":"c1f16ed70cb3ce70","repo":"crowdsecurity/crowdsec","slug":"marking-stale-usage-metrics-as-sent-w","errorCode":null,"errorMessage":"marking stale usage metrics as sent: %w","messagePattern":"marking stale usage metrics as sent: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/metrics.go","lineNumber":108,"sourceCode":"\t\treturn nil, fmt.Errorf(\"getting unsent usage metrics: %w\", err)\n\t}\n\n\treturn metrics, nil\n}\n\n// MarkStaleUsageMetricsAsSent gives up on metrics too old to be worth pushing, so a CAPI outage\n// cannot leave a growing backlog behind. The rows live on until the age flush, for cscli.\nfunc (c *Client) MarkStaleUsageMetricsAsSent(ctx context.Context, before time.Time) (int, error) {\n\tupdated, err := c.Ent.Metric.Update().\n\t\tWhere(\n\t\t\tmetric.PushedAtIsNil(),\n\t\t\tmetric.ReceivedAtLT(before),\n\t\t).\n\t\tSetPushedAt(time.Now().UTC()).\n\t\tSave(ctx)\n\tif err != nil {\n\t\tc.Log.Warningf(\"MarkStaleUsageMetricsAsSent: %s\", err)\n\t\treturn 0, fmt.Errorf(\"marking stale usage metrics as sent: %w\", err)\n\t}\n\n\treturn updated, nil\n}\n","sourceCodeStart":90,"sourceCodeEnd":113,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/metrics.go#L90-L113","documentation":"MarkStaleUsageMetricsAsSent gives up on metric rows received before the cutoff by setting their pushed_at, returning the number of updated rows. This error wraps a failing UPDATE; if the DB is fine but no rows match, updated is simply 0 with no error.","triggerScenarios":"Calling MarkStaleUsageMetricsAsSent (pushUsageMetrics during a CAPI outage, or tests) when the bulk UPDATE fails: SQLite write-lock contention, disk full, corrupted DB, canceled context.","commonSituations":"Long CAPI outage followed by a large stale-cleanup UPDATE hitting a lock conflict with live agent writes; disk exhaustion after months of unpersisted usage metrics.","solutions":["Check the Warningf log 'MarkStaleUsageMetricsAsSent: <err>' for the actual cause.","Clear writer contention or enable WAL mode for the SQLite DB.","Free disk space and verify DB integrity.","Retry the cleanup — it's idempotent and will sweep remaining stale rows on the next run."],"exampleFix":"// before\nn, err := c.MarkStaleUsageMetricsAsSent(ctx, before)\nif err != nil { return err }\n// after: log-and-continue is safe\nif err != nil {\n    log.Warnf(\"stale metrics cleanup skipped this cycle: %v\", err)\n    return nil\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := client.MarkStaleUsageMetricsAsSent(ctx, cutoff)\nif err != nil {\n    log.Warnf(\"stale metric cleanup deferred to next cycle: %v\", err)\n    return nil // safe: operation is idempotent\n}","preventionTips":["Treat cleanup as best-effort; it sweeps again next run.","Clear long-lived CAPI outages promptly to avoid huge stale backlogs.","Ensure disk headroom before large UPDATEs.","Check the underlying driver error in the warning log."],"tags":["database","sqlite","update-failure","metrics"],"backgroundTag":"database-write-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}