Skip to content

fix(coordinator): task dispatch fixes — priority order, proofs-ready guard, attempts refund#1813

Open
lispc wants to merge 3 commits into
developfrom
fix/coordinator-task-dispatch
Open

fix(coordinator): task dispatch fixes — priority order, proofs-ready guard, attempts refund#1813
lispc wants to merge 3 commits into
developfrom
fix/coordinator-task-dispatch

Conversation

@lispc

@lispc lispc commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Split out from #1803 (first of two PRs). Three independent coordinator fixes, one commit each:

1. get_task: deterministic dispatch priority Bundle > Batch > Chunk

Previously GetTasks randomly shuffled the prover's requested task types and tried only the first one, rejecting the request with empty-proof-data when that type had no task available even if other types did. Now requested types are tried in priority order (Bundle > Batch > Chunk), so the pipeline finalizes the earliest ready bundle first instead of proving unrelated chunks/batches further ahead, and a type with no available task no longer starves the others.

2. ORM: treat empty chunk/batch sets as not proofs-ready

CheckIfBatchChunkProofsAreReady / CheckIfBundleBatchProofsAreReady previously counted only unverified children, so a batch with zero chunks (or a bundle with zero batches) vacuously counted as proofs-ready and could dispatch an aggregate proving task with no children. Both now return false when the child set is empty. (Also fixes the misleading Chunk. prefix in the Batch method's error message.)

3. Refund burned task attempts on dispatch failure

When the coordinator failed to dispatch a freshly assigned task (formatProverTask / applyUniversal / InsertProverTask failure), only active_attempts was decremented while total_attempts stayed burned. After SessionAttempts (5) such failures the task became invisible to GetUnassigned* queries, yet had no prover_task row for the cron timeout check to mark it failed — the task starved silently.

  • Adds RefundAttemptsByHash to the chunk/batch/bundle ORMs: decrements both counters and resets proving_status to unassigned, guarded against underflow and against touching verified rows.
  • Used from the prover tasks for fresh assignments; re-polls of already assigned tasks keep the old active-attempts-only behavior.
  • Also adds the previously missing attempts recovery on the fixCompatibility failure path of batch/bundle tasks.
  • Includes ORM unit tests (charge→refund round-trip, verified-row no-op, no underflow).
  • test/api_test.go's TestProofGeneratedFailed is updated to assert the new semantics: failed proof submission keeps total_attempts (chunk: assigned, 1/0, recoverable via the prover_task row), while failed dispatch fully refunds (batch: unassigned, 0/0).

Test plan

  • go build ./..., go vet, gofmt/goimports, go mod tidy clean
  • make lint (golangci-lint v1.57.2) passes
  • go test ./internal/orm/ passes (incl. new refund tests, testcontainers)
  • go test -tags mock_verifier ./test/ passes (TestApis / TestProxyClient / TestProxyClientCompatibleMode)

Review notes

  • RefundAttemptsByHash is keyed by hash only (not task-context scoped): if two provers race the same task and one's dispatch fails, the refund also releases the other's charge, resetting status to unassigned. Impact is limited to extra duplicate dispatches (which the design already allows) on a rare failure path.
  • With the new priority order, an Assign error (not empty result) from a higher-priority type now short-circuits the request instead of falling through to lower-priority types.

Summary by CodeRabbit

  • Bug Fixes

    • Improved prover task assignment by trying available proof types in a consistent priority order.
    • Corrected attempt accounting when task dispatch fails, including refunds for newly assigned tasks.
    • Prevented proof-readiness checks from treating empty batches or chunks as complete.
    • Improved handling of missing task implementations and assignment failures.
  • Tests

    • Added coverage for attempt refunds and failure-state transitions across proof types.

lispc added 3 commits July 16, 2026 22:29
… dispatch

Previously GetTasks randomly shuffled the prover's requested task types
and tried only the first one, rejecting the request with empty-proof-data
when that type had no task available even if other types did.

Now the requested types are tried in deterministic priority order
(Bundle > Batch > Chunk), so the pipeline finalizes the earliest ready
bundle first instead of proving unrelated chunks/batches further ahead,
and a type with no available task no longer starves the others.
CheckIfBatchChunkProofsAreReady and CheckIfBundleBatchProofsAreReady
previously counted only unverified children, so a batch with zero chunks
(or a bundle with zero batches) vacuously counted as proofs-ready and
could dispatch an aggregate proving task with no children.

Both now return false when the child set is empty. Also fix the
misleading 'Chunk.' prefix in the Batch method's error message.
When the coordinator failed to dispatch a freshly assigned task
(formatProverTask / applyUniversal / InsertProverTask failure), only
active_attempts was decremented while total_attempts stayed burned.
After SessionAttempts (5) such failures the task became invisible to
GetUnassigned* queries, yet had no prover_task row for the cron timeout
check to mark it failed -- the task starved silently.

Add RefundAttemptsByHash to the chunk/batch/bundle ORMs (decrement both
counters, reset proving_status to unassigned, guarded against underflow
and against touching verified rows) and use it from the prover tasks for
fresh assignments; re-polls of already assigned tasks keep the old
active-attempts-only behavior. This also adds the previously missing
attempts recovery on the fixCompatibility failure path of batch/bundle
tasks.
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The coordinator now tries proof assignments in deterministic Bundle, Batch, Chunk priority order. Failed newly assigned tasks refund total and active attempts through new ORM methods, while re-polls reduce active attempts. Readiness checks reject empty sets, with ORM and API tests covering the updated states.

Changes

Task assignment and recovery

Layer / File(s) Summary
Prioritized task dispatch
coordinator/internal/controller/api/get_task.go
Proof types are deterministically ordered, access counting occurs before assignment, and missing or unsuccessful assignments fall through to the next candidate.
Assignment failure recovery
coordinator/internal/logic/provertask/*_prover_task.go
Batch, bundle, and chunk assignment failures use conditional refunds for fresh assignments and active-attempt decrements for re-polls.
ORM readiness and refunds
coordinator/internal/orm/{batch,bundle,chunk}.go
Readiness checks return false for empty sets, and refund methods decrement counters and restore unassigned status under guarded conditions.
Recovery validation
coordinator/internal/orm/orm_test.go, coordinator/test/api_test.go
Tests cover refund transitions, verified and zero-attempt no-ops, and the expected proof-generation failure state.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Suggested reviewers: noel2004, georgehao, colinlyguo

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes, but it does not follow the required template or include deployment tag and breaking-change sections. Rewrite the PR description using the template: Purpose/design rationale, PR title, Deployment tag versioning, and Breaking change label.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional and clearly summarizes the main coordinator fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/coordinator-task-dispatch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
coordinator/internal/logic/provertask/chunk_prover_task.go (1)

272-285: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use ctx.Copy() for consistent context propagation.

For consistency with batch_prover_task.go, bundle_prover_task.go, and the rest of this file, consider using ctx.Copy() when passing the Gin context to the ORM methods.

♻️ Proposed refactor
 func (cp *ChunkProverTask) recoverAttempts(ctx *gin.Context, taskCtx *proverTaskContext, chunkTask *orm.Chunk) {
 	if taskCtx.hasAssignedTask == nil {
-		if err := cp.chunkOrm.RefundAttemptsByHash(ctx, chunkTask.Hash); err != nil {
+		if err := cp.chunkOrm.RefundAttemptsByHash(ctx.Copy(), chunkTask.Hash); err != nil {
 			log.Error("failed to refund chunk attempts", "hash", chunkTask.Hash, "error", err)
 		}
 		return
 	}
-	if err := cp.chunkOrm.DecreaseActiveAttemptsByHash(ctx, chunkTask.Hash); err != nil {
+	if err := cp.chunkOrm.DecreaseActiveAttemptsByHash(ctx.Copy(), chunkTask.Hash); err != nil {
 		log.Error("failed to recover chunk active attempts", "hash", chunkTask.Hash, "error", err)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@coordinator/internal/logic/provertask/chunk_prover_task.go` around lines 272
- 285, Update recoverAttempts to pass a copied Gin context to both
RefundAttemptsByHash and DecreaseActiveAttemptsByHash, matching the context
propagation pattern used elsewhere. Use ctx.Copy() at each ORM call while
preserving the existing refund and decrement behavior.
coordinator/internal/controller/api/get_task.go (1)

139-151: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Return pre-sorted slice directly for default task types.

When TaskTypes is empty, you can return the default task types in priority order directly to skip the sorting overhead.

♻️ Proposed refactor
-	if len(proofTypes) == 0 {
-		proofTypes = []message.ProofType{
-			message.ProofTypeChunk,
-			message.ProofTypeBatch,
-			message.ProofTypeBundle,
-		}
-	}
-
-	// Bundle (3) > Batch (2) > Chunk (1)
-	sort.Slice(proofTypes, func(i, j int) bool {
-		return proofTypes[i] > proofTypes[j]
-	})
-	return proofTypes
+	if len(proofTypes) == 0 {
+		return []message.ProofType{
+			message.ProofTypeBundle,
+			message.ProofTypeBatch,
+			message.ProofTypeChunk,
+		}
+	}
+
+	// Bundle (3) > Batch (2) > Chunk (1)
+	sort.Slice(proofTypes, func(i, j int) bool {
+		return proofTypes[i] > proofTypes[j]
+	})
+	return proofTypes
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@coordinator/internal/controller/api/get_task.go` around lines 139 - 151,
Update the proofTypes defaulting logic in the surrounding task-type helper to
return the default ProofType slice directly in priority order when no task types
are provided, bypassing sort.Slice for that path. Preserve sorting for
explicitly supplied task types and keep the existing Bundle > Batch > Chunk
ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@coordinator/internal/orm/orm_test.go`:
- Around line 143-163: Extend the refund tests around the existing chunk case
and the corresponding batch and bundle cases to cover two active/total attempts
before refunding one, asserting the counters become 1/1 and the status remains
ProvingTaskAssigned. Reuse the existing ORM setup and refund APIs for each
resource, while preserving the current 1/1 → 0/0 unassignment coverage.

---

Nitpick comments:
In `@coordinator/internal/controller/api/get_task.go`:
- Around line 139-151: Update the proofTypes defaulting logic in the surrounding
task-type helper to return the default ProofType slice directly in priority
order when no task types are provided, bypassing sort.Slice for that path.
Preserve sorting for explicitly supplied task types and keep the existing Bundle
> Batch > Chunk ordering.

In `@coordinator/internal/logic/provertask/chunk_prover_task.go`:
- Around line 272-285: Update recoverAttempts to pass a copied Gin context to
both RefundAttemptsByHash and DecreaseActiveAttemptsByHash, matching the context
propagation pattern used elsewhere. Use ctx.Copy() at each ORM call while
preserving the existing refund and decrement behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6e6f610-d3d3-4a98-a3bb-a1cefdbf7dd6

📥 Commits

Reviewing files that changed from the base of the PR and between 788fdf0 and 99ae23b.

📒 Files selected for processing (9)
  • coordinator/internal/controller/api/get_task.go
  • coordinator/internal/logic/provertask/batch_prover_task.go
  • coordinator/internal/logic/provertask/bundle_prover_task.go
  • coordinator/internal/logic/provertask/chunk_prover_task.go
  • coordinator/internal/orm/batch.go
  • coordinator/internal/orm/bundle.go
  • coordinator/internal/orm/chunk.go
  • coordinator/internal/orm/orm_test.go
  • coordinator/test/api_test.go

Comment on lines +143 to +163
// (a) charge via UpdateChunkAttempts then refund -> both counters back to 0, status back to unassigned
insertChunk(1, "0xchunk-a", int16(types.ProvingTaskUnassigned), 0, 0)
rowsAffected, err := chunkOrm.UpdateChunkAttempts(context.Background(), 1, 0, 0)
assert.NoError(t, err)
assert.Equal(t, int64(1), rowsAffected)
active, total, err := chunkOrm.GetAttemptsByHash(context.Background(), "0xchunk-a")
assert.NoError(t, err)
assert.Equal(t, int16(1), active)
assert.Equal(t, int16(1), total)
status, err := chunkOrm.GetProvingStatusByHash(context.Background(), "0xchunk-a")
assert.NoError(t, err)
assert.Equal(t, types.ProvingTaskAssigned, status)

assert.NoError(t, chunkOrm.RefundAttemptsByHash(context.Background(), "0xchunk-a"))
active, total, err = chunkOrm.GetAttemptsByHash(context.Background(), "0xchunk-a")
assert.NoError(t, err)
assert.Equal(t, int16(0), active)
assert.Equal(t, int16(0), total)
status, err = chunkOrm.GetProvingStatusByHash(context.Background(), "0xchunk-a")
assert.NoError(t, err)
assert.Equal(t, types.ProvingTaskUnassigned, status)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Cover refunds with multiple active assignments.

Add a 2/2 → 1/1 case for chunk, batch, and bundle. The expected status must remain ProvingTaskAssigned.

Also applies to: 204-224, 270-288

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 143-143: Narrowing a non-constant integer to a smaller fixed-width type (int8/int16/int32, uint8/uint16/uint32) can silently overflow or wrap, yielding negative or truncated values that are dangerous in size, length, or index logic. Validate the source value is within the target type's range before converting (e.g. bounds-check, or use a checked helper), and avoid narrowing untrusted or len()/parsed values.
Context: int16(types.ProvingTaskUnassigned)
Note: [CWE-190] Integer Overflow or Wraparound.

(integer-overflow-narrowing-conversion-go)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@coordinator/internal/orm/orm_test.go` around lines 143 - 163, Extend the
refund tests around the existing chunk case and the corresponding batch and
bundle cases to cover two active/total attempts before refunding one, asserting
the counters become 1/1 and the status remains ProvingTaskAssigned. Reuse the
existing ORM setup and refund APIs for each resource, while preserving the
current 1/1 → 0/0 unassignment coverage.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 48.00000% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.95%. Comparing base (788fdf0) to head (99ae23b).

Files with missing lines Patch % Lines
coordinator/internal/orm/batch.go 44.73% 19 Missing and 2 partials ⚠️
coordinator/internal/orm/chunk.go 45.94% 18 Missing and 2 partials ⚠️
...or/internal/logic/provertask/bundle_prover_task.go 0.00% 11 Missing ⚠️
...tor/internal/logic/provertask/chunk_prover_task.go 0.00% 9 Missing ⚠️
...tor/internal/logic/provertask/batch_prover_task.go 36.36% 6 Missing and 1 partial ⚠️
coordinator/internal/orm/bundle.go 73.91% 4 Missing and 2 partials ⚠️
coordinator/internal/controller/api/get_task.go 80.95% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1813      +/-   ##
===========================================
+ Coverage    35.44%   35.95%   +0.50%     
===========================================
  Files          262      262              
  Lines        22596    22703     +107     
===========================================
+ Hits          8010     8162     +152     
+ Misses       13748    13689      -59     
- Partials       838      852      +14     
Flag Coverage Δ
coordinator 30.90% <48.00%> (+2.40%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants