Skip to content

dxf, server: batch history transfer and bound task cleanup#69811

Open
D3Hunter wants to merge 6 commits into
pingcap:masterfrom
D3Hunter:codex/cherry-pick-b2a173a3
Open

dxf, server: batch history transfer and bound task cleanup#69811
D3Hunter wants to merge 6 commits into
pingcap:masterfrom
D3Hunter:codex/cherry-pick-b2a173a3

Conversation

@D3Hunter

@D3Hunter D3Hunter commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: ref #69788, #67640

Problem Summary:

DXF cleanup previously selected every finished task in one query and transferred task and subtask history using SQL statements whose count grew with the number of tasks. With many small import jobs, the per-task SQL round trips can make cleanup slower than task completion, while selecting an unbounded cleanup set can produce an excessively large transfer transaction.

What changed and how does it work?

  • Add a process-local DXF cleanup batch-size setting with a default of 20 and a valid range of [1, 1000].
  • Limit each cleanup query to at most the configured number of tasks in the failed, reverted, or succeed state. Each cleanup invocation processes one batch and leaves any remaining backlog for later invocations.
  • Batch the metadata redaction update and the task and subtask history inserts and deletes into five SQL statements in one transaction, reducing SQL round trips while preserving atomic transfer behavior.
  • Add a SYSTEM-keyspace-only GET and POST API at /dxf/schedule/task_cleanup_batch_size. The setting is memory-only, resets on restart, and should be read or changed on the current DXF owner.
  • Add proto, storage, scheduler, HTTP, keyspace-isolation, and rollback coverage, regenerate the affected mocks and Bazel metadata, and document the API.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)

each task has 3 subtask, task/subtask meta size is 500B/100B. we can see the transfer method became faster now

Tasks Meta moved Base median Batch median Speedup Time saved Reduction
20 16 KB 18.051 ms 6.696 ms 2.70x 11.355 ms 62.9%
50 40 KB 56.068 ms 12.369 ms 4.53x 43.699 ms 77.9%
100 80 KB 141.798 ms 22.382 ms 6.34x 119.416 ms 84.2%
300 240 KB 499.280 ms 91.193 ms 5.47x 408.087 ms 81.7%
1000 800 KB 4.824 s 700.926 ms 6.88x 4.123 s 85.5%
  • No need to test
    • I checked and no code files have been changed.

Verification:

  • make bazel_prepare
  • go test ./pkg/dxf/framework/proto -run '^TestTaskCleanupBatchSize$' -tags=intest,deadlock -count=1
  • ./tools/check/failpoint-go-test.sh pkg/dxf/framework/scheduler -run '^(TestSchedulerCleanupTask|TestCleanUpRoutine)$' -count=1
  • ./tools/check/failpoint-go-test.sh pkg/dxf/framework/storage -run '^(TestTaskHistoryTable|TestTransferTasks2HistoryUsesBatchStatements)$' -count=1
  • ./tools/check/failpoint-go-test.sh pkg/server/handler/tests -run '^(TestDXFAPI|TestDXFMaintenanceAPINotAvailableInUserKeyspace)$' -tags=intest,deadlock,nextgen -count=1
  • make lint
  • git diff --check upstream/master

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

Improve DXF cleanup throughput by batching history transfers and draining finished tasks in bounded batches. Add the SYSTEM-keyspace `/dxf/schedule/task_cleanup_batch_size` HTTP API to tune the process-local batch size from 1 to 1000.

Summary by CodeRabbit

Summary

  • New Features
    • Added a system-keyspace maintenance API to view and update DXF task cleanup batch size (default 20, allowed 1–1,000), stored in memory and reset on restart.
    • Requests to these maintenance endpoints return 404 from user keyspaces.
  • Improvements
    • DXF cleanup now processes tasks in bounded batches according to the configured limit.
    • Task and subtask history transfers are optimized to use more bulk updates/inserts.
  • Documentation
    • Added docs describing the new API, limits, and memory-only persistence behavior.

D3Hunter added 2 commits July 13, 2026 17:20
dxf: bound task history transfer batches

