Repository navigation
dm: support FK causality for one-to-one routes - #12675
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for foreign key causality checks under multi-worker syncer configurations with routing rules, specifically handling case-insensitive table routing and rejecting DML boundary-changing options (like compact and multiple-rows) when foreign_key_checks is enabled. The feedback identifies three potential nil pointer dereference panics due to missing nil checks on filter.Table parameters in sameForeignKeyTableIdentity, foreignKeyRouteTopologyTableID, and causalityKeySourceTableNameForRowChange.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/test pull-syncdiff-integration-test |
|
cc @joechenrh PTAL. |
There was a problem hiding this comment.
make the file name the same as the inner used db name fk_route_src
There was a problem hiding this comment.
I used db1.prepare.sql to follow the existing DM integration naming convention for source-1 SQL files. The schema name itself is still fk_route_src in the SQL and task config.
There was a problem hiding this comment.
i guess existing are wrong, please remove the db1 prefix or rename using real schema name or source1
| return dmls, nil | ||
| } | ||
|
|
||
| func (s *Syncer) causalityKeySourceTableNameForRowChange(sourceTable *filter.Table) *cdcmodel.TableName { |
There was a problem hiding this comment.
| func (s *Syncer) causalityKeySourceTableNameForRowChange(sourceTable *filter.Table) *cdcmodel.TableName { | |
| func (s *Syncer) getCausalityKeySourceTableName(sourceTable *filter.Table) *cdcmodel.TableName { |
There was a problem hiding this comment.
The current name is meant to tie this helper to the RowChange.SetCausalityKeySourceTable hook. It only changes the source table used by CausalityKeys(), not the row change source table itself. If the shorter name is not a must-change for you, I'd prefer to keep this one.
| }) | ||
| p := parser.New() | ||
|
|
||
| execTrackedDDL(t, syncer.schemaTracker, p, "", "create database db") |
There was a problem hiding this comment.
maybe abstract those common pattern a bit, such as prepareSimpleParentChildSchema
There was a problem hiding this comment.
My concern with a higher-level helper here is that the nearby cases vary in schema case, route rules, target table names, and downstream FK definitions. The shared lower-level pieces are already covered by execTrackedDDL and expectFetchAllDoTables.
There was a problem hiding this comment.
just let AI try to do it without hurting readibility
| sourceTable *cdcmodel.TableName | ||
| targetTable *cdcmodel.TableName | ||
| // Optional source table name used only by CausalityKeys. | ||
| causalityKeySourceTable *cdcmodel.TableName |
There was a problem hiding this comment.
can we set sourceTable to the right table directly, so no need to add a separate field
There was a problem hiding this comment.
I think we should keep these two identities separate here. sourceTable is still the real row-change source table and is used by GetSourceTable(), DML job metadata, validator/checkpoint paths, and logs. This field only overrides the table name used by CausalityKeys(), so the FK causality key can use the normalized source-domain identity without changing the row change's source table semantics.
| return ok | ||
| } | ||
|
|
||
| func (s *Syncer) markForeignKeyRouteTopologyChecked(cacheKey string) { |
There was a problem hiding this comment.
the key is always the same, what's the purpose to make a map?
add comment to explain this, if for future extension, we can do it later
There was a problem hiding this comment.
The map/key came from an earlier version that cached checks per routed target set for the current FK graph, instead of one task-level check. We moved back to task-level checking because scoped checks need more support to catch parent-only DML cases early enough. With the current task-level check, there is only one checked state, so I simplified it to a single checked flag in d8c4449.
If we further extend this to scoped FK-graph checks later, the keyed cache can come back with the needed parent-side detection.
| func isForeignKeyChecksEnabled(session map[string]string) bool { | ||
| for key, value := range session { | ||
| if strings.EqualFold(key, "foreign_key_checks") { | ||
| trimmed := strings.Trim(value, " '\"") | ||
| return variable.TiDBOptOn(trimmed) | ||
| } | ||
| } | ||
| return false | ||
| return config.IsForeignKeyChecksEnabled(session) | ||
| } |
There was a problem hiding this comment.
Why not removing this function
There was a problem hiding this comment.
I kept the wrapper initially to avoid moving the existing syncer-side coverage while moving the parsing helper into config. I’ve moved that coverage to the config package and removed the wrapper in d8c4449.
| case job, ok := <-causalityCh: | ||
| require.True(t, ok) | ||
| return job | ||
| case <-time.After(3 * time.Second): |
There was a problem hiding this comment.
Will this make the test flaky?
There was a problem hiding this comment.
I don't think this adds a flaky timing dependency. The timeout is meant as a fail-fast guard if the causality goroutine does not produce output. The test has already put both jobs into a buffered channel and closed it, so it is expected that the receive is waiting for local goroutine processing rather than an external timing condition. Without the timeout, a bug in the causality path could leave the test hanging.
| parentDMLs, err := syncer.genAndFilterInsertDMLs(tcontext.Background(), &genDMLParam{ | ||
| sourceTable: parentSource, | ||
| targetTable: parentTarget, | ||
| originalData: [][]interface{}{{10}}, |
There was a problem hiding this comment.
nit: replace interface{} with any
| downstreamTableInfo *schema.DownstreamTableInfo, | ||
| ) error { | ||
| if downstreamTableInfo == nil || downstreamTableInfo.TableInfo == nil { | ||
| const foreignKeyRouteTopologyTaskCacheKey = "task" |
There was a problem hiding this comment.
Will we have other task keys in the future?
There was a problem hiding this comment.
No, not in the current design. The map/key came from an earlier version that cached checks per routed target set for the current FK graph, instead of one task-level check. We moved back to task-level checking because scoped checks need more support to catch parent-only DML cases early enough. With the current task-level check, there is only one checked state, so I simplified it to a single checked flag in d8c4449. If we further extend this to scoped FK-graph checks later, we can bring back keyed cache behavior together with the needed parent-side detection.
|
@joechenrh: adding LGTM is restricted to approvers and reviewers in OWNERS files. DetailsIn response to this: 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 kubernetes-sigs/prow repository. |
| }) | ||
| p := parser.New() | ||
|
|
||
| execTrackedDDL(t, syncer.schemaTracker, p, "", "create database db") |
There was a problem hiding this comment.
just let AI try to do it without hurting readibility
|
/retest |
|
/test pull-dm-compatibility-test |
[LGTM Timeline notifier]Timeline:
|
|
/retest |
1 similar comment
|
/retest |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: Benjamin2037, D3Hunter, joechenrh, yudongusa The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
In response to a cherrypick label: new pull request created to branch |
What problem does this PR solve?
Issue Number: ref #12350
What is changed and how it works?
Support DM FK causality when
foreign_key_checks=1andsyncer.worker-count > 1with static one-to-one route rules.This PR:
foreign_key_checks=1A follow-up PR will explicitly reject hot updates that can change route/filter/case-sensitive/FK causality mode.
Check List
Tests
Questions
Will it cause performance regression or break compatibility?
Do you need to update user documentation, design documentation or monitoring documentation?
Release note