Make background workers safe across control-plane replicas - #64
Make background workers safe across control-plane replicas#64gciavarrini wants to merge 5 commits into
Conversation
DB-backed claiming with optimistic updates on SQLite and SKIP LOCKED on Postgres. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
Second control-plane replica in compose. assert one cleanup delete publish per scheduler tick across replicas. Fail fast when the second replica is not reachable. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
PR Summary by QodoLease deferred deletions across control-plane replicas
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
Code Review by Qodo
1. Replicas can publish the same deletion
|
| now := time.Now() | ||
| pending, err := s.store.ServiceTypeInstance().ClaimPendingDeletions(ctx, now, now.Add(deletionClaimTTL), 0) |
There was a problem hiding this comment.
3. Some deletions stall for five minutes 🐞 Bug ☼ Reliability
ProcessPendingDeletions leases the whole batch before processing, but transient agent lookup failures and context cancellation return without releasing those claims. Those rows cannot be retried on the next one-minute cleanup cycle and instead remain unavailable until the fixed five-minute lease expires, including rows never reached before the ten-second cycle timeout.
Agent Prompt
## Issue description
Claims survive transient early exits and cancellation, delaying affected deletions until lease expiry even though the scheduler says they will retry on the next cycle.
## Fix Focus Areas
- internal/sp/cleanup/scheduler.go[93-105]
- internal/sp/cleanup/scheduler.go[132-140]
- internal/sp/store/resource_manager/service_instance.go[358-368]
## Recommended Fix
Add ownership-aware claim release and release any claimed row that exits before a retry or terminal transition. Avoid claiming more rows than the cycle can process, or explicitly release the unprocessed remainder when the cycle context ends.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if limit <= 0 { | ||
| limit = 100 | ||
| } |
There was a problem hiding this comment.
1. Old requests block newer deletions 🐞 Bug ≡ Correctness
ClaimPendingDeletions converts the scheduler's zero limit to 100 and always orders by the oldest request, while successful attempts clear their claim without changing the SCHEDULED status. If those first 100 requests receive no acknowledgments, every cycle selects them again; with unlimited retries newer rows are never attempted, and with the default retry limit they wait through up to ten cycles per older batch.
Agent Prompt
## Issue description
The bounded oldest-first query repeatedly reclaims the same unacknowledged rows, preventing later scheduled deletions from entering a batch.
## Fix Focus Areas
- internal/sp/store/resource_manager/service_instance.go[361-364]
- internal/sp/store/resource_manager/service_instance.go[375-380]
- internal/sp/store/resource_manager/service_instance.go[426-435]
## Recommended Fix
Track when each deletion is next eligible and exclude attempted rows until that retry time, while allowing later scheduled rows into subsequent batches. Preserve deterministic ordering among currently eligible rows and add coverage with more than 100 unacknowledged deletions across multiple cycles.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| "retry_count": gorm.Expr("retry_count + 1"), | ||
| "last_deletion_attempt": now, | ||
| "deletion_claimed_until": nil, | ||
| }) |
There was a problem hiding this comment.
2. Replicas can publish the same deletion 🐞 Bug ≡ Correctness
IncrementDeletionRetry clears deletion_claimed_until immediately after processOne publishes a delete request, including after a successful publish, while the deletion remains SCHEDULED. Because claim eligibility accepts a null lease, another replica's scheduler can reclaim the row before the asynchronous agent acknowledgement arrives, publish the same delete request, and increment the retry count again.
Agent Prompt
## Issue description
`IncrementDeletionRetry` releases a deletion's lease immediately after publishing while the row remains `SCHEDULED`. This allows another control-plane replica to reclaim and republish the same deletion before the asynchronous agent acknowledgement arrives, defeating the database claim and potentially incrementing retry accounting again.
## Fix Focus Areas
- internal/sp/store/resource_manager/service_instance.go[426-435]
- internal/sp/cleanup/scheduler.go[151-166]
## Recommended Fix
Do not clear `deletion_claimed_until` as part of recording a publish attempt or retry accounting. Preserve the claim until deletion is finalized or the worker intentionally abandons it, and make claim release or renewal atomic with the corresponding deletion state transition; rely on lease expiry for recovery after a crashed worker, or, if retries must occur sooner, add an explicit atomically maintained next-attempt or ownership mechanism that keeps the row excluded until the intended retry time.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Use compose profile ha so test-up stays single-replica. HA spec brings cp2 up on demand, polls health, and stops it after. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
Make deferred-deletion cleanup safe when multiple control-plane instances share the same Postgres database.
Each replica leases
SCHEDULEDrows before publishing to the agent, so only one instance drives a given deletion at a time.deletion_claimed_untilandClaimPendingDeletions(optimistic onSQLite,
FOR UPDATE SKIP LOCKEDon Postgres)control-plane-2in composeProvider health-check claiming is out of scope here:
mainuses the agent heartbeat monitor (MarkStaleUnavailable), which is already a single atomic update.Fixes
https://redhat.atlassian.net/browse/FLPATH-4630