peiyu created FLINK-40148:
-----------------------------
Summary: TableIdRouter resolves wrong sink table when source regex
has multi-digit suffix and sink-table has no back-reference
Key: FLINK-40148
URL: https://issues.apache.org/jira/browse/FLINK-40148
Project: Flink
Issue Type: Bug
Components: Flink CDC
Affects Versions: cdc-3.6.0
Environment: flink 1.20.3
flinkcdc 3.6.0
Reporter: peiyu
Fix For: cdc-3.7.0
## Problem
TableIdRouter.resolveReplacement can produce incorrect sink table names when
the source-table
regex contains a capturing group whose value may span multiple characters (e.g.
multi-digit
suffixes like `13`, `14`, `16`) and the sink-table does not use a `$N`
back-reference.
In this case the unmatched characters after the captured prefix are appended to
the configured
sink-table, producing wrong sink names. For example, given:
route:
- source-table: db_6.table_([1-9]|1[0-6])
sink-table: new_db_6.table_merged
`db_6.table_13` is wrongly routed to `new_db_6.table_merged3` (instead of the
configured
`new_db_6.table_merged`), `db_6.table_14` to `new_db_6.table_merged4`,
`db_6.table_16` to
`new_db_6.table_merged6`, etc. Tables 1-9 are not affected because the regex
consumes the
entire single-digit suffix.
## Reproduction
1. Configure a pipeline with the route above.
2. Run the job against a source that has tables `db_6.table_1` …
`db_6.table_16`.
3. Observe in the logs / sink database that tables 10-16 are written to sink
tables named
`new_db_6.table_merged0`, `new_db_6.table_merged1`,
`new_db_6.table_merged3`, … instead of
the single configured sink table.
A unit test that reproduces the bug (without the fix) is added in this ticket;
it fails before
the patch and passes after.
## Root Cause
`TableIdRouter.resolveReplacement` builds a `Matcher` against the source table
id and calls
`Matcher.find()` followed by `Matcher.replaceAll(sinkTable)`:
Matcher matcher = route.f0.matcher(originalTable.toString());
if (matcher.find()) {
return TableId.parse(matcher.replaceAll(route.f1));
}
Two issues combine to produce the bug:
1. `find()` only matches a *prefix* of the source table id. With the regex
`([1-9]|1[0-6])`, the alternation `[1-9]` wins first, matching only `1` from
input `13`.
2. `Matcher.replaceAll` internally calls `reset()` and iterates with `find()`,
so it only
replaces the prefix matched in step 1 and leaves the trailing characters of
the source
table id (`0`, `3`, `4`, `5`, `6`) appended to the sink-table. When the
sink-table has no
`$N` reference, those trailing characters end up in the result.
The same root cause also makes `find()`-based capturing groups return only the
first matched
digit when the sink-table *does* use `$1`, so the bug is not strictly limited
to back-reference
free sinks.
## Proposed Fix
Switch to `Matcher.matches()` to ensure the entire source table id is consumed,
and use
`Matcher.appendReplacement` / `Matcher.appendTail` to perform the `$N`
substitution on the
matched region only — this avoids the trailing-characters-left-over problem of
`replaceAll`.
## Tests
- Add `TableIdRouterTest#testRouteWithoutBackReferenceWithMultiDigitSuffix` to
lock down the
reported scenario (sink-table has no back-reference).
- Add `TableIdRouterTest#testRouteWithBackReferenceAndMultiDigitCapture` to
cover the related
case where sink-table uses `$1` and the captured group must be the full
multi-digit value.
- All existing tests in `TableIdRouterTest`, `TableIdRouterMatchModeTest`, and
`SchemaDerivatorTest` continue to pass (27 tests total).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)