docs: design DXF cleanup batch tuning

dxf: add task cleanup batch setting

dxf: decouple cleanup batch from concurrency

server: expose DXF cleanup batch setting

build: update DXF test metadata

docs: document DXF cleanup batch API

change
@ti-chi-bot ti-chi-bot Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds owner-local DXF cleanup batch tuning, bounded task selection, bulk task and subtask history transfer, and a SYSTEM keyspace HTTP API with tests and documentation.

Changes

DXF cleanup batching

Layer / File(s) Summary
Cleanup batch tuning
pkg/dxf/framework/proto/task.go, pkg/dxf/framework/proto/task_test.go, pkg/dxf/framework/proto/BUILD.bazel, .agents/skills/tidb-test-guidelines/...
Defines cleanup batch bounds, atomic accessors, test restoration, and related test metadata.
Cleanup selection ordering
pkg/dxf/framework/{scheduler,storage}/..., pkg/dxf/framework/mock/..., pkg/dxf/framework/{scheduler,storage}/*_test.go
Replaces arbitrary state selection with bounded cleanup selection and validates cleanup draining and task-base queries.
Bulk history transfer
pkg/dxf/framework/storage/history.go, pkg/dxf/framework/storage/table_test.go
Replaces per-task metadata and subtask history operations with bulk transactional SQL and validates transferred versus untouched records.
Maintenance API integration
pkg/server/handler/tikvhandler/dxf.go, pkg/server/http_status.go, pkg/server/handler/tests/dxf_test.go, pkg/server/handler/tests/BUILD.bazel, docs/tidb_http_api.md
Adds the SYSTEM keyspace cleanup batch endpoint, validates methods and values, restricts maintenance APIs by keyspace, and documents memory-only behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DXFTaskCleanupBatchSizeHandler
  participant TaskCleanupBatchSize
  Client->>DXFTaskCleanupBatchSizeHandler: GET or POST cleanup batch size
  DXFTaskCleanupBatchSizeHandler->>TaskCleanupBatchSize: read or validate and update
  TaskCleanupBatchSize-->>DXFTaskCleanupBatchSizeHandler: return current value
  DXFTaskCleanupBatchSizeHandler-->>Client: return JSON response
Loading

Suggested labels: approved, lgtm

Suggested reviewers: wjhuang2016, joechenrh, gmhdbjd, expxiaoli

Poem

A rabbit tuned the cleanup dial,
And hurried history in a bulked-up style.
Tasks stayed bounded, rows moved neat,
The SYSTEM API made control complete.
“Hop!” said the hare, “what a tidy feat!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and clearly reflects the main change: bounded DXF cleanup plus batched history transfer.
Description check ✅ Passed The description includes the required issue reference, problem summary, change details, test checklist, side effects, documentation, and release note.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 (1)
pkg/dxf/framework/storage/table_test.go (1)

1374-1377: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert batching for the global-task transfer statements.

The test counts only the metadata update and subtask operations. Also assert the task-history insert and active-task delete occur once, preventing a regression to per-task SQL.

Proposed assertions
 require.Equal(t, 1, recorder.countContains("update mysql.tidb_global_task"))
 recorder.requireContains(t, "set meta = case id")
+require.Equal(t, 1, recorder.countContains("insert into mysql.tidb_global_task_history"))
+require.Equal(t, 1, recorder.countContains("delete from mysql.tidb_global_task"))
 require.Equal(t, 1, recorder.countContains("insert into mysql.tidb_background_subtask_history"))
 require.Equal(t, 1, recorder.countContains("delete from mysql.tidb_background_subtask"))

Based on the PR objective to batch both task and subtask history transfers.

🤖 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 `@pkg/dxf/framework/storage/table_test.go` around lines 1374 - 1377, Extend the
assertions in the relevant transfer test to verify the task-history insert and
active-task delete statements each occur exactly once, using the recorder’s
count helper and matching the existing global-task SQL patterns. Keep the
current metadata and subtask batching assertions unchanged.
🤖 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 `@pkg/dxf/framework/scheduler/scheduler_manager.go`:
- Around line 429-456: Update the cleanup loop around cleanupFinishedTasks so
“cleanup routine success” is logged only when allCleaned is true; retain the
existing failure handling and termination behavior for false results.

---

Nitpick comments:
In `@pkg/dxf/framework/storage/table_test.go`:
- Around line 1374-1377: Extend the assertions in the relevant transfer test to
verify the task-history insert and active-task delete statements each occur
exactly once, using the recorder’s count helper and matching the existing
global-task SQL patterns. Keep the current metadata and subtask batching
assertions unchanged.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2521aa34-7cda-40f6-915a-d9d69c849483

📥 Commits

Reviewing files that changed from the base of the PR and between 3268b65 and 087d0e9.

📒 Files selected for processing (15)
  • .agents/skills/tidb-test-guidelines/references/dxf-case-map.md
  • docs/tidb_http_api.md
  • pkg/dxf/framework/proto/BUILD.bazel
  • pkg/dxf/framework/proto/task.go
  • pkg/dxf/framework/proto/task_test.go
  • pkg/dxf/framework/scheduler/scheduler_manager.go
  • pkg/dxf/framework/scheduler/scheduler_manager_nokit_test.go
  • pkg/dxf/framework/storage/BUILD.bazel
  • pkg/dxf/framework/storage/history.go
  • pkg/dxf/framework/storage/table_test.go
  • pkg/dxf/framework/storage/task_table.go
  • pkg/server/handler/tests/BUILD.bazel
  • pkg/server/handler/tests/dxf_test.go
  • pkg/server/handler/tikvhandler/dxf.go
  • pkg/server/http_status.go

Comment on lines 429 to 456
for {
cleanupBatchSize := proto.GetTaskCleanupBatchSize()
tasks, err := sm.taskMgr.GetTasksInStates(
sm.ctx,
proto.TaskStateFailed,
proto.TaskStateReverted,
proto.TaskStateSucceed,
)
if err != nil {
sm.logger.Warn("get task in states failed", zap.Error(err))
return
}
if len(tasks) == 0 {
return
}
sm.logger.Info("cleanup routine start")
allCleaned, err := sm.cleanupFinishedTasks(tasks)
if err != nil {
sm.logger.Warn("cleanup routine failed", zap.Error(err))
return
}
failpoint.InjectCall("WaitCleanUpFinished")
sm.logger.Info("cleanup routine success")
if !allCleaned || len(tasks) < cleanupBatchSize {
return
}
}
failpoint.InjectCall("WaitCleanUpFinished")
sm.logger.Info("cleanup routine success")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Log "cleanup routine success" is emitted even when allCleaned is false.

When a cleanup error occurs (firstErr != nil) but TransferTasks2History succeeds (err == nil), cleanupFinishedTasks returns (false, nil). The doCleanupTask error check passes, and line 451 logs "cleanup routine success" — contradicting the "cleanup routine failed" Warn message already logged inside cleanupFinishedTasks at line 482. This produces contradictory log lines for the same batch.

🔧 Suggested fix: gate the success log on `allCleaned`
 	failpoint.InjectCall("WaitCleanUpFinished")
-	sm.logger.Info("cleanup routine success")
 	if !allCleaned || len(tasks) < cleanupBatchSize {
+		if !allCleaned {
+			sm.logger.Info("cleanup routine partially failed")
+		}
 		return
 	}
+	sm.logger.Info("cleanup routine success")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for {
cleanupBatchSize := proto.GetTaskCleanupBatchSize()
tasks, err := sm.taskMgr.GetTasksInStates(
sm.ctx,
proto.TaskStateFailed,
proto.TaskStateReverted,
proto.TaskStateSucceed,
)
if err != nil {
sm.logger.Warn("get task in states failed", zap.Error(err))
return
}
if len(tasks) == 0 {
return
}
sm.logger.Info("cleanup routine start")
allCleaned, err := sm.cleanupFinishedTasks(tasks)
if err != nil {
sm.logger.Warn("cleanup routine failed", zap.Error(err))
return
}
failpoint.InjectCall("WaitCleanUpFinished")
sm.logger.Info("cleanup routine success")
if !allCleaned || len(tasks) < cleanupBatchSize {
return
}
}
failpoint.InjectCall("WaitCleanUpFinished")
sm.logger.Info("cleanup routine success")
}
for {
cleanupBatchSize := proto.GetTaskCleanupBatchSize()
tasks, err := sm.taskMgr.GetTasksInStates(
sm.ctx,
proto.TaskStateFailed,
proto.TaskStateReverted,
proto.TaskStateSucceed,
)
if err != nil {
sm.logger.Warn("get task in states failed", zap.Error(err))
return
}
if len(tasks) == 0 {
return
}
sm.logger.Info("cleanup routine start")
allCleaned, err := sm.cleanupFinishedTasks(tasks)
if err != nil {
sm.logger.Warn("cleanup routine failed", zap.Error(err))
return
}
failpoint.InjectCall("WaitCleanUpFinished")
if !allCleaned || len(tasks) < cleanupBatchSize {
if !allCleaned {
sm.logger.Info("cleanup routine partially failed")
}
return
}
sm.logger.Info("cleanup routine success")
}
}
🤖 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 `@pkg/dxf/framework/scheduler/scheduler_manager.go` around lines 429 - 456,
Update the cleanup loop around cleanupFinishedTasks so “cleanup routine success”
is logged only when allCleaned is true; retain the existing failure handling and
termination behavior for false results.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 1.26582% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.2264%. Comparing base (59f6e85) to head (67c26ce).
⚠️ Report is 4 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #69811        +/-   ##
================================================
- Coverage   76.3196%   74.2264%   -2.0932%     
================================================
  Files          2041       2054        +13     
  Lines        560096     576766     +16670     
================================================
+ Hits         427463     428113       +650     
- Misses       131732     148302     +16570     
+ Partials        901        351       -550     
Flag Coverage Δ
integration 40.8845% <1.2658%> (+1.1792%) ⬆️

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

Components Coverage Δ
dumpling 60.4471% <ø> (ø)
parser ∅ <ø> (∅)
br 47.4060% <ø> (-15.3154%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ingress-bot

Copy link
Copy Markdown

🔍 Starting code review for this PR...

@ti-chi-bot ti-chi-bot Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Jul 13, 2026

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/dxf/framework/storage/task_table.go (1)

438-453: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Removing ORDER BY contradicts the PR objective of prioritizing oldest terminal-state transitions.

The PR summary states cleanup "prioritizes the oldest terminal-state transitions," but this change removes the ORDER BY clause entirely. Without explicit ordering, TiDB may return rows in any order — there is no guarantee that the oldest terminal-state tasks are selected first. The downstream consumer (doCleanupTask in scheduler_manager.go) processes whatever GetTasksInStates returns without additional sorting, so the non-determinism propagates directly to cleanup ordering.

If oldest-first processing is intended, restore an ORDER BY clause (e.g., ORDER BY state_update_time ASC, create_time ASC, id ASC) before the LIMIT. If ordering is no longer a requirement, update the PR summary to avoid misleading documentation.

🔧 Proposed fix: restore ORDER BY before LIMIT
	rs, err := mgr.ExecuteSQLWithNewSession(ctx,
		"select "+TaskColumns+" from mysql.tidb_global_task t "+
			"where state in ("+strings.Repeat("%?,", len(states)-1)+"%?)"+
+			" order by state_update_time, create_time, id"+
			" limit %?", args...)
🤖 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 `@pkg/dxf/framework/storage/task_table.go` around lines 438 - 453, Restore
deterministic oldest-first ordering in TaskManager.GetTasksInStates by adding
ORDER BY state_update_time ASC, create_time ASC, and id ASC before the existing
LIMIT clause, preserving the current state filtering and cleanup batch size
behavior.
🤖 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.

Outside diff comments:
In `@pkg/dxf/framework/storage/task_table.go`:
- Around line 438-453: Restore deterministic oldest-first ordering in
TaskManager.GetTasksInStates by adding ORDER BY state_update_time ASC,
create_time ASC, and id ASC before the existing LIMIT clause, preserving the
current state filtering and cleanup batch size behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: be39b51d-38fa-44dd-8c49-3198c372d3c7

📥 Commits

Reviewing files that changed from the base of the PR and between 4a1ae93 and a5a5c8f.

📒 Files selected for processing (3)
  • .agents/skills/tidb-test-guidelines/references/dxf-case-map.md
  • pkg/dxf/framework/storage/table_test.go
  • pkg/dxf/framework/storage/task_table.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • .agents/skills/tidb-test-guidelines/references/dxf-case-map.md

@ingress-bot ingress-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.

This review was generated by AI and should be verified by a human reviewer.
Manual follow-up is recommended before merge.

Summary

  • Total findings: 12
  • Inline comments: 6
  • Summary-only findings (no inline anchor): 4
Findings (highest risk first)

⚠️ [Major] (1)

  1. Shared multi-consumer interface method GetTasksInStates now hard-codes a cleanup-only limit (pkg/dxf/framework/storage/task_table.go:438, pkg/dxf/framework/scheduler/interface.go:43, pkg/dxf/framework/taskexecutor/interface.go:32)

🟡 [Minor] (11)

  1. GetTasksInStates doc comment hides both a dropped ordering guarantee and a new silent cap (pkg/dxf/framework/storage/task_table.go:438, pkg/dxf/framework/storage/task_table.go:464, pkg/dxf/framework/scheduler/interface.go:43, pkg/dxf/framework/taskexecutor/interface.go:32)
  2. DXFTaskCleanupBatchSizeHandler duplicates DXFTaskMaxConcurrentHandler's ServeHTTP structure verbatim (pkg/server/handler/tikvhandler/dxf.go:390, pkg/server/handler/tikvhandler/dxf.go:348)
  3. Subtask-history transfer logic duplicated instead of reusing TransferSubtasks2HistoryWithSession (pkg/dxf/framework/storage/history.go:98, pkg/dxf/framework/storage/history.go:42)
  4. Task cleanup batch size knob duplicates the max-concurrent-task knob machinery in proto/task.go verbatim (pkg/dxf/framework/proto/task.go:116, pkg/dxf/framework/proto/task.go:124)
  5. New task_cleanup_batch_size doc states a SYSTEM-keyspace restriction the sibling max_concurrent_task section omits (docs/tidb_http_api.md:1024, docs/tidb_http_api.md:1058, pkg/server/http_status.go:252)
  6. New cleanup-batch-size subtest sets an unrelated MaxConcurrentTask override (pkg/dxf/framework/storage/table_test.go:1057)
  7. TestTaskHistoryTable positionally compares now-unordered GetTasksInStates against ordered GetTaskBasesInStates (pkg/dxf/framework/storage/table_test.go:1085, pkg/dxf/framework/storage/task_table.go:438, pkg/dxf/framework/storage/task_table.go:464)
  8. No regression test that the batch-limited cleanup still drains all finished tasks across cycles (pkg/dxf/framework/storage/task_table.go:449, pkg/dxf/framework/scheduler/scheduler_manager.go:427, pkg/dxf/framework/storage/table_test.go:1056)
  9. Cleanup pass is now capped at batchSize with no re-trigger, so a signal-less backlog drains only at the 10-minute ticker (pkg/dxf/framework/storage/task_table.go:453, pkg/dxf/framework/scheduler/scheduler_manager.go:427, pkg/dxf/framework/scheduler/scheduler_manager.go:406)
  10. GetTasksInStates now silently truncates and unorders results via a cleanup-specific knob (pkg/dxf/framework/storage/task_table.go:453, pkg/dxf/framework/scheduler/interface.go:43, pkg/dxf/framework/taskexecutor/interface.go:32)
  11. TestTaskHistoryTable couples now-unordered GetTasksInStates result to ordered GetTaskBasesInStates (pkg/dxf/framework/storage/task_table.go:453, pkg/dxf/framework/storage/table_test.go:1085)

Unanchored findings

⚠️ [Major] (1)

  1. Shared multi-consumer interface method GetTasksInStates now hard-codes a cleanup-only limit
    • Request: Scope the cleanup batch limit to the cleanup call site instead of the shared interface method, e.g. add an explicit limit int parameter (or a dedicated method used only by doCleanupTask) rather than folding a single caller's tuning knob into a method shared by two independent interfaces. If the shared method must carry the limit, document the capped/unordered behavior on the interface declaration the way GetTopUnfinishedTasks does.

🟡 [Minor] (3)

  1. DXFTaskCleanupBatchSizeHandler duplicates DXFTaskMaxConcurrentHandler's ServeHTTP structure verbatim
    • Request: Extract a single generic handler (or a shared serveBoundedIntKnob(w, req, name string, get func() int, set func(int) error) helper) that both DXFTaskMaxConcurrentHandler and DXFTaskCleanupBatchSizeHandler call, so the GET/POST/parse/log/response logic exists once.
  2. Subtask-history transfer logic duplicated instead of reusing TransferSubtasks2HistoryWithSession
    • Request: Consolidate: either give TransferSubtasks2HistoryWithSession a batched (taskIDs []int64) variant that TransferTasks2History calls, or fold the single-task test helper into the batched form so there is one source of truth for the subtask-history-transfer SQL.
  3. Task cleanup batch size knob duplicates the max-concurrent-task knob machinery in proto/task.go verbatim
    • Request: Factor the bounded, in-memory, owner-local atomic.Int64 knob pattern (bounds check + load + store + test-restore closure) into one small generic helper in proto (for example a boundedKnob type with Get/Set/SetForTest), and construct both maxConcurrentTask and taskCleanupBatchSize from it.

Comment thread pkg/dxf/framework/storage/task_table.go Outdated
Comment thread docs/tidb_http_api.md
Comment thread pkg/dxf/framework/storage/table_test.go Outdated
Comment thread pkg/dxf/framework/storage/table_test.go
Comment thread pkg/dxf/framework/storage/task_table.go Outdated
Comment thread pkg/dxf/framework/storage/task_table.go Outdated
@ti-chi-bot ti-chi-bot Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 14, 2026
@D3Hunter

Copy link
Copy Markdown
Contributor Author

/cherry-pick release-nextgen-202603

@ti-chi-bot

Copy link
Copy Markdown
Member

@D3Hunter: once the present PR merges, I will cherry-pick it on top of release-nextgen-202603 in the new PR and assign it to you.

Details

In response to this:

/cherry-pick release-nextgen-202603

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@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 (1)
pkg/dxf/framework/storage/table_test.go (1)

1164-1165: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The in-memory error assignment is not persisted by TransferTasks2History.

TransferTasks2History only updates the meta column from the provided task slice before copying the row to history; it does not write the Error field to the database. Setting untouchedTask.Error = ... here is an in-memory no-op for the subsequent transfer. If the goal is to test transferring a task with an error, the error must be persisted first (e.g., via FailTask, as is done correctly in the pagination subtest below).

♻️ Proposed fix
 	// task with fail transfer
-	untouchedTask.Error = errors.New("mock err")
+	require.NoError(t, gm.FailTask(ctx, untouchedTask.ID, proto.TaskStatePending, errors.New("mock err")))
 	require.NoError(t, gm.TransferTasks2History(ctx, []*proto.Task{untouchedTask}))
🤖 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 `@pkg/dxf/framework/storage/table_test.go` around lines 1164 - 1165, Update the
test around TransferTasks2History so the task error is persisted through the
storage API before transferring it, rather than assigning untouchedTask.Error
only in memory. Reuse the existing FailTask flow demonstrated in the pagination
subtest, then keep the TransferTasks2History assertion to verify history
transfer of the persisted error.
🤖 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 `@pkg/dxf/framework/storage/task_table.go`:
- Around line 443-447: Update the cleanup query in the task-table method using
ExecuteSQLWithNewSession to order terminal tasks by state_update_time ascending
before applying the limit, adding id ascending as a deterministic tie-breaker if
supported. Preserve the existing terminal-state filters and cleanup batch-size
limit.

---

Nitpick comments:
In `@pkg/dxf/framework/storage/table_test.go`:
- Around line 1164-1165: Update the test around TransferTasks2History so the
task error is persisted through the storage API before transferring it, rather
than assigning untouchedTask.Error only in memory. Reuse the existing FailTask
flow demonstrated in the pagination subtest, then keep the TransferTasks2History
assertion to verify history transfer of the persisted error.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2ac40fb2-3e08-4d22-8ce4-1aa076eb7cfa

📥 Commits

Reviewing files that changed from the base of the PR and between a5a5c8f and 67c26ce.

📒 Files selected for processing (11)
  • .agents/skills/tidb-test-guidelines/references/dxf-case-map.md
  • pkg/dxf/framework/mock/scheduler_mock.go
  • pkg/dxf/framework/mock/task_executor_mock.go
  • pkg/dxf/framework/scheduler/interface.go
  • pkg/dxf/framework/scheduler/scheduler_manager.go
  • pkg/dxf/framework/scheduler/scheduler_manager_nokit_test.go
  • pkg/dxf/framework/scheduler/scheduler_manager_test.go
  • pkg/dxf/framework/scheduler/scheduler_test.go
  • pkg/dxf/framework/storage/table_test.go
  • pkg/dxf/framework/storage/task_table.go
  • pkg/dxf/framework/taskexecutor/interface.go
💤 Files with no reviewable changes (2)
  • pkg/dxf/framework/taskexecutor/interface.go
  • pkg/dxf/framework/mock/task_executor_mock.go

Comment thread pkg/dxf/framework/storage/task_table.go
@D3Hunter

Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot

ti-chi-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: wjhuang2016

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jul 14, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-07-14 05:38:23.464620346 +0000 UTC m=+692089.500715412: ☑️ agreed by wjhuang2016.

}

func TestTaskHistoryTable(t *testing.T) {
t.Run("cleanup tasks are batch limited", func(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Extract to a separate test

_, err = sqlexec.ExecSQL(ctx, exec, `
insert into mysql.tidb_background_subtask_history
select * from mysql.tidb_background_subtask
where task_key in(`+taskIDList+`)`)

@joechenrh joechenrh Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Passing integer here will introduce CAST, thus make this query a full table scan instead of index look up.

root@127.0.0.1 [test]> EXPLAIN SELECT * FROM subtask WHERE task_key = '1';
+-------------------------------+---------+-----------+---------------------------------------------+-------------------------------------------------+
| id                            | estRows | task      | access object                               | operator info                                   |
+-------------------------------+---------+-----------+---------------------------------------------+-------------------------------------------------+
| IndexLookUp_8                 | 10.00   | root      |                                             |                                                 |
| ├─IndexRangeScan_6(Build)     | 10.00   | cop[tikv] | table:subtask, index:idx_task_key(task_key) | range:["1","1"], keep order:false, stats:pseudo |
| └─TableRowIDScan_7(Probe)     | 10.00   | cop[tikv] | table:subtask                               | keep order:false, stats:pseudo                  |
+-------------------------------+---------+-----------+---------------------------------------------+-------------------------------------------------+
3 rows in set (0.004 sec)

root@127.0.0.1 [test]> EXPLAIN SELECT * FROM subtask WHERE task_key = 1;
+-------------------------+----------+-----------+---------------+---------------------------------------------------+
| id                      | estRows  | task      | access object | operator info                                     |
+-------------------------+----------+-----------+---------------+---------------------------------------------------+
| TableReader_8           | 8000.00  | root      |               | data:Selection_7                                  |
| └─Selection_7           | 8000.00  | cop[tikv] |               | eq(cast(test.subtask.task_key, double BINARY), 1) |
|   └─TableFullScan_6     | 10000.00 | cop[tikv] | table:subtask | keep order:false, stats:pseudo                    |
+-------------------------+----------+-----------+---------------+---------------------------------------------------+
3 rows in set (0.001 sec)

@joechenrh joechenrh Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My local test shows that only using string task keys can already get a 4.55× speedup. But since there is no network transmission here, the actual improvement in the cloud environment remains uncertain.

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

Labels

approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants