Copilot commented on code in PR #12463:
URL: https://github.com/apache/seatunnel/pull/12463#discussion_r4090951725


##########
seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/splitter/AbstractJdbcSourceChunkSplitter.java:
##########
@@ -173,6 +173,46 @@ private List<ChunkRange> splitTableIntoChunks(
                             && doubleCompare(distributionFactor, 
distributionFactorUpper) <= 0;
 
             if (dataIsEvenlyDistributed) {
+                // When distributionFactor > 1.0, primary key IDs are not 
consecutive
+                // (there are gaps in the ID space). Arithmetic-based 
evenly-sized chunk
+                // splitting assumes gaps are uniformly distributed, which 
fails when
+                // gaps are clustered (e.g., large ranges of deleted IDs). In 
this case,
+                // prefer sampling-based splitting which uses actual data 
points for
+                // chunk boundaries, producing balanced splits regardless of 
gap
+                // distribution.
+                int shardCount = (int) (approximateRowCnt / chunkSize);
+                if (doubleCompare(distributionFactor, 1.0d) > 0
+                        && sampleShardingAllow
+                        && shardCount > 0) {
+                    int inverseSamplingRate = 
sourceConfig.getInverseSamplingRate();
+                    if (inverseSamplingRate > chunkSize) {
+                        log.warn(
+                                "The inverseSamplingRate is {}, which is 
greater than chunkSize {}, so we set inverseSamplingRate to chunkSize",
+                                inverseSamplingRate,
+                                chunkSize);
+                        inverseSamplingRate = chunkSize;
+                    }
+                    log.info(
+                            "Distribution factor {} > 1.0 for table {}, using 
sampling-based "
+                                    + "splitting for better chunk balance with 
non-consecutive IDs, "
+                                    + "the sampling rate is {}",
+                            distributionFactor,
+                            tableId,
+                            inverseSamplingRate);
+                    Object[] sample =
+                            sampleDataFromColumn(jdbc, tableId, splitColumn, 
inverseSamplingRate);
+                    log.info(
+                            "Sample data from table {} end, the sample size is 
{}",
+                            tableId,
+                            sample.length);
+                    if (sample.length > 0) {
+                        return efficientShardingThroughSampling(
+                                tableId, sample, approximateRowCnt, 
shardCount);

Review Comment:
   Checking only `sample.length > 0` is not enough for this fix. The MySQL 
sampler selects rows where `(id - MIN(id)) MOD inverseSamplingRate = 0`; with 
sparse keys such as 0, 1,000,001, 2,000,002 and the default rate 1000, the 
sample can contain only the minimum even though the table has many rows. 
`efficientShardingThroughSampling` then creates only two ranges, leaving one 
overloaded chunk and the original checkpoint-timeout failure intact. Please 
make sampling representative for sparse keys (or detect an insufficient sample 
and use a splitting strategy that does not under-split).



##########
seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/splitter/AbstractJdbcSourceChunkSplitter.java:
##########
@@ -173,6 +173,46 @@ private List<ChunkRange> splitTableIntoChunks(
                             && doubleCompare(distributionFactor, 
distributionFactorUpper) <= 0;
 
             if (dataIsEvenlyDistributed) {
+                // When distributionFactor > 1.0, primary key IDs are not 
consecutive
+                // (there are gaps in the ID space). Arithmetic-based 
evenly-sized chunk
+                // splitting assumes gaps are uniformly distributed, which 
fails when
+                // gaps are clustered (e.g., large ranges of deleted IDs). In 
this case,
+                // prefer sampling-based splitting which uses actual data 
points for
+                // chunk boundaries, producing balanced splits regardless of 
gap
+                // distribution.
+                int shardCount = (int) (approximateRowCnt / chunkSize);

Review Comment:
   `shardCount` is floored here, so a table with just over one `chunkSize` of 
rows (for example, 1,001 rows with a split size of 1,000) gets `shardCount == 
1`. `efficientShardingThroughSampling` then returns one unbounded range, 
defeating the configured split size and leaving the entire table in a single 
snapshot task. Use a ceiling when deriving the shard count (while preserving 
the existing overflow/zero handling).



##########
seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/splitter/AbstractJdbcSourceChunkSplitter.java:
##########
@@ -173,6 +173,46 @@ private List<ChunkRange> splitTableIntoChunks(
                             && doubleCompare(distributionFactor, 
distributionFactorUpper) <= 0;
 
             if (dataIsEvenlyDistributed) {
+                // When distributionFactor > 1.0, primary key IDs are not 
consecutive
+                // (there are gaps in the ID space). Arithmetic-based 
evenly-sized chunk
+                // splitting assumes gaps are uniformly distributed, which 
fails when
+                // gaps are clustered (e.g., large ranges of deleted IDs). In 
this case,
+                // prefer sampling-based splitting which uses actual data 
points for
+                // chunk boundaries, producing balanced splits regardless of 
gap
+                // distribution.
+                int shardCount = (int) (approximateRowCnt / chunkSize);
+                if (doubleCompare(distributionFactor, 1.0d) > 0
+                        && sampleShardingAllow
+                        && shardCount > 0) {

Review Comment:
   This condition bypasses the existing `sample-sharding.threshold` for every 
evenly distributed table with a factor above 1.0. In the MySQL implementation, 
`sampleDataFromColumn` reads every row with `SELECT <column> FROM <table>` 
before retaining 1/N values 
(`connector-cdc-mysql/.../MySqlUtils.java:167-217`), so large tables with only 
modest gaps now incur an additional full-table scan during startup. Please 
retain a bounded/opt-in trigger or otherwise document and account for this 
extra O(row-count) planning cost.



##########
seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/splitter/AbstractJdbcSourceChunkSplitterTest.java:
##########
@@ -155,6 +155,61 @@ public void testEfficientShardingThroughSampling() throws 
NoSuchMethodException
                         ChunkRange.of(6, null)));
     }
 
+    /**
+     * When IDs are non-consecutive (distributionFactor > 1.0) and sampling is 
enabled, the
+     * splitEvenlySizedChunks path should NOT be used. Instead, sampling-based 
splitting should be
+     * preferred for better chunk balance.
+     *
+     * <p>This test verifies that for a table with IDs spanning a wide range 
but few actual rows
+     * (distributionFactor >> 1.0), the algorithm falls back to arithmetic 
stepping only when
+     * sampling is disabled.
+     */
+    @Test
+    public void testSplitEvenlySizedChunksWithNonConsecutiveIds() {

Review Comment:
   This test never exercises the new decision in `splitTableIntoChunks`: it 
calls `splitEvenlySizedChunks` and `efficientShardingThroughSampling` directly 
on a splitter whose source config is null. It would pass even if the added 
`distributionFactor > 1.0` condition, sampling callback, or fallback were 
removed, so the PR's core regression is unprotected. Please configure a test 
double and invoke the actual split-generation path, asserting the sampling 
callback is used for sparse IDs and the arithmetic fallback is used when 
sampling is disabled or empty.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to