Skip to content

importinto: fix conflict row identity and MVI deduplication (#70076) - #70208

Merged
ti-chi-bot[bot] merged 2 commits into
pingcap:release-nextgen-202603from
ti-chi-bot:cherry-pick-70076-to-release-nextgen-202603
Jul 30, 2026
Merged

ti-chi-bot[bot] merged 2 commits into
pingcap:release-nextgen-202603from
ti-chi-bot:cherry-pick-70076-to-release-nextgen-202603

Conversation

@ti-chi-bot

@ti-chi-bot ti-chi-bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Member

This is an automated cherry-pick of #70076

What problem does this PR solve?

Issue Number: close #69799, close #69800, close #69801

Problem Summary:

Global-sort IMPORT INTO ... WITH on_duplicate_key='capture' can identify conflicted rows incorrectly in three related cases:

  • Multiple elements of one multi-valued unique index can point to the same row in different buffers or workers, causing the row to be collected and checksummed more than once.
  • A GLOBAL unique index on a partitioned table carries the logical table ID in the index key but the physical partition ID in its partition handle. Rebuilding the record key from the logical table ID can miss the row KV, so conflict collection and deletion do not see the complete row.
  • The bounded deduplication set uses Handle.String() as row identity. Distinct common handles can have the same display string, and partition handles omit the partition ID from that string, causing valid rows to be skipped.

These cases can produce an incorrect conflicted-row count or checksum, fail post-process checksum validation, or leave inconsistent row and index KVs after conflict resolution.

What changed and how does it work?

  • Use the encoded physical data row key as the conflict-row identity and snapshot lookup key. tablecodec.EncodeRecordKey honors PartitionHandle.PartitionID, while the binary row key avoids collisions between distinct common handles.
  • Track both the shared set from completed index KV groups and a worker-local set for the current group. A row is added to the local set only after it has been re-encoded and handled successfully, which deduplicates repeated MVI handles across buffer flushes without hiding failed work.
  • For multi-valued indexes, decode each conflicted index KV and consistently dispatch all KVs for the same encoded handle to one collector. This keeps per-row deduplication effective across concurrent workers.
  • Add focused unit coverage for physical row keys, bounded key filters, MVI dispatch, cross-buffer deduplication, and failed-row retry behavior, plus a RealTiKV regression for the multi-valued unique-index conflict topology.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
Scenario Step Master PR #70076 Delta PR vs. master
15M conflicted rows, 4 plain UKs collect-conflicts 1m55.523s 1m56.975s +1.452s 1.26% slower
  conflict-resolution 10m21.936s 10m20.545s -1.391s 0.22% faster
  Total 12m17.459s 12m17.519s +0.060s 0.01% slower
15M rows, 15M conflicted MV KVs, 1 MV index KV/row collect-conflicts 44.423s 48.553s +4.130s 9.30% slower
  conflict-resolution 8m20.372s 8m33.325s +12.953s 2.59% slower
  Total 9m4.794s 9m21.878s +17.083s 3.14% slower
15M rows, 45M conflicted MV KVs, 3 MV index KVs/row collect-conflicts 2m4.422s 1m36.951s -27.471s 22.08% faster
  conflict-resolution 9m39.286s 9m35.986s -3.300s 0.57% faster
  Total 11m43.708s 11m12.938s -30.771s 4.37% faster

PR #70076 is effectively unchanged for the four-plain-UK workload, with only a 0.01% total-time difference from master. It shows a modest 3.14% slowdown for the single-MV-index-KV workload, including a 9.30% increase in collect-conflicts time.

For the 15M rows, 45M conflicted KVs, three MV index KVs per row workload, PR #70076 is 4.37% faster overall, mainly because collect-conflicts is 22.08% faster. However, the master run is not a successful baseline: it failed with the expected checksum mismatch. On master, the MV index KVs associated with each row are handled three times, and write conflicts occur while deleting them. This additional work makes master slightly slower than the PR, particularly during conflict resolution, where the PR is 3.300 seconds, or 0.57%, faster.

Overall, the results do not show a consistent performance regression from the new KV routing. The PR has a small penalty in the 15M-KV case but improves the heavier 45M-KV case. Because the master run for the 45M-KV scenario failed and performed extra conflict-handling work, that result should be treated as a diagnostic time comparison rather than a like-for-like successful end-to-end comparison.

  • No need to test
    • I checked and no code files have been changed.

Commands run:

make bazel_prepare
./tools/check/failpoint-go-test.sh pkg/dxf/importinto -run 'TestCollectConflictsKVGroupIndexInfo|TestDispatchMVIndexKVPairs' -count=1
./tools/check/failpoint-go-test.sh pkg/dxf/importinto/conflictedkv -run 'TestHandler|TestKeyFilter|TestBoundedKeySet' -count=1
./tools/check/failpoint-go-test.sh tests/realtikvtest/importintotest4 -run 'TestImportInto/TestGlobalSortMultiValuedUniqueIndexCountsConflictedRowOnce' -count=1 -args -tikv-path 'tikv://127.0.0.1:12379?disableGC=true'

The RealTiKV case used an isolated tiup playground --mode tikv-slim --tag realtikvtest --port-offset 10000, and the PD endpoint was verified unreachable after teardown.

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.

Fix an issue where global-sort IMPORT INTO in conflict-capture mode could miscount or fail to delete conflicted rows for multi-valued unique indexes, common handles, or GLOBAL indexes on partitioned tables.

Summary by CodeRabbit

  • Bug Fixes
    • Improved import conflict detection and resolution for data and multi-valued unique indexes, including correct row-local MV routing.
    • Prevented duplicate conflict counting across table partitions with more accurate deduplication.
    • Added more robust retries for transient transaction and commit-related conflicts during conflict cleanup.
    • Improved cancellation/error handling during conflict processing by using bounded row-key tracking for memory control.
  • Documentation
    • Clarified when checksum verification is skipped due to in-memory row-key limit conditions.
  • Tests
    • Added internal and real-TiKV coverage for MV dispatch, cancellation behavior, and row-key based filtering.

Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
@ti-chi-bot ti-chi-bot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. 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. type/cherry-pick-for-release-nextgen-202603 labels Jul 29, 2026
@ti-chi-bot

Copy link
Copy Markdown
Member Author

@D3Hunter This PR has conflicts, I have hold it.
Please resolve them or ask others to resolve them, then comment /unhold to remove the hold label.

@ti-chi-bot

ti-chi-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

@ti-chi-bot: ## If you want to know how to resolve it, please read the guide in TiDB Dev Guide.

Details

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 commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 56e5bd7d-33d7-4bc1-9348-72601446ee2f

📥 Commits

Reviewing files that changed from the base of the PR and between 80fd108 and 1a2fc2b.

📒 Files selected for processing (11)
  • pkg/dxf/importinto/BUILD.bazel
  • pkg/dxf/importinto/collect_conflicts.go
  • pkg/dxf/importinto/collect_conflicts_internal_test.go
  • pkg/dxf/importinto/conflict_resolution.go
  • pkg/dxf/importinto/conflictedkv/collector.go
  • pkg/dxf/importinto/conflictedkv/collector_test.go
  • pkg/dxf/importinto/conflictedkv/deleter.go
  • pkg/dxf/importinto/conflictedkv/deleter_test.go
  • pkg/dxf/importinto/conflictedkv/handler.go
  • pkg/dxf/importinto/conflictedkv/handler_test.go
  • tests/realtikvtest/importintotest4/BUILD.bazel
💤 Files with no reviewable changes (1)
  • pkg/dxf/importinto/conflict_resolution.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/dxf/importinto/collect_conflicts_internal_test.go
  • pkg/dxf/importinto/conflictedkv/deleter.go

📝 Walkthrough

Walkthrough

Conflict collection and resolution now identify rows by physical row keys instead of handles, decode keys through storage codecs, dispatch MV-index pairs to dedicated workers, and retry transaction-retryable deletions. Tests cover bounded tracking, cancellation, codec variants, multi-valued indexes, partitioned global indexes, and conflicting common handles.

Changes

Conflict Processing Refactor

Layer / File(s) Summary
Row-key tracking and handler contracts
pkg/dxf/importinto/conflictedkv/row_handle.go, pkg/dxf/importinto/conflictedkv/handler.go, pkg/dxf/importinto/conflictedkv/collector.go, pkg/dxf/importinto/conflictedkv/*_test.go, docs/agents/import-into/README.md
Handle-based filters and bounded sets are replaced with row-key tracking. Handlers decode keys through tikv.Codec, pass row keys through callbacks, and deduplicate multi-valued index rows by physical row key.
MV-index collection and dispatch
pkg/dxf/importinto/collect_conflicts.go, pkg/dxf/importinto/conflict_resolution.go, pkg/dxf/importinto/collect_conflicts_internal_test.go, pkg/dxf/importinto/BUILD.bazel, tests/realtikvtest/importintotest4/conflict_resolution_test.go
MV-index KV pairs are routed to per-worker channels using CRC32 of decoded handles, with local row-key sets merged after collection. Tests cover routing, cancellation, index mapping, and integration scenarios.
Codec-aware deletion and retry validation
pkg/dxf/importinto/conflictedkv/deleter.go, pkg/dxf/importinto/conflictedkv/deleter_internal_test.go, pkg/dxf/importinto/conflictedkv/deleter_test.go, pkg/dxf/importinto/conflictedkv/BUILD.bazel, pkg/dxf/importinto/conflictedkv/doc.go
Deleters use storage codecs, retry transaction-retryable errors, and validate commit failures and codec v1/v2 behavior. Documentation describes row-key checksum tracking.

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

Possibly related PRs

  • pingcap/tidb#70076: Implements the same row-key conflict-handling refactor and MV-index dispatch approach.

Poem

A rabbit watched row keys hop,
While MV pairs found each stop.
CRC32 sent them right,
Codecs made the paths precise,
And retries closed the loop tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% 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
Title check ✅ Passed The title is concise and accurately reflects the main fixes to conflict row identity and MVI deduplication.
Description check ✅ Passed The description follows the template well, includes issue links, problem summary, implementation details, tests, and release note.
Linked Issues check ✅ Passed The code changes address all three linked issues by using physical row keys, fixing partitioned GLOBAL-index lookup, and deduplicating MVI conflicts.
Out of Scope Changes check ✅ Passed The extra docs, Bazel, and test updates support the same importinto fix and do not appear unrelated to the stated objectives.
✨ 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: 3

Caution

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

⚠️ Outside diff range comments (1)
pkg/dxf/importinto/conflictedkv/handler.go (1)

110-126: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

NewBaseHandler was resolved to the old signature while callers use the new one, and collector is never assigned.

BaseHandler now declares collector execute.Collector, but this constructor neither accepts nor sets it, so progress reporting is dead. Meanwhile collector.go (Line 134) and handler_test.go (Lines 115-117, 199-202, 306-314) pass a 7th progressCollector argument. Add the parameter and wire it.

🐛 Proposed fix
 func NewBaseHandler(
 	targetTable table.Table,
 	kvGroup string,
 	codec tikv.Codec,
 	encoder *importer.TableKVEncoder,
 	encodedRowHdl EncodedRowHandler,
+	collector execute.Collector,
 	logger *zap.Logger,
 ) *BaseHandler {
 	return &BaseHandler{
 		targetTable:       targetTable,
 		kvGroup:           kvGroup,
 		codec:             codec,
 		encoder:           encoder,
+		collector:         collector,
 		logger:            logger,
 		EncodedRowHandler: encodedRowHdl,
 	}
 }
🤖 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/importinto/conflictedkv/handler.go` around lines 110 - 126, Update
NewBaseHandler to accept the execute.Collector progressCollector argument
expected by its callers, and assign it to the returned BaseHandler’s collector
field. Preserve the existing initialization of the other constructor fields so
progress reporting is wired without changing unrelated behavior.
🧹 Nitpick comments (1)
tests/realtikvtest/importintotest4/conflict_resolution_test.go (1)

521-541: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider asserting the surviving rows for the common-handle regression.

The other two new tests verify final table state; this one only asserts an empty conflicted-row result, so a regression that deletes the wrong handle could still pass. A MustQuery("select * from t ...") check on the expected remaining rows would pin the intent of issue #69801.

🤖 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 `@tests/realtikvtest/importintotest4/conflict_resolution_test.go` around lines
521 - 541, Extend TestGlobalSortDistinguishesCommonHandlesWithSameString to
assert the final contents of table t after conflict resolution, including the
expected surviving rows and ordering. Keep the existing empty conflicted-row
assertion, and use the established query assertion helper to verify the
regression preserves the correct handles from the two distinct conflict groups.
🤖 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/importinto/conflictedkv/collector_test.go`:
- Around line 29-33: Resolve all merge conflicts in
pkg/dxf/importinto/conflictedkv/collector_test.go:29-33 by retaining imports
required by the resolved API, choose the matching NewCollector constructor calls
at 179-183, make the collector configuration consistent at 267-271, and resolve
both collector initializations consistently at 365-377; remove every conflict
marker so the test file parses.

In `@pkg/dxf/importinto/conflictedkv/deleter.go`:
- Around line 84-88: Resolve the cherry-pick conflict as one coherent API
migration: in pkg/dxf/importinto/conflictedkv/deleter.go lines 84-88, remove the
conflict markers and use the resolved NewBaseHandler contract consistently. In
pkg/dxf/importinto/conflictedkv/deleter_test.go lines 24-32, 139-144, 156-162,
184-200, and 205-223, align imports, NewDeleter arguments, encoded KV channel
types, and KV-group constants with the resolved external/simplesst helper and
deleter.Run APIs, including index-conflict coverage.

In `@pkg/dxf/importinto/conflictedkv/handler.go`:
- Around line 22-27: Remove all unresolved conflict markers and retain the
cherry-pick resolution across the affected sites: in
pkg/dxf/importinto/conflictedkv/handler.go (22-27, 90-103, 197-203, 274-280),
keep execute and the codec/simplesst.KVPair implementations; in
pkg/dxf/importinto/conflictedkv/collector.go (131-135, 161-175), keep the
codec-aware NewBaseHandler call and remove hdlSet.Add; in
pkg/dxf/importinto/conflictedkv/handler_test.go (111-118, 195-203, 254-265),
keep globalsort and execute.TestCollector with the matching resolution. In
pkg/dxf/importinto/collect_conflicts.go (37-41, 184-192), keep
globalsort/simplesst and getKVGroupIndexInfo; in
pkg/dxf/importinto/conflict_resolution.go (143-148), keep
globalsort.ReadKVFilesAsync and createConflictHandlerChannels; in
pkg/dxf/importinto/BUILD.bazel (116-120, 166-170), keep shard_count 37, resolve
dependencies accordingly, and regenerate with make bazel_prepare.

---

Outside diff comments:
In `@pkg/dxf/importinto/conflictedkv/handler.go`:
- Around line 110-126: Update NewBaseHandler to accept the execute.Collector
progressCollector argument expected by its callers, and assign it to the
returned BaseHandler’s collector field. Preserve the existing initialization of
the other constructor fields so progress reporting is wired without changing
unrelated behavior.

---

Nitpick comments:
In `@tests/realtikvtest/importintotest4/conflict_resolution_test.go`:
- Around line 521-541: Extend
TestGlobalSortDistinguishesCommonHandlesWithSameString to assert the final
contents of table t after conflict resolution, including the expected surviving
rows and ordering. Keep the existing empty conflicted-row assertion, and use the
established query assertion helper to verify the regression preserves the
correct handles from the two distinct conflict groups.
🪄 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 Plus

Run ID: 97586dce-3e8b-4cda-8b45-46a09fc758e2

📥 Commits

Reviewing files that changed from the base of the PR and between 8dffb79 and 80fd108.

📒 Files selected for processing (17)
  • docs/agents/import-into/README.md
  • pkg/dxf/importinto/BUILD.bazel
  • pkg/dxf/importinto/collect_conflicts.go
  • pkg/dxf/importinto/collect_conflicts_internal_test.go
  • pkg/dxf/importinto/conflict_resolution.go
  • pkg/dxf/importinto/conflictedkv/BUILD.bazel
  • pkg/dxf/importinto/conflictedkv/collector.go
  • pkg/dxf/importinto/conflictedkv/collector_test.go
  • pkg/dxf/importinto/conflictedkv/deleter.go
  • pkg/dxf/importinto/conflictedkv/deleter_internal_test.go
  • pkg/dxf/importinto/conflictedkv/deleter_test.go
  • pkg/dxf/importinto/conflictedkv/doc.go
  • pkg/dxf/importinto/conflictedkv/handler.go
  • pkg/dxf/importinto/conflictedkv/handler_test.go
  • pkg/dxf/importinto/conflictedkv/row_handle.go
  • pkg/dxf/importinto/conflictedkv/row_handle_test.go
  • tests/realtikvtest/importintotest4/conflict_resolution_test.go
💤 Files with no reviewable changes (1)
  • pkg/dxf/importinto/conflictedkv/BUILD.bazel

Comment thread pkg/dxf/importinto/conflictedkv/collector_test.go Outdated
Comment thread pkg/dxf/importinto/conflictedkv/deleter.go Outdated
Comment thread pkg/dxf/importinto/conflictedkv/handler.go Outdated
@ti-chi-bot

Copy link
Copy Markdown
Member Author

Cherry-pick conflicts appear resolved; removing the do-not-merge/hold label.

@ti-chi-bot ti-chi-bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 29, 2026
@ti-chi-bot ti-chi-bot Bot added approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jul 29, 2026
@codecov

codecov Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 8 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (release-nextgen-202603@8dffb79). Learn more about missing BASE report.

Additional details and impacted files
@@                     Coverage Diff                     @@
##             release-nextgen-202603     #70208   +/-   ##
===========================================================
  Coverage                          ?   76.2610%           
===========================================================
  Files                             ?       1937           
  Lines                             ?     541444           
  Branches                          ?          0           
===========================================================
  Hits                              ?     412911           
  Misses                            ?     128533           
  Partials                          ?          0           
Flag Coverage Δ
unit 76.2610% <95.4545%> (?)

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

Components Coverage Δ
dumpling 61.5065% <0.0000%> (?)
parser ∅ <0.0000%> (?)
br 48.8147% <0.0000%> (?)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ti-chi-bot

ti-chi-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: D3Hunter, joechenrh

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 lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jul 30, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-07-29 11:58:44.180200465 +0000 UTC m=+2010910.216295522: ☑️ agreed by D3Hunter.
  • 2026-07-30 02:48:24.159012757 +0000 UTC m=+2064290.195107803: ☑️ agreed by joechenrh.

@ti-chi-bot
ti-chi-bot Bot merged commit 6b91e9d into pingcap:release-nextgen-202603 Jul 30, 2026
18 checks passed
@ti-chi-bot
ti-chi-bot Bot deleted the cherry-pick-70076-to-release-nextgen-202603 branch July 30, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved 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. type/cherry-pick-for-release-nextgen-202603

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants