SEPURI-SAI-KRISHNA opened a new pull request, #13092:
URL: https://github.com/apache/seatunnel/pull/13092
Yes, glad to contribute to Apache SeaTunnel.
### Purpose of this pull request
Fixes #13091.
The JDBC source skips every row whose split column is NULL once a table is
read by more than one split, and the job finishes without an error. Each split
reads a range or a hash bucket of the split column, and none of these
conditions matches NULL. The split column can be NULL without any
configuration: for a table without a primary key, the split column is taken
from a unique index, which may hold NULLs.
This PR adds one split that reads the rows whose split column is NULL:
- `ChunkSplitter#generateSplits` adds it after the splitter has built its
splits, when the split column is nullable in the catalog table and a probe
finds a NULL in it: `SELECT 1 FROM <table> WHERE col IS NULL` (or over the
`query`), at most one row, on the same `where_condition` scope as the existing
MIN/MAX query. Nothing is added when the column is `NOT NULL` (a primary key,
for example), when it has no NULL values, or when one of the splits already
reads the whole table, so those jobs get the same splits as before.
- The split is a `JdbcSourceSplit` with the new `nullSplitKey` flag. Its
statement is built like a range split: `SELECT * FROM <table> WHERE col IS
NULL`, or `SELECT * FROM (<query>) st_jdbc_splitter WHERE col IS NULL`, with
`where_condition` applied to the inner query once, so a narrow `query` still
gets the columns the condition needs.
- Hash splits of a nullable column also get `AND col IS NOT NULL`. On MySQL
`CRC32(NULL)` is NULL, but DuckDB's `hash(NULL)` is not, so without this the
NULL rows would be read twice there.
This is the JDBC source counterpart of #12597, which fixed the same gap for
the CDC sources by not splitting on a nullable column. I did not copy that
approach here: the CDC snapshot needs ordered key ranges for its backfill, and
the JDBC source does not, while a JDBC user who sets `partition_column` on a
large table has asked for parallel reads, which a fallback to one split would
silently remove.
Compatibility:
- `JdbcSourceSplit` keeps its `serialVersionUID` and its seven-argument
constructor. A split serialized by the `dev` class deserializes with
`nullSplitKey = false` and its other fields unchanged (checked by writing a
split with the `dev` connector jar and reading it with this class). A restored
job keeps its pending splits, so the fix applies when the table is split again.
- No option is added or changed.
Overlap: #12292 restructures `ChunkSplitter#generateSplits`. Whichever lands
second needs to keep the `addNullSplitKeySplit` call. #11746 (composite keys)
handles NULL key components in its own composite path and leaves single-column
splitting unchanged, and #12991 stops NULL sample values from becoming chunk
boundaries but does not read the NULL rows, so neither overlaps in behavior.
### Does this PR introduce _any_ user-facing change?
Yes. A job whose split column has NULL values now reads the rows it used to
skip. Jobs whose split column is `NOT NULL` or has no NULL values get the same
splits as before; for a nullable split column, split planning runs the one
probe query. The `Jdbc` source docs (EN and ZH) explain the extra split, and
`incompatible-changes.md` (EN and ZH) records the change.
### How was this patch tested?
**Unit tests** in `FixedChunkSplitterTest`, on an embedded DuckDB table
where every tenth row has a NULL split column:
- `testNullSplitKeyRowsAreReadExactlyOnce` reads every split of a range
split on a numeric column and of a hash split on a string column, and checks
that all 100 ids are read exactly once and that one split reads the NULL rows.
Without the extra split it fails with 90 of 100 rows; without the `IS NOT NULL`
guard it fails with 110, because DuckDB hashes NULL to a value.
- `testNotNullSplitKeyAddsNoNullSplitKeySplit` checks that a `NOT NULL`
split column gets no extra split.
- `testNullableSplitKeyWithoutNullValuesAddsNoNullSplitKeySplit` checks that
a nullable split column without NULL values gets no extra split either. If the
probe always reported a NULL, it would fail with 5 splits instead of 4.
**E2E.** `JdbcMysqlSplitIT#testNullSplitKeyRowsAreReadOnce` creates a MySQL
table without a primary key and with a nullable unique key, then reads it with
the dynamic splitter and no split settings (the unique key becomes the split
column) and with a fixed hash split on a nullable string column. Each read must
return all 100 ids exactly once. The existing tests of the class keep their
exact split counts, because the nullable columns of their table hold no NULL
values. I ran the whole `JdbcMysqlSplitIT` (all 6 tests) against a local
`mysql:8.0.43`: all pass with this change. On `dev` (without the
`isNullSplitKey` assertion, which needs this change) the new test fails with 90
of 100 ids.
**Local jobs** on the Zeta engine (local mode), 1000 rows, 100 or 142 with a
NULL split column, `dev` against this change:
| Source settings | `dev` | This PR |
| --- | --- | --- |
| MySQL, `table_path`, no split settings (nullable unique key) | 900 | 1000 |
| MySQL, `table_path`, `partition_column` (int) | 900 | 1000 |
| MySQL, `query`, `partition_column` (string, hash split) | 858 | 1000 |
| MySQL, `query`, `partition_column` (string, `charset_based`) | 858 | 1000 |
| MySQL, `table_path`, `partition_column` (string, `split.string-strategy =
"hash"`) | 858 | 1000 |
| MySQL, narrow `query` + `where_condition` on a column it does not select |
772 | 858 (all matching rows) |
| MySQL, primary key table | 1000 | 1000, no extra split |
| DuckDB, fixed int, dynamic int, dynamic sampled string | 900 / 900 / 858 |
1000 |
| DuckDB, fixed hash split (string) | 1000 | 1000, no duplicates |
No duplicate ids in any run with this change.
Checks, JDK 17:
- `./mvnw test -pl seatunnel-connectors-v2/connector-jdbc`: 1200 tests; the
only errors are the 4 Oracle and Kingbase container tests, which cannot start a
container on this machine.
- `./mvnw spotless:check` on `connector-jdbc` and
`connector-jdbc-e2e-part-7`: clean.
### Check list
* [X] If any new Jar binary package adding in your PR, please add License
Notice according
[New License
Guide](https://github.com/apache/seatunnel/blob/dev/docs/en/developer/new-license.md):
none added.
* [X] If necessary, please update the documentation to describe the new
feature. https://github.com/apache/seatunnel/tree/dev/docs: `Jdbc` source docs,
EN and ZH.
* [X] If necessary, please update `incompatible-changes.md` to describe the
incompatibility caused by this PR: added, EN and ZH.
* [X] If you are contributing the connector code, please check that the
following files are updated: not applicable, no new connector.
--
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]