Skip to content

[Improve][Connector-V2] Move Debezium ownership from connector-cdc-base to each CDC connector - #11639

Open
DanielLeens wants to merge 12 commits into
apache:devfrom
DanielLeens:daniel-debezium-per-connector-versions
Open

DanielLeens wants to merge 12 commits into
apache:devfrom
DanielLeens:daniel-debezium-per-connector-versions

Conversation

@DanielLeens

@DanielLeens DanielLeens commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Purpose of this pull request

Follow-up to #10799, which added the DebeziumAdapter SPI and per-connector debezium.version properties as a staged foundation for per-connector Debezium version management.

Those properties do not yet control the Debezium a connector runs on. connector-cdc-base declares debezium-api and debezium-embedded at compile scope, so debezium-core reaches every CDC connector transitively at the version resolved in connector-cdc-base's own POM. Setting <debezium.version> in connector-cdc-mysql changes only debezium-connector-mysql; the Debezium core stays pinned by the shared module.

That is not just a POM detail. AbstractPluginDiscovery#filterPluginJar special-cases CDC and forces connector-cdc-base into the class loader of every CDC plugin, requiring exactly two jars:

if (pluginName.contains("cdc")) {
    return pathname.getName().endsWith(".jar")
            && (StringUtils.startsWithIgnoreCase(pathname.getName(), pluginJarPrefix)
                    || StringUtils.startsWithIgnoreCase(pathname.getName(), "connector-cdc-base"));
}

Checking the published jars confirms where Debezium actually lives today:

jar io.debezium core/embedded classes debezium-connector-* classes
connector-cdc-base 294 0
connector-cdc-mysql 0 158

So the shared connector-cdc-base jar is the single project-wide Debezium version, and no connector can override it. This PR moves that ownership to the connectors.

Does this PR introduce any user-facing change?

No change to CDC job configuration, runtime behavior or checkpoint compatibility. Every in-tree connector still resolves Debezium 1.9.8.Final, so the set of classes on a CDC job's class loader is unchanged — only relocated from the shared jar into the connector jar.

Two things do change for people building against SeaTunnel, both documented in docs/{en,zh}/introduction/concepts/incompatible-changes.md:

  • connectors/connector-cdc-base-*.jar no longer contains the stock io.debezium runtime classes; it still ships the five patched io.debezium.* classes listed under "Version compatibility" below.
  • Third-party CDC connectors that relied on connector-cdc-base to supply Debezium transitively must now declare debezium-api and debezium-embedded themselves.

What changed

Dependency ownership

  • connector-cdc-base declares debezium-api, debezium-embedded and zstd-jni as provided. It still compiles against Debezium but no longer imposes a version on its consumers, and its shaded jar is Debezium-free.
  • The Debezium dependency management moves up to the connector-cdc parent. Inherited dependency management is interpolated against the effective properties of the module being built, so each connector resolves Debezium at its own debezium.version while sharing one copy of the exclusion rules (kafka-log4j-appender, glassfish jersey, the Apple-M1 zstd-jni override).
  • Every Debezium-based connector declares debezium-api, debezium-embedded and zstd-jni explicitly. connector-cdc-opengauss and connector-cdc-vitess gain the debezium.version property they were missing in [Feature][Connector-V2] Foundation for per-connector Debezium version management (staged) #10799.
  • connector-cdc-tidb is intentionally untouched — it uses no Debezium classes. Verified at the bytecode level: the only connector-cdc-base classes it touches (SourceOptions, StartupMode, TemporalConversions) contain zero io/debezium or org/apache/kafka constant-pool references, because CompatibleDebeziumJsonDeserializationSchema.IDENTIFIER is a compile-time constant that javac inlines.

The SPI gets real providers

Each connector now registers a DebeziumAdapter via META-INF/services, so the extension point added in #10799 is populated and the version a connector ships is introspectable at runtime.

connector-cdc-opengauss deliberately registers none. It runs Debezium's PostgreSQL connector and shades connector-cdc-postgres into its own jar, and the shade plugin is configured with ServicesResourceTransformer, which merges META-INF/services entries. A second provider for io.debezium.connector.postgresql.PostgresConnector would therefore land in the same jar and make every openGauss CDC job fail the exactly-one-match rule. OpengaussDebeziumAdapterTest pins that so a future contributor cannot reintroduce it silently.

How was this patch tested?

Unit tests per connector assert that the adapter resolves uniquely for its connector class, that it claims no other connector class, and — the drift guard — that its declared version equals the debezium-core version actually resolved onto the module classpath (read from META-INF/maven/io.debezium/debezium-core/pom.properties). A POM bump that forgets the adapter, or vice versa, fails the build.

The ownership claim itself was verified with dependency:tree. Before, debezium-core was fixed for everyone. After, with only connector-cdc-mysql's property temporarily set to 1.9.7.Final:

connector-cdc-base
  io.debezium:debezium-api:jar:1.9.8.Final:provided
  io.debezium:debezium-embedded:jar:1.9.8.Final:provided
    io.debezium:debezium-core:jar:1.9.8.Final:provided

connector-cdc-mysql
  io.debezium:debezium-api:jar:1.9.7.Final:compile
  io.debezium:debezium-embedded:jar:1.9.7.Final:compile
    io.debezium:debezium-core:jar:1.9.7.Final:compile
  io.debezium:debezium-connector-mysql:jar:1.9.7.Final:compile

connector-cdc-postgres
  io.debezium:debezium-api:jar:1.9.8.Final:compile
  io.debezium:debezium-embedded:jar:1.9.8.Final:compile
    io.debezium:debezium-core:jar:1.9.8.Final:compile

Two CDC connectors resolving genuinely different Debezium cores — not possible before this change. That probe was reverted; everything in this PR is on 1.9.8.Final.

Version compatibility

Three things have to keep agreeing once Debezium ownership moves, and each is now enforced rather than assumed.

1. connector-cdc-base ships patched Debezium classes, and they must stay the only copies.

connector-cdc-base recompiles five Debezium classes under src/main/java/io/debezium:

class also in debezium-core
io.debezium.connector.base.ChangeEventQueue (+ $1, $Builder) yes
io.debezium.relational.TableId yes
io.debezium.relational.HistorizedRelationalDatabaseConnectorConfig yes
io.debezium.heartbeat.HeartbeatFactory yes
io.debezium.heartbeat.DefaultHeartbeatConnectionProvider no

Comparing the shipped jars confirms the patched copies currently win — all four overlapping classes differ byte-for-byte from debezium-core, because the shade plugin resolved the overlap at build time inside one jar.

Moving Debezium into the connector jars breaks that: the stock copies would arrive in the connector jar while the patched ones stay in connector-cdc-base, with both jars on the same class loader. AbstractPluginDiscovery builds that class path from File#listFiles, so which copy wins would depend on filesystem ordering, and the patches could silently stop applying on some machines.

The CDC shade configuration now excludes those classes from debezium-core, keeping connector-cdc-base's patched copies the single definition exactly as before. connector-cdc-opengauss declares its own filter list at execution level, which takes precedence over the inherited one, so it restates the filter. PatchedDebeziumClassesTest pins the patched set against the exclusion list, so a newly patched class cannot be added without updating both.

2. Modules that share a Debezium version must not drift apart.

The per-connector tests now assert that every Debezium artifact on a connector's class path resolves to the single version its adapter declares, rather than spot-checking debezium-core. This matters most for connector-cdc-opengauss: it declares its own debezium.version for debezium-embedded but reaches debezium-connector-postgres transitively through connector-cdc-postgres, and it recompiles Debezium's PostgreSQL connection internals. Bumping one module and not the other now fails the build instead of shipping two Debezium versions on one class path.

3. How far a connector may actually diverge today.

Packaging ownership has moved, but connector-cdc-base is still compiled against a single baseline and is loaded into every CDC connector's class loader. It uses roughly 85 Debezium types across io.debezium.relational, io.debezium.pipeline, io.debezium.schema, io.debezium.config and io.debezium.jdbc, plus the five patched classes above.

So divergence is safe only within a Debezium line that keeps those APIs binary compatible — a connector can move within 1.9.x today, not to a release that reshapes relational/pipeline. Making major-version divergence real requires connector-cdc-base's Debezium usage to move behind the DebeziumAdapter SPI, which is exactly what the SPI exists for and is not attempted here. This is stated in incompatible-changes.md so the capability is not over-claimed.

Trade-off for reviewers

This increases the size of the binary distribution. The Debezium runtime is no longer shared: connector-cdc-base drops from ~24 MB to roughly 1 MB (only ~0.37 MB of it was ever SeaTunnel code — the rest is Kafka Connect ~4.9 MB, Guava, Jetty, snappy, jackson and the zstd-jni native binaries), and each of the 7 Debezium-based connector jars gains that payload instead. Net growth is on the order of +140 MB.

That cost is inherent to the goal: if a connector can pin its own Debezium, it has to carry it. Sharing one copy is exactly what makes overriding impossible today.

If the size is not acceptable, the alternative worth discussing is shipping versioned shared Debezium runtime jars (e.g. connector-cdc-debezium-1.9.8) and having AbstractPluginDiscovery load the one each connector declares. That reclaims the sharing for connectors on the same version while still allowing divergence, but it rewires the plugin-discovery path — a runtime change deliberately kept out of this staged PR. Raising it here as a draft so that direction can be settled before anything in the runtime path moves.

Concrete follow-up milestone: MySQL 9.x connector on Debezium 3.x

The first real consumer of per-connector versions is a MySQL 9 CDC connector. MySQL 8.4/9.0 removed the legacy replication commands Debezium 1.9 depends on (SHOW MASTER STATUS is gone in favor of SHOW BINARY LOG STATUS), so current MySQL-CDC cannot follow MySQL past 8.x, and Debezium only added MySQL 9 support in its 2.7/3.x line. Landing that connector has two hard prerequisites, which is why it is not part of this PR:

  1. Java baseline. Debezium 3.x ships Java-17 bytecode; Debezium 2.7 needs Java 11. The reactor compiles with JDK 8 (java.version=1.8, CI Install runs Temurin 8), so a module depending on Debezium 3.x cannot compile anywhere in today's build. [Improve][Core] Raise minimum Java runtime to 11 and move CI to JDK 11/17 #11545 (JDK 17 CI) is the gating PR.
  2. cdc-base neutrality. connector-cdc-base compiles against ~85 Debezium 1.9 types and patches five Debezium classes; Debezium 2.0 renamed the schema-history API this framework implements (DatabaseHistory → SchemaHistory), among other reshapes. Those touchpoints must move behind the DebeziumAdapter SPI before a connector can bring a 3.x core onto a class path shared with cdc-base.

One additional trap for that PR, noted here so it is not rediscovered: AbstractPluginDiscovery resolves CDC plugin jars by filename prefix, and a jar named connector-cdc-mysql* anything would make the new plugin unresolvable (its jar matches two plugin identifiers and is discarded, leaving only cdc-base and failing the exactly-two-jars rule). The new module needs a non-prefix-colliding artifact id or a discovery fix.

Staged order: this PR (packaging ownership) → #11545 (JDK 17) → SPI-ify cdc-base's Debezium touchpoints → MySQL9-CDC connector on Debezium 3.x with MySQL 9 E2E.

Check list

  • Code changed are covered with tests
  • If any new Jar binary package adding in your PR, please add License Notice according New License Guide — no new dependencies are introduced; existing Debezium artifacts are re-scoped only
  • If necessary, please update the documentation to describe the new feature — incompatible-changes.md updated in en and zh

@DanielLeens

Copy link
Copy Markdown
Contributor Author

What Problem This PR Solves

  • User pain point
    Today every Debezium-based CDC connector inherits Debezium from connector-cdc-base, and because that base jar is loaded into every CDC connector classloader, no connector can really own or override the Debezium runtime it ships with.
  • Fix approach
    This PR makes connector-cdc-base compile against Debezium with provided scope, moves the runtime Debezium dependencies into each connector module, adds a lightweight adapter SPI plus module-local adapter tests, and documents the packaging change in both docs/en and docs/zh.
  • One-sentence summary
    The goal is to move Debezium runtime ownership from the shared CDC base jar to the individual CDC connector modules.

I. Code Change Review

1.1 Core Logic Analysis

Exact change description

The core packaging changes are in:

  • seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:50-103
  • seatunnel-connectors-v2/connector-cdc/pom.xml:44-99
  • per-connector poms such as:
    • connector-cdc-mysql/pom.xml
    • connector-cdc-postgres/pom.xml
    • connector-cdc-opengauss/pom.xml:32-85
    • connector-cdc-oracle/pom.xml
    • connector-cdc-sqlserver/pom.xml
    • connector-cdc-mongodb/pom.xml
    • connector-cdc-vitess/pom.xml
  • SPI and guardrail tests:
    • connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/debezium/DebeziumAdapter.java:22-44
    • connector-local *DebeziumAdapter.java
    • connector-local *DebeziumAdapterTest.java

Before / after snippets

Before, connector-cdc-base shipped the shared Debezium runtime:

<dependency>
    <groupId>io.debezium</groupId>
    <artifactId>debezium-api</artifactId>
    <version>${debezium.version}</version>
</dependency>
<dependency>
    <groupId>io.debezium</groupId>
    <artifactId>debezium-embedded</artifactId>
    <version>${debezium.version}</version>
</dependency>

After, the base jar only compiles against Debezium:

<dependency>
    <groupId>io.debezium</groupId>
    <artifactId>debezium-api</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>io.debezium</groupId>
    <artifactId>debezium-embedded</artifactId>
    <scope>provided</scope>
</dependency>

and each connector module now declares the runtime Debezium artifacts it packages itself.

Key findings

  • The packaging change matches the real plugin classloader problem described in connector-cdc-base/pom.xml:52-58.
  • All Debezium-based in-tree connectors now explicitly declare their own debezium.version and runtime Debezium dependencies.
  • connector-cdc-opengauss deliberately reuses the PostgreSQL adapter by depending on connector-cdc-postgres, and the added test documents that exactly-one-adapter rule.
  • The new DebeziumAdapter SPI is currently a guardrail layer and forward-compatibility seam, not a runtime behavior change by itself.

Deep logic analysis

The relevant packaging/runtime path is:

Plugin discovery
  -> connector-cdc-base is loaded into every CDC connector classloader
      -> if base ships Debezium classes, that shared copy wins for everybody

This PR
  -> connector-cdc-base/pom.xml:50-74
      -> Debezium dependencies become provided only
      -> base jar no longer ships io.debezium classes

Per connector ownership
  -> connector-cdc parent pom manages exclusions and baseline version
  -> each Debezium-based connector pom declares:
       - its own debezium.version
       - debezium-api
       - debezium-embedded
       - zstd-jni
       - connector-specific debezium-connector-*

Guardrails
  -> DebeziumAdapter SPI + META-INF/services entry per connector
  -> connector-local adapter tests
      -> verify one adapter per connector.class
      -> verify packaged Debezium version matches module declaration

For openGauss specifically:

connector-cdc-opengauss
  -> declares its own debezium.version [opengauss pom:32-40]
  -> packages debezium-api / debezium-embedded / zstd-jni [56-67]
  -> depends on connector-cdc-postgres [81-85]
      -> inherits the PostgreSQL adapter registration
  -> OpengaussDebeziumAdapterTest
      -> verifies exactly one Postgres adapter is visible
      -> prevents accidental double-registration later

Local verification results used for this review:

  • gh pr view 11639 --json ...: completed, used to confirm the latest head (35882cf6313a2b413dc7363f3934a653817b37c9) and current checks.
  • gh api repos/apache/seatunnel/compare/dev...35882cf6313a2b413dc7363f3934a653817b37c9: completed, current compare status is ahead, ahead_by=1, behind_by=0.
  • gh api repos/apache/seatunnel/pulls/11639: completed, mergeable=true, mergeable_state=blocked, rebaseable=false.
  • gh api repos/apache/seatunnel/pulls/11639/reviews: completed, no prior reviews were present at the time of this pass.
  • git diff upstream/dev...seatunnel-review-11639 -- <changed files>: completed for the full changed set.
  • Build / unit tests / E2E execution: not run locally. Per this batch's SeaTunnel review rule, I relied on source analysis and current GitHub check state.

1.2 Compatibility Impact

Judgment: Partially incompatible

Impacts:

  • API: no user config contract change for in-tree CDC connectors.
  • Configuration: no CDC job config change for in-tree connectors.
  • Default behavior: no intended runtime behavior change for in-tree connectors because they still resolve Debezium 1.9.8.Final.
  • Protocol / serialization / checkpoint: unchanged for in-tree connectors.
  • Historical behavior: third-party CDC connectors that relied on connector-cdc-base to bring Debezium transitively now need to declare Debezium dependencies themselves.

Migration note:

  • Third-party connectors built against connector-cdc-base should explicitly declare debezium-api and debezium-embedded instead of relying on the base jar to ship them.

1.3 Performance / Side-Effect Analysis

  • CPU / memory / GC: unchanged at runtime for in-tree connectors.
  • Network / concurrency / retry / idempotency / resource release: unchanged.
  • Packaging side effect: the Debezium runtime is now duplicated into each Debezium-based connector jar instead of being shared through connector-cdc-base, so binary distribution size increases. The PR documents that explicitly.

1.4 Error Handling and Logging

I did not find a new source-side blocker in the current head.

II. Code Quality Assessment

2.1 Coding Standards

The comments are strong throughout the packaging changes, especially around the base-jar classloader constraint and the openGauss/PostgreSQL coupling.

2.2 Test Coverage and Test Stability

The new adapter tests cover the most important guardrails:

  • discoverability via META-INF/services
  • one-adapter-per-connector-class uniqueness
  • declared debezium.version matches the Debezium actually resolved on the module classpath
  • openGauss intentionally reuses the PostgreSQL adapter without introducing a second provider

Test stability conclusion: Stable

  • The added tests are deterministic and do not rely on time, fixed ports, external services, or concurrent scheduling.

2.3 Documentation Updates

The incompatible-change note was updated in both:

  • docs/en/introduction/concepts/incompatible-changes.md
  • docs/zh/introduction/concepts/incompatible-changes.md

That matches the user-visible packaging change for third-party connector builders.

III. Architecture Rationality

3.1 Elegance of the Solution

This is a long-term packaging fix rather than a workaround. It addresses the shared-classloader root cause directly.

3.2 Maintainability

The ownership model is clearer after this change because each connector now declares the Debezium runtime it ships, and the adapter tests give a concrete drift alarm.

3.3 Extensibility

The new SPI gives the project a clean seam for future runtime wiring or version-specific logic if that becomes necessary later.

3.4 Historical Version Compatibility

For in-tree connectors, compatibility remains effectively unchanged at runtime. The breaking surface is limited to external connectors that previously relied on the base jar's transitive Debezium packaging.

IV. Issue Summary

No formal issues found in the current head.

V. Merge Recommendation

Conclusion: Can be merged

  1. Blocking items (must fix)
  • No new source-side blocker from Daniel's side in the current head.
  1. Suggested improvements (non-blocking)
  • None from the current source review.

Overall assessment:
This is a well-reasoned packaging cleanup with good guardrail tests and the right compatibility note for third-party CDC connector authors.

Remaining gates:

  • The current-head CI is still pending on GitHub.
  • Since this PR changes packaging for multiple CDC connectors, the final merge should still rely on an independent write-capable maintainer gate once CI is complete.

@SEZ9 SEZ9 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I re-reviewed the latest head afb0fada4b16 from scratch.

What this PR fixes

  • User pain: The debezium.version property added in #10799 is cosmetic — connector-cdc-base ships 294 io.debezium core/embedded classes at compile scope and, because AbstractPluginDiscovery#filterPluginJar force-loads that jar into every CDC plugin's class loader, it pins a single project-wide Debezium version no connector can override.
  • Fix approach: Flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base/pom.xml, make each connector (e.g. connector-cdc-mongodb) declare the Debezium runtime it ships plus a DebeziumAdapter pinning its version, and add PatchedDebeziumClassesTest to keep the hand-maintained shade exclusion list (the five patched io.debezium classes) in sync so duplicates can't silently reappear.
  • One-line summary: A packaging-only refactor that makes each connector's jar the sole source of Debezium classes while connector-cdc-base stays a Debezium-free shared jar, with docs (EN/ZH incompatible-changes) honestly capturing the third-party breaking change and the remaining SPI limitation — the parts I want to verify are the shade filters in connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml referenced by the new test, which are not visible in this diff excerpt.

Review conclusion

Conclusion: LGTM, can merge

Nice work — thanks again for the contribution. Happy to discuss any of the above.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base ships the Debezium runtime (debezium-api, debezium-embedded, zstd-jni) at compile scope, and because AbstractPluginDiscovery#filterPluginJar/selectPluginJar force-load connector-cdc-base-*.jar into the class loader of every CDC connector plugin whose name contains "cdc", that single shared jar pins one project-wide Debezium version. No individual connector (MySQL, Postgres, Oracle, SQL Server, MongoDB, openGauss, Vitess) can move independently.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided scope in connector-cdc-base/pom.xml, centralize the version+exclusion management in the connector-cdc parent's dependencyManagement (interpolated per-module against each connector's own debezium.version property), have every Debezium-based connector declare its own runtime Debezium dependencies, add a lightweight DebeziumAdapter SPI + per-connector adapter and guardrail tests, and document the packaging change in docs/en/docs/zh.
  • One-sentence summary: this is a packaging-only refactor that moves Debezium runtime ownership from the shared connector-cdc-base jar into each CDC connector's own jar, with no functional/runtime behavior change for in-tree connectors today.

1. Code Change Review

1.1 Core Logic Analysis

Coverage scope: I fully traced the complete diff (31 files) plus the surrounding, unmodified source needed to validate its premises: AbstractPluginDiscovery (plugin discovery/classloader merge), DebeziumAdapter/DebeziumAdapterFactory (pre-existing SPI from #10799, unmodified here), the pre-PR and post-PR pom.xml of all 8 connector-cdc-* modules (including the untouched connector-cdc-tidb), seatunnel-connectors-v2/pom.xml, seatunnel-format-compatible-debezium-json/pom.xml, seatunnel-dist/pom.xml and assembly-seatunnel-bin.xml, and every connector-cdc-*-e2e/pom.xml. This is full-depth coverage, not a sample.

Change description: connector-cdc-base/pom.xml drops its dependencyManagement pins for debezium-api/debezium-embedded/zstd-jni and adds <scope>provided</scope> to its own <dependencies> entries for them. connector-cdc/pom.xml (the parent) gains the same dependencyManagement entries (version + exclusions, unchanged content, just relocated) and a maven-shade-plugin <configuration><filters combine.children="append"> block that excludes the five patched io.debezium.* classes from debezium-core. Seven Debezium-based connectors (mysql, postgres, oracle, sqlserver, mongodb, opengauss, vitess) each get a direct (compile-scope) debezium-api/debezium-embedded/zstd-jni dependency, a <connector>DebeziumAdapter implementing the pre-existing DebeziumAdapter SPI, a META-INF/services registration, and an adapter test. connector-cdc-tidb (Debezium-free, uses tikv-client-java) is correctly left untouched.

Runtime path verification — I checked whether the normal execution path is affected:

  • DebeziumAdapterFactory.getAdapter(...) has zero production call sites in this diff or in dev — it is only invoked from the new test classes. The DebeziumAdapter interface Javadoc itself says "concrete wiring... will be added in follow-up PRs." So the entire adapter SPI added by this PR is currently inert scaffolding; it changes no job-execution behavior.
  • The only runtime-observable change is classloading/packaging: previously connector-cdc-base.jar carried the Debezium classes into every CDC connector's merged classloader; now the connector's own jar does. Since every in-tree connector still resolves debezium.version=1.9.8.Final (unchanged), the set of classes visible to a running CDC job is identical before and after this PR. This matches the PR's own compatibility claim, and I independently confirmed it rather than taking it on faith.
  • connector-cdc-tidb: TiDBSource implements SeaTunnelSource directly and only imports plain Option/enum holders from connector-cdc-base with no Debezium type in their signature. TiDB is loaded with connector-cdc-base.jar merged into its classloader (per AbstractPluginDiscovery's "cdc"-name matching, which is unconditional and not Debezium-aware), but never touches a Debezium-referencing class path, so it is unaffected by connector-cdc-base no longer shipping Debezium. This was worth checking explicitly — a per-connector Debezium move could plausibly strand a non-Debezium CDC connector that shares the same force-merged base jar — but it does not.

Shade-filter chain (the mechanism worth verifying, not just accepting from a diff excerpt): I traced the full Maven plugin inheritance for the six connectors that don't locally override shade config (mysql, postgres, oracle, sqlserver, mongodb, vitess) — none of them declare their own maven-shade-plugin <execution>, so they inherit the shade <execution> binding from higher ancestors and the <filters combine.children="append"> block newly added in connector-cdc/pom.xml, which appends (does not replace) the debezium-core exclusion filter to whatever base filters exist upstream. connector-cdc-opengauss is the one exception: it already had its own local shade <execution> (for relocating org.postgresql) predating this PR, and a child execution's own <filters> (without combine.children="append" on its side) overrides rather than merges with the inherited one — which is exactly why the PR manually restates the same debezium-core exclusion filter inside connector-cdc-opengauss/pom.xml's own execution, with a comment explaining why. I verified this restatement is present and correct.

openGauss/Postgres coupling: connector-cdc-opengauss has a pre-existing (unchanged by this PR) plain compile-scope dependency on connector-cdc-postgres, and deliberately registers no DebeziumAdapter of its own, relying on the inherited PostgresDebeziumAdapter + META-INF/services merge. OpengaussDebeziumAdapterTest verifies exactly one adapter resolves for PostgresConnector. This matches the documented intent and is correctly guarded.

1.2 Compatibility Impact

Judgment: Partially incompatible

  • API / config / checkpoint / serialization: unchanged for all in-tree connectors — no Option renamed or removed, no default changed, no protocol/serialization format touched.
  • Runtime behavior: unchanged for in-tree connectors, because every connector still resolves the same debezium.version=1.9.8.Final baseline before and after this PR (verified above).
  • Historical behavior: unaffected — the DebeziumAdapter SPI is inert (no call sites in production code), so there is no new runtime decision path that could diverge from prior behavior.
  • Breaking surface: third-party/out-of-tree CDC connectors that built against connector-cdc-base and relied on it to transitively supply debezium-api/debezium-embedded must now declare those themselves. This is honestly and specifically documented in both docs/en and docs/zh incompatible-changes.md, including the caveat about how far connector Debezium versions may safely diverge given connector-cdc-base's own patched-class usage. Good documentation discipline here.

1.3 Performance / Side-Effect Analysis

  • No CPU/memory/GC change at runtime.
  • Packaging side effect (documented in the PR): the Debezium runtime (plus its Kafka Connect/compression/Jetty transitive deps) is now duplicated into each Debezium-based connector's jar instead of being shared via connector-cdc-base, increasing dist size. Acceptable, explicitly called out.
  • Minor, purely positive side effect not mentioned in the PR: connectors that are CDC-named but Debezium-free (i.e., connector-cdc-tidb today) no longer incidentally carry the full Debezium runtime in their merged classloader, since connector-cdc-base itself no longer ships it. Dead weight removed, no functional change.

1.4 Error Handling and Logging

No new error-handling paths are introduced in production code (the SPI's IllegalStateException behavior in DebeziumAdapterFactory is pre-existing and unmodified). Nothing to flag.

2. Code Quality Assessment

2.1 Coding Standards

Comments are unusually thorough and explain why, not just what — e.g., the classloader-merge rationale in connector-cdc-base/pom.xml, the shade-filter-override rationale in connector-cdc-opengauss/pom.xml, and the Javadoc on every new adapter class and test. Every new class/field/method has a comment. This meets a high bar.

2.2 Test Coverage and Test Stability

Test stability rating: Stable. All 8 new test classes (PatchedDebeziumClassesTest + one *DebeziumAdapterTest per connector) are deterministic: they read compiled .class files or META-INF/maven/.../pom.properties off the classpath, use ServiceLoader, and make no use of Thread.sleep, network I/O, fixed ports, floating point, or shared mutable static state. No flakiness risk.

However, there is a real coverage gap in what these tests can actually prove (Issue 1 below): they cannot distinguish "the per-module debezium.version property override is correctly honored by Maven" from "every module happens to resolve the same parent-default value," and they don't inspect the final packaged/shaded jar to confirm the debezium-core exclusion filter actually took effect — they only check target/classes and classpath-resolved artifact metadata.

2.3 Documentation Updates

docs/en/introduction/concepts/incompatible-changes.md and docs/zh/introduction/concepts/incompatible-changes.md were both updated with matching, detailed content, including a specific and honest note about the limits of safe per-connector Debezium divergence given connector-cdc-base's own patched-class usage. This satisfies the "any user-visible change must update docs/en and docs/zh" requirement.

3. Architectural Soundness

3.1 Elegance of the Solution

Precise fix / long-term solution. This addresses the actual root cause (a shared classloader-merged jar pinning one project-wide Debezium version) rather than working around it, and the guardrail tests + SPI groundwork are a reasonable, incremental step toward true per-connector Debezium independence (acknowledged as not-yet-complete by the PR itself and its docs).

3.2 Maintainability

Improved: Debezium ownership is now explicit per module instead of implicit via a shared base jar. The one fragile point — the shade-filter merge/override interaction between the parent's combine.children="append" and any connector that declares its own local shade <execution> — is currently correctly handled but is exactly the kind of thing a future contributor could silently break (e.g., by adding a local shade execution to another connector without knowing to restate the debezium-core filter). See Issue 1/2.

3.3 Extensibility

The DebeziumAdapter SPI (still @Experimental, still unwired into production code) gives the project a real seam for future per-connector version/classloader isolation without another large refactor.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, serialization, or historically-saved job state — this PR does not touch any of those paths.

4. Issue Summary

No. Issue Location Severity
1 New guardrail tests cannot prove the two central architectural claims of this PR: (a) that Maven correctly resolves each connector's own debezium.version property override from the inherited dependencyManagement rather than the parent's default — every module currently pins the identical 1.9.8.Final, so a broken override would still pass; (b) that the debezium-core shade exclusion filter is actually applied in the final packaged/shaded jar — the tests only inspect target/classes and classpath-resolved pom.properties, never the shaded jar contents. connector-cdc-base/.../PatchedDebeziumClassesTest.java; every */debezium/*DebeziumAdapterTest.java Medium
2 The per-module version-ownership mechanism depends on a non-obvious Maven semantic (dependency-management ${property} interpolation resolved per-leaf-module, not per-declaring-ancestor). It is correct today, but there is no comment warning against the common "helpful" refactor of moving this into a <dependencyManagement><scope>import</scope> BOM, which uses different interpolation semantics and would silently defeat per-connector version ownership. connector-cdc/pom.xml Low

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers — must be fixed

    • None. I found no High-severity issue and no functional regression for any in-tree CDC connector (verified mysql, postgres, oracle, sqlserver, mongodb, opengauss, vitess, and confirmed tidb is correctly and safely left untouched).
  2. Recommended fixes — non-blocking

    • Issue 1 (Medium): add at least one packaging-level assertion (e.g., unzip a built connector jar in a test/verify step and assert the five patched class names are absent from the debezium-core contribution, or diff the shipped TableId.class/ChangeEventQueue.class bytes against connector-cdc-base's own compiled patched classes) so a future silent regression of the shade-filter merge is caught by CI instead of by production classloading nondeterminism. This is exactly the failure mode the PR is designed to prevent, so it deserves a real regression test, not just a hand-maintained comment trail.
    • Issue 2 (Low): add a short comment on the connector-cdc/pom.xml dependencyManagement block warning future maintainers not to convert it into an import-scope BOM.

Overall assessment: this is a well-scoped, well-documented packaging refactor. I independently re-derived (not merely trusted) the Maven plugin-inheritance chain that makes the shade-filter exclusion work for all seven Debezium-based connectors, confirmed the one connector requiring a manual restatement (openGauss) handles it correctly, and confirmed the one CDC connector correctly left out of scope (TiDB) is genuinely unaffected because it never touches Debezium-referencing code paths in connector-cdc-base. No in-tree connector loses functionality, no connector registration/plugin-mapping surface is touched, and the documented incompatibility is accurately scoped to third-party connector builders. The remaining gap is test coverage for the mechanism's own guarantees, not a defect in the mechanism as implemented today.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked this PR today. Head is still afb0fada4b164062ef719f273caff9ad1197428d — no new commits since my full review above, and no replies or new discussion since then either, so this is a confirmation pass rather than a fresh review.

What I re-verified independently against current source (fresh checkout of this exact head, not just re-reading the diff):

  • connector-cdc/pom.xml still carries the <filters combine.children="append"> block appending the io.debezium:debezium-core exclusion (the five patched classes: ChangeEventQueue/ChangeEventQueue$*/TableId/TableId$*) for the six connectors that inherit shade config.
  • connector-cdc-opengauss/pom.xml still locally restates the same debezium-core exclusion inside its own <filters> block (without combine.children), which is required precisely because a child <execution>'s own filters override rather than merge with the inherited ones — confirmed this is still correct.
  • connector-cdc-tidb remains completely untouched by this diff (git diff origin/dev...HEAD --stat shows zero changes under connector-cdc-tidb/), consistent with it never touching Debezium-referencing code paths.
  • Full diff is still the same 31 files / +1719/-32 across the 7 Debezium-based connectors' pom.xml, adapters, META-INF/services registrations, and adapter tests, plus the EN/ZH incompatible-changes docs.

CI / merge state: Build, Pull Request Labeler, and Notify test workflow all still SUCCESS at this head. mergeable: MERGEABLE (no textual conflicts), though the branch is currently 44 commits behind dev — worth a rebase before merge just to keep CI validating against current dev, not a blocker in itself. PR is still marked draft and mergeStateStatus: BLOCKED (review-required gate, not a conflict).

Conclusion stands: Ready to merge after fixes, no High-severity issues. The two non-blocking items from my previous review are still open and still the right asks:

  1. (Medium) The guardrail tests don't assert against the actual shaded jar output, so they can't catch a regression in the shade-filter merge itself, nor prove the per-module debezium.version override is honored (every module currently pins the same 1.9.8.Final).
  2. (Low) A short comment on connector-cdc/pom.xml's dependencyManagement block warning against converting it to an import-scope BOM (which would defeat per-connector version interpolation) would help future maintainers.

Neither blocks merge. Nice work — this is a clean, well-documented packaging refactor with no functional regression for any in-tree connector.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, an apology. I reviewed this PR on 2026-08-10 and again on 2026-08-13 and concluded "Ready to merge after fixes, no High-severity issues." That conclusion was wrong, and the things I missed were all sitting in the diff I claimed to have traced at full depth. This is a from-scratch re-review of the same head afb0fada4b164062ef719f273caff9ad1197428d — no new commits since — and Issues 1, 2, 3, 5 and 7 below are carryover, not new findings: every one of them was catchable in the earlier rounds. In particular Issue 2 is a correctness hazard this PR creates for out-of-tree connectors, and I signed off on the migration documentation that omits it. Sorry for the wasted round-trip.

What Problem Does This PR Solve?

  • User pain point. connector-cdc-base ships the Debezium runtime (debezium-api, debezium-embedded and, transitively, debezium-core plus the Kafka Connect tail) at compile scope. Because AbstractPluginDiscovery#filterPluginJar unconditionally force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains cdc (seatunnel-plugin-discovery/src/main/java/org/apache/seatunnel/plugin/discovery/AbstractPluginDiscovery.java:514-523, and the matching "must resolve to exactly 2 jars" rule at :532-538), that one shared jar is the single project-wide Debezium version. The debezium.version property introduced by #10799 is therefore cosmetic: no connector can actually move independently.
  • Fix approach. Flip debezium-api / debezium-embedded / zstd-jni to provided in connector-cdc-base/pom.xml; relocate the version + exclusion dependencyManagement up into the connector-cdc parent so it is interpolated per leaf module against each connector's own debezium.version; have all seven Debezium-based connectors declare the Debezium runtime they ship; add a maven-shade-plugin filter that drops debezium-core's stock copies of the classes connector-cdc-base patches; and add an inert DebeziumAdapter SPI registration plus guardrail tests per connector.
  • One-sentence summary. A packaging-only refactor that moves Debezium runtime ownership out of the shared connector-cdc-base jar into each CDC connector's own jar, with no functional change for in-tree connectors today, but with a documentation gap that leaves out-of-tree connectors exposed to a silent class-resolution hazard.

Simple example. Before this PR, a MySQL CDC job's plugin class loader contained connector-cdc-mysql.jar (no io.debezium core classes at all — every debezium-connector-* dependency explicitly excludes debezium-core and debezium-api) plus connector-cdc-base.jar (Debezium core/embedded plus five patched io.debezium classes). After this PR the same class loader contains connector-cdc-mysql.jar (Debezium core/embedded, minus four patched class families) plus connector-cdc-base.jar (the five patched classes only). Same class set, different jar of origin.

1. Code Change Review

1.1 Core Logic Analysis

Coverage scope. I read the full 31-file diff (11 modified: 9 pom.xml + 2 docs; 20 added; 0 deleted; zero modified .java files), plus the surrounding unmodified source needed to test its premises: AbstractPluginDiscovery, DebeziumAdapter / DebeziumAdapterFactory, all nine connector-cdc-* poms before and after, seatunnel-connectors-v2/pom.xml, the root pom.xml shade pluginManagement block, seatunnel-format-compatible-debezium-json/pom.xml, seatunnel-dist/pom.xml, seatunnel-examples/seatunnel-engine-examples/pom.xml, all seven connector-cdc-*-e2e/pom.xml, and the three e2e test classes that import io.debezium types. I also inspected the actual debezium-core-1.9.8.Final artifact and the six debezium-connector-* artifacts to check which of them physically contain the patched classes.

Did any connector silently lose Debezium? No — verified individually, and the verification matters because provided is not transitive, so the moment connector-cdc-base marks Debezium provided every dependent loses it entirely.

Module Declares debezium.version Declares debezium-api / -embedded / zstd-jni Needs Debezium
connector-cdc-base inherits 1.9.8.Final from parent yes, provided yes (compile only)
connector-cdc-mysql yes yes yes
connector-cdc-postgres yes yes yes
connector-cdc-oracle yes yes yes
connector-cdc-sqlserver yes yes yes
connector-cdc-mongodb yes yes yes
connector-cdc-vitess yes yes yes
connector-cdc-opengauss yes yes yes
connector-cdc-tidb n/a untouched no — git grep io\.debezium under connector-cdc-tidb/ returns zero hits, and it declares connector-cdc-base at provided, so it never packaged Debezium in the first place

Does the shade filter actually reach the six connectors that inherit it? This is the part of the design that is easy to assert and hard to prove, so I re-derived it rather than trusting the comment:

  • The root pom.xml:725-767 declares maven-shade-plugin under pluginManagement with the <filters> (the *:* signature filter) at plugin level, and its single <execution> (no <id>, so id default) carries only <transformers>. That is the load-bearing detail: had the root put <filters> at execution level, the new plugin-level <filters> in connector-cdc/pom.xml would be overridden and silently dropped for all six connectors.
  • Plugin-management injection merges with the <build><plugins> entry dominant, and Xpp3Dom reads combine.children from the dominant node. connector-cdc/pom.xml:148 carries combine.children="append", so the root's *:* filter is appended rather than replacing the new debezium-core filter. Effective plugin-level filter list: [debezium-core exclusions, *:*, *:*].
  • Execution-level config (<transformers> only) merges over that; <filters> has no execution-level counterpart, so it survives intact.
  • connector-cdc-opengauss/pom.xml:95-142 is the exception: it declares its own execution (also id default, so no double shading) with its own <filters> and no combine.children. Under Xpp3Dom's positional child pairing with dominant leaf values winning, the effective result is exactly openGauss's own two filters, verbatim — which is why the manual restatement at :121-133 is genuinely required, not defensive noise. Confirmed present and correct.
  • Shade runs at ResolutionScope.RUNTIME, so provided artifacts are outside project.getArtifacts(). That is what makes connector-cdc-base's new provided scope actually keep Debezium out of its jar, and what keeps connector-cdc-base.jar itself out of the connector jars.

Is the filter necessary, or cargo cult? Necessary. I verified against the real artifacts that io/debezium/relational/TableId.class, io/debezium/connector/base/ChangeEventQueue.class, io/debezium/heartbeat/HeartbeatFactory.class and io/debezium/relational/HistorizedRelationalDatabaseConnectorConfig.class exist only in debezium-core — all six debezium-connector-* artifacts, debezium-embedded, debezium-api and debezium-ddl-parser contain none of them. So the single io.debezium:debezium-core artifact selector is sufficient and correctly scoped. I also confirmed the parent-pom comment's historical claim is accurate: pre-PR the connector jars contained no debezium-core at all, because every debezium-connector-* managed entry excludes debezium-core and debezium-api and connector-cdc-base was provided. Without this filter the move would have introduced a genuine duplicate-class race, and ChangeEventQueue's own header (connector-cdc-base/src/main/java/io/debezium/connector/base/ChangeEventQueue.java:49) says it exists "to avoid the OOM in snapshot phase" — so losing that race is a real production failure mode, not a cosmetic one.

Does the normal CDC read/snapshot path reach the new code? No. DebeziumAdapterFactory.getAdapter(...) has zero production call sites anywhere in the tree — git grep finds it only in DebeziumAdapterFactory itself and in the new/pre-existing test classes. The interface Javadoc says concrete wiring "will be added in follow-up PRs". There is no reflection-based or string-based lookup of the old package path anywhere: no class was renamed or moved, so there is nothing to look up. The only runtime-observable change is which jar supplies which class inside a class loader that already contained both jars.

openGauss / Postgres coupling. connector-cdc-opengauss depends on connector-cdc-postgres at compile scope and deliberately registers no adapter of its own, reusing PostgresDebeziumAdapter via the shaded META-INF/services merge. Worth noting for a future reader: when a job loads both connector-cdc-opengauss.jar and connector-cdc-postgres.jar, two jars will carry an identical META-INF/services entry naming the same FQCN. ServiceLoader.parse() deduplicates by provider class name across config URLs, so DebeziumAdapterFactory's exactly-one-match rule still holds. I checked this specifically because it would have been a latent IllegalStateException once the SPI gets wired; it is safe.

1.2 Compatibility Impact

Judgment: Partially incompatible — deliberately, and for third parties only.

  • Config / API / SPI surface: unchanged. No Option renamed, removed or re-defaulted. No public class changed its fully-qualified name — the diff modifies zero .java files. DebeziumAdapter and DebeziumAdapterFactory pre-date this PR and are untouched.
  • Checkpoint / savepoint / serialized state: untouched. No offset, split, state-serializer or snapshot/incremental-read class is in the diff. I checked specifically for classes participating in snapshot, incremental-read or offset-tracking logic, since a refactor of this shape could plausibly touch them; none are affected. The additions are three files per connector (an adapter, a services registration, a test) that no execution path reaches.
  • In-tree runtime behavior: unchanged, because all seven connectors resolve the same 1.9.8.Final and the patched classes still come from connector-cdc-base.jar. Verified rather than assumed.
  • Out-of-tree connectors: breaking, and the documented migration is incomplete — see Issue 2. That is the compatibility finding that matters in this round.

1.3 Performance / Side-Effect Analysis

  • No CPU / memory / GC change on the job path.
  • Packaging. This is the largest side effect and the docs describe it only qualitatively. Estimating from the local artifacts: per connector the newly packaged runtime is roughly zstd-jni 5.6 MB + debezium-core 0.93 MB + the debezium-embedded Kafka Connect tail (connect-api ~0.1 MB, connect-runtime ~0.6 MB, connect-json, connect-file, Jetty, reflections/javassist) ~2 MB, so on the order of 8-9 MB per connector. Multiplied across seven connectors and netting out the ~8-9 MB removed from connector-cdc-base, the binary distribution's connectors/ directory grows by roughly 45-55 MB, and each install-plugin.sh download of a CDC connector grows by ~8 MB. Treat these as estimates derived from artifact sizes, not from a built distribution — I did not build this PR.
  • zstd-jni is the single biggest line item and has no Debezium-ownership justification — see Issue 5.
  • Duplicate-class overlap inside connector-cdc-opengauss.jar: openGauss now shades both the connector-cdc-postgres uber-jar (which itself carries a filtered debezium-core) and the raw debezium-core artifact. Shade resolves the overlap first-writer-wins and both are 1.9.8.Final, so the outcome is correct today; expect additional "overlapping classes" warnings in the build log. Not a defect, but a maintainer reading a noisier openGauss build should know why.
  • Positive side effect: connector-cdc-tidb, which is Debezium-free, no longer drags the whole Debezium runtime into its merged class loader via connector-cdc-base.

1.4 Error Handling and Logging

No new error-handling paths in production code. DebeziumAdapterFactory's IllegalStateException behavior for the no-match and multi-match cases is pre-existing and unmodified, and its messages are actionable (they name the connector class and list matched providers with their Debezium versions). The test helpers wrap IOException in IllegalStateException with the failing resource path, which is appropriate for test code. Nothing to flag.

2. Code Quality Assessment

2.1 Coding Standards

High. Every new class, field and method carries Javadoc that explains why rather than restating the code, and the pom comments document the classloader-merge rationale, the shade-override rationale and the openGauss/Postgres coupling. License headers are present on all 20 new files (CI's "License header" and "Code style" jobs are green). The one standards problem is that three of those carefully written comments state something that is not true — see Issue 1.

The seven adapter classes are near-identical (constant name, one string, two trivial overrides). That is acceptable for an SPI whose whole point is per-connector ownership; pulling the shared shape into an abstract base in connector-cdc-base would reintroduce exactly the coupling the PR is removing. No objection.

2.2 Test Coverage and Test Stability

Test stability: Stable. All eight new test classes are deterministic — they read compiled .class files, classpath-resolved META-INF/maven/**/pom.properties, and ServiceLoader registrations. No sleeps, no network, no fixed ports, no shared mutable static state, no @DisplayName. No flakiness risk.

Coverage, however, does not reach the two claims that carry the PR:

  • The per-connector *DebeziumAdapterTest classes prove that everything on the module's test classpath agrees on one Debezium version. They cannot distinguish "each module's own debezium.version override was honored" from "every module inherited the same parent default", because all seven currently pin the identical 1.9.8.Final.
  • Nothing inspects a packaged jar. PatchedDebeziumClassesTest reads target/classes; the adapter tests read classpath metadata. The shade-filter merge — the single fragile mechanism this PR introduces, and the one whose failure is silent — has no assertion at all. See Issues 3 and 4.

CI evidence is genuinely good and I want to give it full weight: the fork run exercised all seven CDC e2e modules (connector-cdc-mysql-e2e, -postgres-e2e, -sqlserver-e2e, -mongodb-e2e, -oracle-e2e, -opengauss-e2e, -tidb-e2e) across the eight updated-modules-integration-test parts on both JDK 8 and 11, plus a dedicated oracle-cdc-connector-it, all green. Those e2e suites run against a real built distribution through the real plugin-discovery class loader, so they do validate the classpath move end to end. What they do not do is fail if the shade filter silently stops applying, because both copies of TableId are byte-compatible at 1.9.8.Final — the regression this PR guards against is invisible until the versions diverge.

No test hardcodes the old shared location's package name; there is no old package name, since nothing moved packages.

2.3 Documentation Updates

docs/en and docs/zh incompatible-changes.md were both updated with matching content, which satisfies the "user-visible change must update both" rule. The "How far connectors may actually diverge" bullet is unusually honest and I want to keep it as-is. But the "Impact" bullet immediately above it is factually wrong and directly contradicts it (Issue 1), and the migration instruction it gives third parties is incomplete in a way that can cause a snapshot-phase OOM (Issue 2). Documentation is where this PR's remaining risk lives.

3. Architectural Soundness

3.1 Elegance of the Solution

Addresses the real root cause — a force-merged shared jar pinning one project-wide version — rather than working around it. The choice to manage versions in the connector-cdc parent's dependencyManagement while declaring them per module is the correct Maven idiom for "shared exclusion rules, per-module version", and the shade filter closes the duplicate-class hole the move would otherwise open. The design is sound; the gaps are in what is documented and what is enforced, not in the mechanism.

3.2 Maintainability

Net improvement: ownership is now explicit per module. Two fragile points remain, both hand-maintained and neither machine-checked: the shade-filter merge/override interaction (any connector that later adds its own shade <execution> must restate the debezium-core filter, as openGauss does, or silently lose it), and the correspondence between connector-cdc-base/src/main/java/io/debezium/** and the two exclusion lists. Issue 3 shows the second one has already drifted in letter.

3.3 Extensibility

The DebeziumAdapter SPI gives a real seam for future per-connector isolation. Two forward-looking notes for whoever writes the follow-up: (a) once versions actually diverge, a job that uses two CDC connectors at once puts two different Debezium runtimes on one merged class loader and reintroduces the same first-wins race one level up — the SPI will need real class loader separation, not just a version string; (b) ServiceLoader's name-based dedup means the openGauss/Postgres pair will resolve to whichever PostgresDebeziumAdapter class loads first, which is fine while they are identical and will need attention when they are not.

3.4 Historical-Version Compatibility

No impact. Nothing in the diff touches checkpoint/savepoint state, offset tracking, split serialization, or any persisted format. A job checkpointed on the previous release restores unchanged, because the class set visible to it is identical. Checkpoint/restore rigor is warranted for any cross-module CDC refactor, but it genuinely finds nothing here — I verified by confirming zero .java files were modified and that all 20 added files are unreachable from any production call path.

4. Issue Summary

# Issue Location Severity
1 Three places state connector-cdc-base no longer contains io.debezium classes; it still ships five, and the same doc entry says so two bullets later docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:45-57 Medium
2 Third-party migration instructions omit the mandatory io.debezium:debezium-core shade exclusion, leaving out-of-tree connectors with a silent, order-dependent class-resolution race against the patched ChangeEventQueue docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143 High
3 EXPECTED_PATCHED_CLASSES lists five classes, both shade filters exclude four; the omission is unexplained and the test never compares itself against the pom filters it claims to guard .../connector-cdc-base/src/test/java/.../PatchedDebeziumClassesTest.java:50-59; seatunnel-connectors-v2/connector-cdc/pom.xml:148-161; seatunnel-connectors-v2/connector-cdc/connector-cdc-opengauss/pom.xml:121-133 Medium
4 Guardrail tests assert nothing about the packaged/shaded jar and cannot prove the per-module debezium.version override is honored .../PatchedDebeziumClassesTest.java; every .../debezium/*DebeziumAdapterTest.java Medium
5 zstd-jni is swept into the per-connector move with no Debezium-ownership rationale, multiplying a 5.6 MB artifact across seven jars seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:70-73 plus the seven connector poms Medium
6 Distribution size growth documented only qualitatively; no number given for a change that adds roughly 45-55 MB docs/en/introduction/concepts/incompatible-changes.md:151; docs/zh/introduction/concepts/incompatible-changes.md:144 Low
7 Redundant restatement of the *:* signature filter, justified by a comment that hedges on a Maven merge rule that is in fact deterministic seatunnel-connectors-v2/connector-cdc/pom.xml:162-173 Low
8 PR is still a draft, and its base is 60 commits behind dev; the green CI dates from 2026-08-09 against a 2026-08-03 base PR metadata Low

Issue 1 — connector-cdc-base is documented as Debezium-class-free; it is not

  • Location: docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:45-57.
  • Problem: The EN Impact bullet ends "connectors/connector-cdc-base-*.jar no longer contains io.debezium classes", the ZH mirrors it, and the pom comment says keeping the dependencies provided "leaves the shaded connector-cdc-base jar free of Debezium classes". All three are false. connector-cdc-base/src/main/java/io/debezium/ contains and ships five classes: connector/base/ChangeEventQueue, heartbeat/DefaultHeartbeatConnectionProvider, heartbeat/HeartbeatFactory, relational/HistorizedRelationalDatabaseConnectorConfig, relational/TableId. Line 152 of the same EN entry says so explicitly, so the document contradicts itself two bullets apart.
  • Potential risk: An out-of-tree connector maintainer who believes the base jar is Debezium-free has no reason to suspect a class overlap, and will package their own stock TableId/ChangeEventQueue — which is exactly the failure Issue 2 describes. The statement is not merely imprecise; it actively points readers away from the one thing they must do.
  • Best improvement: Replace with something like "no longer contains the Debezium runtime (debezium-api, debezium-embedded, debezium-core and their Kafka Connect / compression / Jetty dependencies). It still ships its five patched io.debezium classes, which every CDC connector jar must exclude from its own debezium-core copy." Mirror in ZH, and fix the pom comment to say "free of the Debezium runtime" rather than "free of Debezium classes".
  • Severity: Medium.
  • Raised by another reviewer: No.

Issue 2 — Third-party migration path omits the mandatory shade exclusion

  • Location: docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143.
  • Problem: The migration instruction is "Third-party CDC connectors ... must now declare debezium-api and debezium-embedded themselves." A third party who does exactly and only that ends up with debezium-core on their compile scope and therefore with stock io/debezium/relational/TableId.class, io/debezium/connector/base/ChangeEventQueue.class, io/debezium/heartbeat/HeartbeatFactory.class and io/debezium/relational/HistorizedRelationalDatabaseConnectorConfig.class inside their shaded connector jar — sitting next to connector-cdc-base's patched copies on the same force-merged class loader. Which copy wins depends on File#listFiles ordering. This is the exact scenario connector-cdc/pom.xml:137-142 describes as "the patches could silently stop applying", and the in-tree fix for it (the io.debezium:debezium-core filter) is nowhere in the migration text. The instruction also omits zstd-jni, which every in-tree connector pom declares because the managed debezium-embedded entry excludes it.
  • Potential risk: Real and non-cosmetic. ChangeEventQueue is patched, per its own header comment at connector-cdc-base/src/main/java/io/debezium/connector/base/ChangeEventQueue.java:49, "to avoid the OOM in snapshot phase". A third-party CDC connector rebuilt against the new base can therefore start OOM-ing during snapshot on some machines and not others, with no diagnostic pointing at the cause. The PR knowingly creates this hazard for the exact audience the change breaks, and the breaking-change note is the only place it would be caught.
  • Best improvement: Extend the Impact bullet (EN and ZH) with the complete recipe: declare debezium-api, debezium-embedded and zstd-jni; and add a maven-shade-plugin <filter><artifact>io.debezium:debezium-core</artifact> excluding the four patched class families (with the $* inner-class variants), noting that a connector declaring its own shade <execution> must restate the filter inside that execution because execution-level <filters> replace the inherited ones. Copying the exact block from connector-cdc/pom.xml:149-161 into the doc would make this unambiguous. Given the failure mode is a silent snapshot OOM, I would not merge without this.
  • Severity: High.
  • Raised by another reviewer: No.

Issue 3 — Patched-class list and shade exclusion list disagree, and nothing checks them against each other

  • Location: .../connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:50-59; seatunnel-connectors-v2/connector-cdc/pom.xml:148-161; seatunnel-connectors-v2/connector-cdc/connector-cdc-opengauss/pom.xml:121-133.
  • Problem: The field Javadoc states "Every entry here must also be excluded from debezium-core by the CDC shade configuration." EXPECTED_PATCHED_CLASSES has five entries; both shade filters exclude four families — io/debezium/heartbeat/DefaultHeartbeatConnectionProvider is absent from both. I checked the artifact: that class genuinely does not exist in debezium-core-1.9.8.Final (which ships HeartbeatConnectionProvider, an interface, but no Default implementation), so the omission is harmless today and is presumably deliberate. Nothing says so. Separately, the test compares compiled classes against a hardcoded constant, never against the pom filters, so it structurally cannot detect a real list-versus-filter divergence — it can only nag when a new patched class appears, and then only if the author reads the failure message.
  • Potential risk: A maintainer cannot tell whether DefaultHeartbeatConnectionProvider was deliberately omitted or forgotten, which is the exact ambiguity a guardrail is supposed to eliminate. If a later Debezium line introduces that class into debezium-core, the duplicate arrives and the guardrail stays green.
  • Best improvement: (a) Add one line recording that DefaultHeartbeatConnectionProvider is a SeaTunnel-introduced class with no debezium-core counterpart, so no exclusion is needed. (b) Better, make the test machine-check the correspondence: the two poms sit at known relative paths from ${project.basedir}, so the test can parse their io.debezium:debezium-core <excludes> and assert they cover exactly the compiled patched set minus classes absent from debezium-core. That turns a comment-maintained invariant into an enforced one and subsumes half of Issue 4.
  • Severity: Medium.
  • Raised by another reviewer: No — and this is carryover; it was visible at this head in both my earlier rounds.

Issue 4 — Guardrail tests do not exercise the packaged jar or the version-override mechanism

  • Location: .../PatchedDebeziumClassesTest.java; every .../debezium/*DebeziumAdapterTest.java.
  • Problem: (a) No test inspects a built jar, so a regression in the shade-filter merge — the one novel and fragile mechanism here — is undetectable by CI. (b) Every module pins the identical 1.9.8.Final, so packagedDebeziumArtifactsAllMatchDeclaredVersion passes equally well whether the per-module override is honored or silently ignored in favour of the parent default.
  • Potential risk: The failure this PR exists to prevent is precisely the one CI cannot see. Both classes of regression surface only in production class loading, non-deterministically.
  • Best improvement: Add a packaging-level assertion — for instance a maven-invoker/verify-phase check, or a test that opens target/connector-cdc-*.jar and asserts the four patched entries are absent while, say, io/debezium/relational/TableIdParser.class is present (proving debezium-core was shaded in and the filter fired, rather than debezium-core simply being missing). For (b), a build-time assertion that ${debezium.version} as resolved in the module equals the adapter's constant would at least pin the interpolation path.
  • Severity: Medium.
  • Raised by another reviewer: Yes (@DanielLeens, review of 2026-08-10, reaffirmed 2026-08-13) — still open at this head.

Issue 5 — zstd-jni moved without a Debezium-ownership rationale

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:70-73 (now provided) and the corresponding new blocks in all seven connector poms.
  • Problem: The PR's entire argument is that connector-cdc-base must stop packaging Debezium so each connector can own its Debezium version. com.github.luben:zstd-jni contains no io.debezium classes, is not part of the Debezium API surface, and is referenced nowhere in connector-cdc/** source (git grep -i zstd across the CDC tree returns only pom hits). It is present only because the managed debezium-embedded entry excludes the transitive copy for Apple M1 compatibility and re-adds 1.5.5-5 explicitly. Making it provided in the base and re-declaring it in seven connectors does not advance version ownership; it just copies a 5.6 MB artifact seven times instead of once.
  • Potential risk: About 5.6 MB × 6 = ~34 MB of avoidable growth in the distribution and in every CDC connector download — the single largest contributor to Issue 6's number — with no stated benefit. It also widens the blast radius of any future zstd-jni bump from one pom to eight.
  • Best improvement: Either leave zstd-jni at compile scope in connector-cdc-base (the merged class loader supplies it to every CDC connector exactly as before, and it does not participate in the Debezium version-pinning problem), or state explicitly in the pom comment why a connector owning its Debezium version must also own its zstd version. If the intent is future-proofing for a Debezium line requiring a different zstd, say so.
  • Severity: Medium.
  • Raised by another reviewer: No.

Issue 6 — Distribution size growth is not quantified

  • Location: docs/en/introduction/concepts/incompatible-changes.md:151; docs/zh/introduction/concepts/incompatible-changes.md:144.
  • Problem: The packaging note says the distribution "increases in size" without a figure. My estimate from artifact sizes is roughly +45-55 MB in connectors/ and ~+8 MB per CDC connector download.
  • Potential risk: Low on its own, but this is the sort of number packagers, image builders and air-gapped users need before an upgrade, and an incompatible-changes entry is exactly where they will look for it.
  • Best improvement: Add the measured (not estimated) delta from a built distribution, before and after, to both docs.
  • Severity: Low.
  • Raised by another reviewer: No.

Issue 7 — Redundant signature filter, with a comment that hedges on a deterministic rule

  • Location: seatunnel-connectors-v2/connector-cdc/pom.xml:162-173.
  • Problem: The restated *:* META-INF/*.SF|DSA|RSA filter is justified by "so the signature files stay excluded whether or not the inherited filter list is merged into this one". The merge is not conditional: combine.children="append" on line 148 is read from the dominant node during plugin-management injection, so the root's filter is always appended. The result is two identical *:* filters in the effective configuration. Harmless — shade applies each independently — but the comment records uncertainty about a rule the PR in fact depends on being deterministic (openGauss's manual restatement is correct only because that same merge behaviour is predictable).
  • Potential risk: Cosmetic. The risk is that a future maintainer reads the hedge, concludes the merge is unpredictable, and starts restating filters defensively in every connector.
  • Best improvement: Keep the duplicate if you prefer belt-and-braces — removing it is not worth the tail risk of leaking signature files into an uber-jar — but rewrite the comment to state the actual rule: plugin-level <filters> merge by combine.children="append", execution-level <filters> replace, which is why openGauss must restate and the others need not.
  • Severity: Low.
  • Raised by another reviewer: No.

Issue 8 — Draft status and stale base

  • Location: PR metadata.
  • Problem: isDraft: true, mergeStateStatus: BLOCKED, reviewDecision: REVIEW_REQUIRED, mergeable: MERGEABLE (no textual conflict). The head is 60 commits behind dev, and the green CI run finished 2026-08-09 against a 2026-08-03 base.
  • Potential risk: A draft cannot be merged. And because this PR edits a shared parent pom's build/plugins section, a stale base is more consequential than usual — any shade or plugin change landing on dev in those 60 commits would interact with it and is not covered by the current green run.
  • Best improvement: Mark ready for review, rebase or gh pr update-branch onto current dev, and let CI re-run before merge.
  • Severity: Low.
  • Raised by another reviewer: No.

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers — must be fixed

    • Issue 2 (High): complete the third-party migration instructions in docs/en and docs/zh with the mandatory io.debezium:debezium-core shade exclusion (and zstd-jni), including the note that a connector with its own shade <execution> must restate the filter inside it. Without this, this PR hands out-of-tree CDC connectors a silent snapshot-phase OOM risk with no documented mitigation. It is a two-paragraph fix.
    • Issue 1 (Medium, bundled with the above): correct the three statements claiming connector-cdc-base no longer contains io.debezium classes. This lives in the same doc bullet as Issue 2 and directly undercuts the fix for it, so it should land together.
  2. Recommended fixes — non-blocking

    • Issue 3: explain or close the DefaultHeartbeatConnectionProvider asymmetry, and preferably make PatchedDebeziumClassesTest check the pom filters directly instead of a hand-kept constant.
    • Issue 4 (carryover): add one packaging-level assertion against a built connector jar, so the shade-filter merge is covered by CI rather than by comment discipline.
    • Issue 5: leave zstd-jni compile-scoped in connector-cdc-base, or document why per-connector Debezium ownership requires per-connector zstd ownership.
    • Issue 6: replace the qualitative size note with a measured delta.
    • Issue 7: fix the comment on the restated signature filter to state the real Maven merge rule.
    • Issue 8: mark ready for review and rebase onto current dev before merge.
  3. Overall assessment

    The mechanism is right and I verified it rather than accepting it. I re-derived the Maven plugin-inheritance and Xpp3Dom merge chain that makes the debezium-core filter reach all six inheriting connectors, confirmed why openGauss is the one module that must restate it and that it does, confirmed shade's ResolutionScope.RUNTIME is what makes the provided flip actually keep Debezium out of the base jar, confirmed against the real artifacts that only debezium-core carries the patched classes (so the artifact selector is correctly scoped), confirmed every one of the seven Debezium-based connectors gained explicit declarations so none silently lost classes, confirmed connector-cdc-tidb has zero io.debezium references and is correctly untouched, and confirmed no .java file was modified — so no public FQCN moved, no SPI or serialized-state reference changed, and the DebeziumAdapter SPI remains inert with zero production call sites. CI ran all seven CDC e2e suites green against a real distribution on JDK 8 and 11.

    What lets this down is documentation, and specifically the one bullet an out-of-tree connector maintainer will read. This PR deliberately breaks third-party CDC connectors and then tells them to do half of what is required; the omitted half prevents a non-deterministic snapshot-phase OOM. That is a blocker in my view even though no in-tree connector is affected, because the breaking-change note is the only channel through which the affected audience learns anything at all. The remaining items are quality-of-guardrail and packaging-hygiene asks, none of which should hold the change.

    I should have caught Issues 1, 2, 3, 5 and 7 in my 2026-08-10 review and again on 2026-08-13. They were all present at this head, and "no High-severity issues" was the wrong call. Correcting that here.

@DanielLeens

Copy link
Copy Markdown
Contributor Author

Re-checked this PR today. Head is still afb0fada4b164062ef719f273caff9ad1197428d — the same commit my full re-review at #11639 (review) covered — and there has been no new commit and no new reply since then, so I did not publish a second full Steps-1-11 review against the same code version (that would just duplicate Issues 1-8 above against unchanged code). Instead, this is a re-verification round:

  • CI: still green, and it's literally the same run (Build / run 91769082988, completed 2026-08-04) — nothing to re-diagnose.
  • Merge state: mergeable: true (no textual conflict), mergeable_state: blocked because the PR is still a draft with reviewDecision: REVIEW_REQUIRED — matches Issue 8.
  • Upstream drift: dev has moved further away since my last check (behind_by is now 85 vs. 60 three days ago). I walked every dev commit added in that window that touches pom.xml, seatunnel-connectors-v2/connector-cdc/**, or seatunnel-dist/** to see whether anything on dev now interacts with the shade-filter mechanism this PR depends on (the connector-cdc parent's <filters combine.children="append"> block and the connector-cdc-opengauss manual restatement). Nothing does — the one CDC/shading-adjacent commit in that range, [Fix][Docker] Stop unrelocated openGauss driver from hijacking PostgreSQL connections #11522 ("Stop unrelocated openGauss driver from hijacking PostgreSQL connections"), only touches seatunnel-dist/pom.xml's driver packaging and a new DriverJarPackagingTest, not connector-cdc/pom.xml or connector-cdc-opengauss/pom.xml. So the shade-inheritance chain I traced on 08-16 is still accurate as-is; the growing behind_by is queue metadata, not a new blocker, and I'm not asking for a sync here since nothing about the current CI failure needs it (CI isn't failing).
  • Blockers unchanged: since no commit has landed, Issue 2 (High — the third-party migration note in docs/en and docs/zh incompatible-changes.md omits the mandatory io.debezium:debezium-core shade exclusion, leaving out-of-tree CDC connectors exposed to a silent, order-dependent class-resolution race against the patched ChangeEventQueue) and Issue 1 (Medium — the same doc bullet incorrectly claims connector-cdc-base ships zero io.debezium classes, when it still ships five patched ones) are both still open at this head. That's the same class of risk as the Jackson-relocation regressions this project has hit before with cross-module shading changes: the mechanism itself is correctly implemented and verified for every in-tree connector, but the migration guidance for anyone building outside the tree is incomplete, which is exactly the audience least likely to notice before they hit an OOM in the snapshot phase.

Conclusion is unchanged from the 08-16 review: Ready to merge after fixes, blocked on Issue 2 (High) and Issue 1 (Medium), with Issues 3-7 as non-blocking follow-ups and Issue 8 (draft status / rebase) still applying. I'll take another full pass as soon as a new commit lands.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checking my own PR again today. Head is still afb0fada4b164062ef719f273caff9ad1197428d — no new commits, and no new comments or reviews from anyone (including @SEZ9) since my last round on 2026-08-19. I re-skimmed the diff and it still matches what I reviewed then: moving Debezium ownership from connector-cdc-base into each individual CDC connector module. Build on the apache-side check is still green (SUCCESS). This PR remains in draft, and my prior conclusion holds unchanged — no new findings to report.

@DanielLeens

Copy link
Copy Markdown
Contributor Author

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base ships the Debezium runtime (debezium-api, debezium-embedded, and transitively debezium-core plus its Kafka Connect tail) at compile scope, and because AbstractPluginDiscovery#filterPluginJar force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc", that one shared jar pins a single project-wide Debezium version. No individual CDC connector can move independently.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base/pom.xml, relocate the version + exclusion dependencyManagement into the connector-cdc parent (interpolated per leaf module against each connector's own debezium.version), have all seven Debezium-based connectors declare their own runtime Debezium dependencies, add a maven-shade-plugin filter dropping debezium-core's stock copies of the five classes connector-cdc-base patches, and add an inert DebeziumAdapter SPI + guardrail tests per connector.
  • One-sentence summary: a packaging-only refactor moving Debezium ownership from the shared connector-cdc-base jar into each connector's own jar, with no runtime behavior change for in-tree connectors, but with a documentation gap for out-of-tree connectors that I flagged in my 2026-08-16 review and that is still open today.

1. Code Change Review

1.1 Core Logic Analysis

Status of this round. Head is still afb0fada4b164062ef719f273caff9ad1197428d (unchanged since 2026-08-03). No new commit, no new comment/review from anyone (including @SEZ9) since my last check on 2026-08-21. I did not merely re-read my own prior conclusions; I re-derived the load-bearing facts against the current tree and against dev's current tip, and I checked one thing I had not specifically checked before:

  • git diff --stat of this PR's own changes (merge-base 6bf786bee7cc vs. head) is still exactly the 31 files / +1719/-32 I traced in full in the 2026-08-16 review: the connector-cdc-base -> provided flip, the connector-cdc parent dependencyManagement relocation, the <filters combine.children="append"> shade block, seven connectors' new Debezium dependencies + DebeziumAdapter + META-INF/services + adapter test, and the two docs. Zero .java files under existing production packages were touched by this PR (only new files), consistent with every earlier round.
  • I re-confirmed directly against the current head that connector-cdc-base/src/main/java/io/debezium/** still ships exactly five patched classes (ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfig, HeartbeatFactory, DefaultHeartbeatConnectionProvider), and that connector-cdc/pom.xml's shade <filters> block still excludes only four of those five families from debezium-core (DefaultHeartbeatConnectionProvider is still not excluded) — Issues 1 and 3 below are unchanged.
  • New check this round: dev gained a same-day commit after my last review, 4ba289595 ("[Feature][shade] Refactor the seatunnel-shade module", [Feature][shade]Refactor the seatunnel-shade module. #9993, merged 2026-08-21 14:00, i.e. after my 00:29 check), which touches the root pom.xml (115 lines) and, notably, connector-cdc-base/pom.xml (4 lines). Because this PR also edits connector-cdc-base/pom.xml's dependency block, I specifically checked whether [Feature][shade]Refactor the seatunnel-shade module. #9993 conflicts with or invalidates the shade-filter mechanism this PR depends on. It does neither: [Feature][shade]Refactor the seatunnel-shade module. #9993's connector-cdc-base/pom.xml hunk only swaps the module's seatunnel-hikari dependency coordinate for the new seatunnel-shade-hikari artifact (a pre-built shaded-jar rename, unrelated to Debezium), and its root pom.xml hunk only adds new seatunnel-shade-* managed dependencies — it does not touch the maven-shade-plugin pluginManagement/<filters>/<execution> configuration my 2026-08-16 review traced (I grepped the full [Feature][shade]Refactor the seatunnel-shade module. #9993 diff for maven-shade-plugin; there are zero hits). So the plugin-inheritance chain that makes the debezium-core exclusion filter reach all six inheriting connectors, and the manual restatement required in connector-cdc-opengauss, are both still exactly as I verified them.
  • GitHub's own merge computation confirms no textual conflict has been introduced by this drift: mergeable: true, mergeable_state: blocked (the REVIEW_REQUIRED gate, not a conflict) — same as every prior round.
  • DebeziumAdapterFactory.getAdapter(...) still has zero production call sites; the SPI remains inert scaffolding, so the normal CDC read/snapshot path is unaffected by this PR, as established previously.

I am not restating the full plugin-inheritance derivation, the zstd-jni analysis, or the openGauss/Postgres coupling check here since nothing about them has changed and they were derived in detail in my 2026-08-16 review (still visible above on this PR) — I re-verified their conclusions still hold against the current head and against current dev, rather than re-deriving them from first principles a second time.

1.2 Compatibility Impact

Judgment: Partially incompatible (unchanged). No in-tree connector's config, API, checkpoint, or serialization surface is touched. All seven Debezium-based connectors still resolve debezium.version=1.9.8.Final, so the class set on a running CDC job's class loader is identical before and after. The breaking surface remains scoped to third-party/out-of-tree CDC connectors that relied on connector-cdc-base to transitively supply Debezium — and the migration note for that audience is still incomplete (Issue 2).

1.3 Performance / Side-Effect Analysis

Unchanged from prior rounds: no CPU/memory/GC change on the job path; the Debezium runtime (~8-9 MB per connector including zstd-jni) is now duplicated into each of the seven connector jars instead of shared via connector-cdc-base, growing the distribution by an estimated 45-55 MB (still not quantified in the docs — Issue 6).

1.4 Error Handling and Logging

No new error-handling paths in production code; nothing to flag.

2. Code Quality Assessment

2.1 Coding Standards

Unchanged: comments throughout are unusually thorough and explain why, not just what. License headers present on all new files.

2.2 Test Coverage and Test Stability

Test stability rating: Stable (unchanged) — all eight new test classes are deterministic (compiled-class/classpath/ServiceLoader inspection only, no sleeps/network/fixed ports/shared mutable state).

Coverage gap unchanged: none of the guardrail tests inspect a packaged/shaded jar (so a regression in the shade-filter merge — the one genuinely fragile, novel mechanism here — would not be caught by CI), and every module currently resolving the identical 1.9.8.Final baseline means the tests cannot distinguish "the per-module debezium.version override is honored" from "every module inherited the same parent default." This is Issue 4, carried over.

2.3 Documentation Updates

docs/en and docs/zh incompatible-changes.md remain updated and symmetric, but the specific content problems I raised on 2026-08-16 (Issues 1 and 2) are both still present verbatim at the same lines.

3. Architectural Soundness

3.1 Elegance of the Solution

Unchanged assessment: this is a precise, long-term fix for the real root cause (a force-merged shared jar pinning one project-wide Debezium version), not a workaround.

3.2 Maintainability

Unchanged: net improvement, with the same two hand-maintained (not machine-checked) fragile points as before — the shade-filter merge/override interaction, and the correspondence between the patched-class list and the two exclusion lists (which has already drifted in letter, per Issue 3).

3.3 Extensibility

Unchanged: the DebeziumAdapter SPI is a reasonable seam for future per-connector isolation, still not wired into production code.

3.4 Historical-Version Compatibility

Unchanged: no impact on checkpoint/savepoint, offset tracking, or serialized state — confirmed again this round that zero .java files under existing packages were modified.

4. Issue Summary

# Issue Location Severity Status
1 Docs and a pom comment claim connector-cdc-base no longer contains io.debezium classes; it still ships five patched ones docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143; connector-cdc-base/pom.xml:45-57 Medium Still open, re-verified this round
2 Third-party migration instructions omit the mandatory io.debezium:debezium-core shade exclusion (and zstd-jni), leaving out-of-tree connectors exposed to a silent, order-dependent class-resolution race against the patched ChangeEventQueue (which exists specifically "to avoid the OOM in snapshot phase") docs/en/introduction/concepts/incompatible-changes.md:150; docs/zh/introduction/concepts/incompatible-changes.md:143 High Still open, re-verified this round
3 EXPECTED_PATCHED_CLASSES lists five classes; both shade filters exclude only four (DefaultHeartbeatConnectionProvider omitted from both, unexplained); the test never checks itself against the pom filters .../PatchedDebeziumClassesTest.java:50-59; connector-cdc/pom.xml:148-161; connector-cdc-opengauss/pom.xml:121-133 Medium Still open, re-verified this round
4 Guardrail tests assert nothing about the packaged/shaded jar and cannot prove the per-module debezium.version override is honored over the parent default .../PatchedDebeziumClassesTest.java; every .../debezium/*DebeziumAdapterTest.java Medium Still open
5 zstd-jni moved to each connector without a Debezium-ownership rationale, multiplying a 5.6 MB artifact seven times connector-cdc-base/pom.xml:70-73 + seven connector poms Medium Still open
6 Distribution size growth (est. +45-55 MB) documented only qualitatively docs/en/.../incompatible-changes.md:151; docs/zh/.../incompatible-changes.md:144 Low Still open
7 Redundant *:* signature filter restatement justified by a comment that hedges on a merge rule that is in fact deterministic connector-cdc/pom.xml:162-173 Low Still open
8 PR is still a draft, mergeStateStatus: BLOCKED (review-required gate), and is now 105 commits behind dev PR metadata Low Still open; no conflict introduced by the drift (verified against #9993 specifically this round)

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers — must be fixed

    • Issue 2 (High): complete the third-party migration instructions in docs/en and docs/zh with the mandatory io.debezium:debezium-core shade exclusion (and zstd-jni), including the note that a connector declaring its own shade <execution> must restate the filter. Without this, out-of-tree CDC connectors are left with an undocumented, non-deterministic snapshot-phase OOM risk.
    • Issue 1 (Medium, bundled with the above): correct the "no longer contains io.debezium classes" claim, which sits in the same doc bullet and directly undercuts the fix for Issue 2.
  2. Recommended fixes — non-blocking

    • Issue 3: explain or close the DefaultHeartbeatConnectionProvider asymmetry; ideally make the guardrail test check the pom filters directly.
    • Issue 4: add a packaging-level assertion against a built connector jar.
    • Issue 5: leave zstd-jni compile-scoped in connector-cdc-base, or document why per-connector Debezium ownership requires per-connector zstd ownership.
    • Issue 6: replace the qualitative size note with a measured delta.
    • Issue 7: fix the comment to state the real Maven merge rule.
    • Issue 8: mark ready for review and rebase onto current dev before merge.

On the other reviewer: @SEZ9's 2026-08-04 COMMENTED review ("LGTM, can merge") predates all of the above findings and was explicitly conditioned on verifying the shade filters in connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml, which were "not visible in this diff excerpt" at the time. I have since verified those filters directly against the real artifacts (see 1.1) and found the gap in Issue 2 that his review did not have visibility into. I don't read his approval as still standing against the current, fully-traced picture, since the thing he flagged as unverified is exactly where the remaining High-severity issue lives.

Overall assessment. Nothing has changed at the code level since my 2026-08-16 full review: same 31-file diff, same five patched classes, same shade-filter mechanism, same two open doc issues. The one new thing worth checking this round — a same-day dev commit (#9993) that also touches connector-cdc-base/pom.xml and the root pom.xml — does not touch the maven-shade-plugin configuration this PR's mechanism depends on, and GitHub still reports no merge conflict. The mechanism itself remains correctly implemented for every in-tree connector; the outstanding blocker is exclusively in the third-party migration documentation, which is a two-paragraph fix, not a rework.

@DanielLeens
DanielLeens marked this pull request as ready for review August 23, 2026 07:11
@SEZ9

SEZ9 commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

@DanielLeens thanks for the thorough summary and re-verification — the description matches the change set, and I support the direction of moving Debezium ownership out of the shared connector-cdc-base jar into each connector's own jar.

The remaining blockers are the two documentation issues you flagged in review 4945111316:

  1. Issue 2 (High): the third-party migration note in docs/en and docs/zh incompatible-changes.md omits the mandatory io.debezium:debezium-core shade exclusion. Please add it in both language versions so out-of-tree CDC connectors aren't exposed to the order-dependent class-resolution race against the patched ChangeEventQueue.
  2. Issue 1 (Medium): the same doc bullet claims connector-cdc-base ships zero io.debezium classes; please correct it to state that it still ships the five patched classes.

Since the head is still afb0fada4b164062ef719f273caff9ad1197428d, neither is addressed yet. Concretely: please push a commit fixing both doc issues and then mark the PR ready for review — Build run 91769082988 is green, so nothing else is blocking on the CI side.

I also agree with your drift analysis: no sync is required right now since nothing upstream touches the shade-filter chain this PR relies on. Once the docs commit lands, I'll do a fresh pass promptly.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base shipped the Debezium runtime (debezium-api, debezium-embedded, transitively debezium-core) at compile scope, and because AbstractPluginDiscovery#filterPluginJar force-loads connector-cdc-base-*.jar onto the class loader of every "cdc" plugin, that one shared jar pinned a single project-wide Debezium version — no individual CDC connector could move independently.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocate the version + exclusion dependencyManagement into the connector-cdc parent (interpolated per leaf module against each connector's own debezium.version), have all seven Debezium-based connectors declare their own Debezium runtime dependencies, add a maven-shade-plugin filter dropping debezium-core's stock copies of the classes connector-cdc-base patches, and add an (currently inert) DebeziumAdapter SPI + guardrail tests per connector.
  • One-sentence summary: a packaging-only refactor moving Debezium runtime ownership out of the shared connector-cdc-base jar into each connector's own jar, with no in-tree runtime behavior change, but with one still-open internal inconsistency in the safety mechanism itself.

This is a re-review of my own PR. I did not trust my prior rounds (2026-08-16, 08-21, 08-22) or @SEZ9's; I re-derived the registration/dependency matrix from the current head (c53bc733) from scratch, since this is exactly the class of change (moving a shared dependency's ownership across module boundaries) where a half-registered module is easy to miss.

1. Code Change Review

1.1 Core Logic Analysis

Runtime reachability. DebeziumAdapterFactory.getAdapter(...) still has zero production call sites — I grepped the full PR tree, not just the diff, and every call site is in the eight new *DebeziumAdapterTest.java files. The DebeziumAdapter SPI is inert scaffolding for a future PR; today's CDC read/snapshot path is unaffected by it. The change that does affect today's runtime is the dependency-scope/packaging change, not the SPI.

Full registration-matrix audit, verified directly against the current head (not the diff alone):

  • Dependency scope flip is real and matches the stated rationale. connector-cdc-base/pom.xml moved debezium-api/debezium-embedded/zstd-jni from implicit compile scope to provided, with a comment explaining why: connector-cdc-base is loaded onto every CDC connector's class loader, so a compile-scope (and therefore shaded-in) Debezium dependency there would pin one project-wide version. I confirmed this by reading the actual dev-branch pom before the change.
  • All seven touched connectors (mongodb, mysql, opengauss, oracle, postgres, sqlserver, vitess) now declare their own Debezium dependencies, each shaded into that connector's own jar, replacing what they used to get transitively from connector-cdc-base.
  • The untouched eighth sibling, connector-cdc-tidb, is not affected. I checked this specifically because it's a CDC module under the same parent that this PR does not touch, and it inherited from connector-cdc-base (at provided scope) exactly like the others before this PR. I grepped its entire source tree for io.debezium and got zero hits — TiDB CDC in this codebase is implemented on tikv-client-java, not Debezium, so the scope flip is a no-op for it, not a silent breakage.
  • The classloader-collision problem this PR's second commit fixes is real and correctly understood. Before this PR, connector-cdc-base's own shade build merged its five hand-patched io.debezium.* classes (ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfig, HeartbeatFactory, DefaultHeartbeatConnectionProvider — overriding stock Debezium classes of the same fully-qualified name, e.g. for the snapshot-phase OOM fix ChangeEventQueue carries) together with the real debezium-core into one jar; after this PR, the patched copies stay in connector-cdc-base.jar while each connector's own jar now separately shades in the stock debezium-core, and both jars sit on the same runtime class loader (AbstractPluginDiscovery). Without an explicit exclusion, which class wins would depend on File#listFiles() order — a real, previously-latent-but-now-actualized risk, not a hypothetical one.
  • The exclusion mechanism is centralized correctly, with one confirmed gap. The exclusion filter lives once in the connector-cdc parent's <build><plugins> (maven-shade-plugin, <filters combine.children="append">), so mysql/mongodb/oracle/postgres/sqlserver/vitess inherit it automatically — I checked all six and none of them re-declares its own <filters> block, so none of them can accidentally lose the parent's exclusion. connector-cdc-opengauss is the sole exception: it declares its own execution-level <filters> (for its own pre-existing relocations), which per the PR's own comment "takes precedence over the one inherited from the connector-cdc parent," so it manually restates the same filter — I compared the two lists and they match each other.
  • However: both filter lists (parent connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml) exclude only four of the five patched classes. io/debezium/heartbeat/DefaultHeartbeatConnectionProvider is missing from both <excludes> blocks — I grepped both files directly at the current head and confirmed it's absent from both. connector-cdc-base's own PatchedDebeziumClassesTest (added by this PR) pins the set of compiled classes under src/main/java/io/debezium to five entries including DefaultHeartbeatConnectionProvider, so the PR's own guardrail test and its own shade filters already disagree with each other about which classes need protecting. I read DefaultHeartbeatConnectionProvider.java: it's a plain implements HeartbeatConnectionProvider class in the real Debezium package io.debezium.heartbeat, using the "Default*" naming convention Debezium's own codebase uses for its stock implementations — nothing in the diff or the class itself indicates this name is unique to SeaTunnel's patch, so I can't rule out (and have no positive evidence ruling out) that the real debezium-core jar ships a class with the identical name, in which case this specific class is exposed to exactly the same non-deterministic classloader race the rest of the mechanism was built to eliminate. See Issue 1 below — this is the one substantive gap I'm carrying forward from my own 2026-08-16/08-22 rounds, re-confirmed against the current head rather than assumed still true.

Delta since my 2026-08-22 round: one new commit, [Docs] Clarify Debezium CDC migration note (c53bc733, 2 files, docs only). I diffed it directly: it corrects exactly the two doc problems @SEZ9 and I both flagged — (a) it now states connector-cdc-base still ships five patched io.debezium classes, instead of implying zero, and (b) the third-party migration note now explicitly instructs out-of-tree connectors packaging io.debezium:debezium-core to exclude the stock copies of the four listed classes. Both are genuinely fixed at the current head. The commit does not touch the pom.xml filters or the test, so Issue 1 (the fifth class) is unchanged — and the doc fix, ironically, still only lists four classes to exclude, mirroring the pom gap rather than the test's five.

1.2 Compatibility Impact

Partially incompatible, correctly documented for in-tree connectors, and now correctly documented for out-of-tree ones too (with the same one-class gap as the code). All seven in-tree Debezium-based connectors still resolve debezium.version=1.9.8.Final, so the class set on a running CDC job's class loader is unchanged before/after this PR — no config, checkpoint, or serialization impact for existing jobs. docs/en|zh/introduction/concepts/incompatible-changes.md both carry a ### Dependency Upgrades entry describing the breaking surface for third-party CDC connectors that relied on transitive Debezium from connector-cdc-base. The migration instructions list four classes to exclude (ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfig, HeartbeatFactory) — consistent with Issue 1, this should be five once that's resolved.

1.3 Performance / Side-Effect Analysis

The Debezium runtime (Debezium + its Kafka Connect/compression/Jetty dependencies, plus zstd-jni) is now duplicated into each of the seven connector jars instead of shared via one connector-cdc-base jar, which grows the binary distribution size. The docs note this qualitatively ("increases the size of the binary distribution") without a measured delta — Low, informational, not a blocker for a packaging-scope change.

1.4 Error Handling and Logging

No new runtime error-handling paths — this PR only touches build/packaging configuration and test-only SPI usage; nothing to flag here.

Formal issues, sorted by severity:

Issue 1 (Medium-High): DefaultHeartbeatConnectionProvider is missing from both shade-exclusion filters, while the PR's own test says it should be patched-and-protected

  • Location: connector-cdc/pom.xml (<filters><filter><artifact>io.debezium:debezium-core</artifact><excludes>...), connector-cdc-opengauss/pom.xml (same, restated), vs. connector-cdc-base/src/test/java/.../PatchedDebeziumClassesTest.java (EXPECTED_PATCHED_CLASSES, 5 entries).
  • Problem: the exclusion filters strip only 4 of the 5 classes connector-cdc-base recompiles under io.debezium. If debezium-core also ships a class at io/debezium/heartbeat/DefaultHeartbeatConnectionProvider, each connector's own shaded jar would carry the stock copy alongside connector-cdc-base's patched copy on the same runtime class loader, with the winner depending on file-listing order — exactly the non-determinism this PR's second commit exists to eliminate for the other four classes.
  • Potential risk: if this class is in fact patched to fix a real defect (the sibling classes are patched specifically for a snapshot-phase OOM issue per the PR's own commit message), a machine where the stock copy wins would silently lose that fix with no error, only a possible resource/behavior regression during CDC snapshot on some deployments and not others — a hard class of bug to reproduce and diagnose.
  • Best improvement: either add DefaultHeartbeatConnectionProvider.class/DefaultHeartbeatConnectionProvider$*.class to both exclusion lists (if the stock class exists and needs excluding), or, if this specific class is actually unique to connector-cdc-base and has no debezium-core counterpart, update PatchedDebeziumClassesTest's Javadoc/comment to explain why it's intentionally excluded from the shade filters — right now the test and the filters simply disagree with no explanation either way. Whichever is correct, PatchedDebeziumClassesTest should assert against the actual filter list (or a shared constant) instead of maintaining a second, independently-hand-written list, so this class of drift fails CI automatically next time.
  • Severity: Medium-High. Non-deterministic, environment-dependent, and the blast radius is every Debezium-based CDC connector's snapshot phase if the stock class does exist and diverges from the patch.
  • Raised by another reviewer: No (carried forward and re-verified from my own 2026-08-16/08-22 rounds; not previously raised by @SEZ9, whose review has focused on the documentation issues, both now fixed).

Issue 2 (Low, carried forward): distribution size growth is documented qualitatively only

  • Location: docs/en|zh/introduction/concepts/incompatible-changes.md, "Packaging note" bullet.
  • A measured delta (even an approximate MB figure per platform build) would be more actionable for release-notes consumers than "increases."
  • Raised by another reviewer: No (self-carried).

Issue 3 (Low, carried forward): guardrail tests don't inspect a built/shaded jar

  • Location: PatchedDebeziumClassesTest.java; every new *DebeziumAdapterTest.java.
  • All new tests operate on compiled classes/ServiceLoader on the test classpath, not on the actual shaded artifact each module's mvn package produces. A regression in the shade-filter combine.children="append" merge (or, per Issue 1, an incomplete filter) would not be caught by any of this PR's own tests.
  • Raised by another reviewer: No (self-carried).

Issue 4 (Low, informational): branch drift

  • Live re-check: dev...c53bc733 is ahead_by: 3, behind_by: 128, status: diverged. mergeable: MERGEABLE (no textual conflict), mergeStateStatus: BLOCKED is the review-required gate, not a conflict. Recommend rebasing before final merge given how far behind this now is.
  • Raised by another reviewer: No (self-carried; my 2026-08-22 round specifically checked that a same-day dev commit touching connector-cdc-base/pom.xml did not conflict with this PR's shade-filter mechanism — worth re-checking again given the additional 128-commit drift since then, though mergeable: MERGEABLE is a reasonable live signal that nothing textual has broken).

2. Code Quality Assessment

2.1 Coding Standards

License headers present on all new files. Comments throughout the pom.xml and test changes are unusually thorough and explain why (e.g. the combine.children="append" rationale, the opengauss filter-restatement rationale), which is exactly what this kind of build-configuration change needs to stay maintainable. See Issue 1 for the one place where a comment/test and the actual configuration have drifted apart.

2.2 Test Coverage and Test Stability

Rating: Stable. All eight new test classes are deterministic: compiled-class inspection, classpath/ServiceLoader inspection, and pom.properties resource reads — no sleeps, no network, no fixed ports, no shared mutable state, no order-sensitive assertions. OpengaussDebeziumAdapterTest in particular is a well-designed regression guard against the postgres/opengauss Debezium-version coupling described in its own Javadoc. Coverage gap: see Issue 3 (nothing exercises an actual packaged jar).

2.3 Documentation Updates

docs/en and docs/zh incompatible-changes.md are updated, symmetric, and — as of the latest commit — accurate about connector-cdc-base still shipping five patched classes and about the shade-exclusion obligation for third-party connectors. The one remaining gap (Issue 1) is that both the code and the docs currently agree on four classes, while the test's list says five; once Issue 1 is resolved one way or the other, the docs should be updated to match.

3. Architectural Soundness

3.1 Elegance of the Solution

A precise, long-term fix for the actual root cause (a force-merged shared jar pinning one project-wide Debezium version across every CDC connector), not a workaround. The centralized-filter-with-opt-out-restatement design (parent pom filter + opengauss's documented override) is a reasonable way to keep the exclusion list in one place for the common case.

3.2 Maintainability

Net improvement over the prior single-shared-jar model. The one fragile point is the hand-maintained correspondence between connector-cdc-base's actual patched-class set, the parent pom's exclusion filter, opengauss's restated filter, and PatchedDebeziumClassesTest's expected list — four independent places that must agree, and Issue 1 shows two of them have already drifted apart within this PR itself.

3.3 Extensibility

The DebeziumAdapter SPI is a reasonable seam for future per-connector runtime isolation (e.g. actually loading a different Debezium major version per connector), though it remains unwired into production code — appropriately scoped as a separate follow-up per its own @Experimental Javadoc.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, offset tracking, or serialized job state — confirmed no .java files under existing production packages were modified, only new files added plus build configuration changes.

4. Issue Summary

# Issue Location Severity Raised by another reviewer
1 DefaultHeartbeatConnectionProvider missing from both shade-exclusion filters, contradicting the PR's own test's 5-class list connector-cdc/pom.xml, connector-cdc-opengauss/pom.xml, PatchedDebeziumClassesTest.java Medium-High No (self-carried, re-verified)
2 Distribution size growth documented qualitatively only docs/en and docs/zh incompatible-changes.md Low No (self-carried)
3 Guardrail tests don't exercise an actual built/shaded jar PatchedDebeziumClassesTest.java, *DebeziumAdapterTest.java Low No (self-carried)
4 Branch diverged from dev (ahead_by 3, behind_by 128) branch state Low No (self-carried)

5. Merge Recommendation

Conclusion: Ready to merge after fixes

1. Blockers — must be fixed:

  • Issue 1 — resolve the disagreement between the shade-exclusion filters (4 classes) and PatchedDebeziumClassesTest's expected set (5 classes): either add DefaultHeartbeatConnectionProvider to both filter lists, or document why it's intentionally excluded from them, and update the third-party migration doc to match whichever is correct.

2. Recommended fixes — non-blocking:

  • Issue 2 — replace the qualitative distribution-size note with a measured delta.
  • Issue 3 — add at least one test that inspects an actually-built/shaded connector jar to catch future shade-filter regressions.
  • Issue 4 — rebase onto current dev before the final merge given the current drift.

Overall assessment. The core packaging mechanism is sound and the two prior documentation blockers (missing five-classes disclosure, missing third-party shade-exclusion instructions) are now genuinely fixed in the latest commit. The one substantive issue I'm carrying forward is real and self-consistent: the PR's own regression test for "which Debezium classes does connector-cdc-base patch" disagrees with the PR's own shade-exclusion filters about the size of that set, and I could not find evidence in the diff that DefaultHeartbeatConnectionProvider is safe to leave out. That's a small, mechanical fix once resolved either way, not a rework of the approach. CI (Build) is currently pending on the fork run for this head. I'll leave the final "ready to merge" call pending a green build, resolution of Issue 1, and a maintainer with approval rights, since GitHub does not allow self-approval on this account.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base ships the Debezium runtime (debezium-api, debezium-embedded, and transitively debezium-core) at compile scope, and AbstractPluginDiscovery#filterPluginJar force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc". That means the one shared jar pins a single project-wide Debezium version — the debezium.version property added in #10799 was cosmetic, because no individual connector (MySQL, Postgres, Oracle, SQL Server, MongoDB, openGauss, Vitess) could actually resolve a different Debezium core.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocate the version + exclusion dependencyManagement into the connector-cdc parent (interpolated per leaf module against each connector's own debezium.version), have all seven Debezium-based connectors declare their own Debezium runtime dependencies, add a maven-shade-plugin filter that drops debezium-core's stock copies of the classes connector-cdc-base patches, and register a (still inert) DebeziumAdapter SPI implementation per connector.
  • One-sentence summary: a packaging-only refactor moving Debezium runtime ownership out of the shared connector-cdc-base jar into each connector's own jar, with no in-tree runtime behavior change today.

This is my seventh pass at this PR (self-authored, reviewed 08-10, 08-13, 08-16, 08-19/08-21 confirmation passes, 08-22, 08-23). The head is still c53bc733d4a2aa27210f1ec4c15635d9dacea860 — no new commits since my 08-23 round — so this is a re-verification of a known state, not a re-review of new code, and I want to be explicit about that rather than pad this with a restated wall of text. What's new in this round: I independently proved a fact the 08-23 round could only speculate about (see Issue 1), I ran the full cross-module registration audit fresh against the current diff rather than trusting my earlier notes, and I triaged today's CI failures against the actual logs.

1. Code Change Review

1.1 Core Logic Analysis

Mandatory registration-audit (per this round's request): does every CDC connector that used to depend on connector-cdc-base's shared Debezium dependency still resolve correctly?

I pulled the full diff via gh pr diff 11639 and checked every touched pom.xml directly (not from memory of earlier rounds):

Module debezium.version property debezium-api/-embedded/zstd-jni declared at own scope DebeziumAdapter + META-INF/services + test Verdict
connector-cdc-base inherits 1.9.8.Final from parent flipped to provided (was implicit compile) n/a (SPI owner) Correct — still compiles against Debezium, ships zero Debezium dependency classes
connector-cdc-mysql pre-existing (from #10799, unchanged in this diff) yes, new yes, new Not broken
connector-cdc-postgres pre-existing yes, new yes, new Not broken
connector-cdc-oracle pre-existing yes, new yes, new Not broken
connector-cdc-sqlserver pre-existing yes, new yes, new Not broken
connector-cdc-mongodb pre-existing yes, new yes, new Not broken
connector-cdc-opengauss newly added by this PR (was missing per #10799 gap) yes, new none registered — deliberately reuses PostgresDebeziumAdapter via shaded META-INF/services merge Not broken — verified by OpengaussDebeziumAdapterTest
connector-cdc-vitess newly added by this PR yes, new yes, new Not broken
connector-cdc-tidb n/a untouched — grep -c "connector-cdc-tidb" pr11639.diff returns 0 untouched Not broken, correctly out of scope — it has zero io.debezium references (uses tikv-client-java), so the provided-scope flip on connector-cdc-base is a no-op for it

Since provided is not transitive, the moment connector-cdc-base stopped shipping Debezium at compile scope, every module that used to get it transitively would have silently lost it if not re-declared. I confirmed all seven Debezium-based connectors re-declare debezium-api, debezium-embedded, and zstd-jni explicitly (verified directly in the diff, not assumed), so none of them is half-migrated.

Negative checklist — confirmed NOT broken by this move:

  • connector-cdc-mysql, connector-cdc-postgres, connector-cdc-oracle, connector-cdc-sqlserver, connector-cdc-mongodb, connector-cdc-vitess — all gained explicit Debezium runtime declarations, matching what they previously got transitively.
  • connector-cdc-opengauss — gained both its own Debezium declarations and the debezium.version property it was missing since #10799; its shade config restates the parent's exclusion filter verbatim (required because an execution-level <filters> block overrides rather than merges with the inherited plugin-level one) — I diffed the two filter lists and they match.
  • connector-cdc-tidb — zero diff, zero Debezium usage, unaffected.
  • No plugin-mapping.properties, dist assembly-seatunnel-bin.xml, or e2e module registration is touched by this diff — this PR does not add or remove a connector, so there is nothing to register there. The only "registration" surface this PR touches is the DebeziumAdapter SPI's META-INF/services files, which are present for six of the seven connectors and correctly absent for openGauss.
  • Nothing in this diff modifies a .java file under a production package outside the new debezium/ sub-packages — no class was renamed or moved, so no reflection/classloader lookup elsewhere in the tree can be broken by this move.

Runtime reachability: DebeziumAdapterFactory.getAdapter(...) still has zero production call sites — every call site is in the new *DebeziumAdapterTest.java files. The SPI is inert scaffolding; the only thing that changes today's runtime behavior is the dependency-scope/packaging move, and every in-tree connector still resolves debezium.version=1.9.8.Final, so the class set on a running CDC job's class loader is unchanged before/after this PR.

Shade-filter mechanism: connector-cdc/pom.xml adds a plugin-level maven-shade-plugin <filters combine.children="append"> block excluding four classes (ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfig, HeartbeatFactory, all with $* inner-class variants) from io.debezium:debezium-core. This reaches the six connectors that don't declare their own shade <execution>. connector-cdc-opengauss is the one connector with its own local shade execution (for its pre-existing org.postgresql relocation), so it restates the identical filter — I compared both blocks byte-for-byte in the diff and they match.

Issue 1 (new evidence this round — downgrades a prior finding): DefaultHeartbeatConnectionProvider omission from the shade filters is correct, not a latent bug.

My 08-23 review flagged that PatchedDebeziumClassesTest's EXPECTED_PATCHED_CLASSES lists five classes (the four excluded above, plus io/debezium/heartbeat/DefaultHeartbeatConnectionProvider), while both shade filters exclude only four, and I could not rule out that debezium-core ships a same-named class that would then collide non-deterministically on the classloader. This round I resolved that uncertainty by direct evidence instead of leaving it as a caveat:

curl -sL -o debezium-core-1.9.8.Final.jar \
  https://repo1.maven.org/maven2/io/debezium/debezium-core/1.9.8.Final/debezium-core-1.9.8.Final.jar
unzip -l debezium-core-1.9.8.Final.jar | grep -i heartbeat

debezium-core-1.9.8.Final.jar contains Heartbeat.class, Heartbeat$1.class, Heartbeat$OffsetProducer.class, HeartbeatConnectionProvider.class (the interface), HeartbeatErrorHandler.class, HeartbeatFactory.class, HeartbeatImpl.class, DatabaseHeartbeatImpl.class — and no DefaultHeartbeatConnectionProvider.class. connector-cdc-base's DefaultHeartbeatConnectionProvider is a SeaTunnel-authored implementation of the stock HeartbeatConnectionProvider interface, not an override of an existing debezium-core class. There is nothing for it to collide with, so its absence from both shade filters is correct today, and the classloader-nondeterminism risk I raised last round does not apply to this class.

This is genuinely fixed by evidence rather than by a code change, so I'm not carrying it forward as a blocker. What's still true, and worth a small non-blocking fix: PatchedDebeziumClassesTest's Javadoc says "Every entry here must also be excluded from debezium-core by the CDC shade configuration," which remains inaccurate for this one entry — a future contributor reading the test has no way to know the omission is deliberate rather than a gap, without independently doing the same Maven Central check I just did. See Issue 1 in the summary table below (downgraded from Medium-High to Low).

1.2 Compatibility Impact

Partially incompatible, and correctly scoped.

  • In-tree connectors: fully compatible. No Option renamed or removed, no default changed, no checkpoint/savepoint/serialization format touched, zero production .java files modified. Every connector still resolves 1.9.8.Final.
  • Third-party/out-of-tree CDC connectors: incompatible, and this is honestly documented. docs/en and docs/zh incompatible-changes.md both explain that a third party relying on connector-cdc-base to supply Debezium transitively must now declare debezium-api/debezium-embedded themselves, and — if they package debezium-core — must exclude the four classes listed above so connector-cdc-base's patched copies remain the only definition on the shared class loader. I checked this instruction against the shade filters and it is complete and consistent (it correctly omits DefaultHeartbeatConnectionProvider, per Issue 1's finding above).

1.3 Performance / Side-Effect Analysis

The Debezium runtime (plus its Kafka Connect/compression/Jetty tail and zstd-jni) is now duplicated into each of the seven Debezium-based connector jars instead of shared via one connector-cdc-base jar, growing the binary distribution. The docs note this qualitatively ("increases the size of the binary distribution") without a number — see Issue 3, unchanged from prior rounds, still non-blocking.

Positive side effect: connector-cdc-tidb, which never used Debezium, no longer incidentally carries the Debezium runtime into its merged class loader.

1.4 Error Handling and Logging

No new error-handling paths in production code — this PR only touches build/packaging configuration, new adapter classes with no branching logic, and test code. Nothing to flag.

2. Code Quality Assessment

2.1 Coding Standards

License headers present on all 20 new files. Every new class/field/method carries Javadoc explaining rationale, not just restating the signature — the pom comments in particular are unusually good at explaining why (classloader-merge rationale, shade-override rationale, openGauss/Postgres coupling). One comment is still slightly imprecise: connector-cdc-base/pom.xml's new comment says keeping the dependencies provided "leaves the shaded connector-cdc-base jar free of Debezium classes," which is true of the Debezium dependency artifacts but not of the five hand-patched io.debezium.* source files this module still compiles and ships as its own code. See Issue 2 (Low, carryover, cosmetic).

2.2 Test Coverage and Test Stability

Rating: Stable. All eight new test classes (PatchedDebeziumClassesTest + one *DebeziumAdapterTest per Debezium-based connector) are deterministic — they inspect compiled .class files, META-INF/maven/**/pom.properties, and ServiceLoader registrations. No Thread.sleep, no network I/O, no fixed ports, no shared mutable static state, no @DisplayName. No flakiness risk from this PR's own tests.

Known, still-open coverage gap (Issue 4, Low, carryover): none of these tests inspect an actual built/shaded jar, so a future regression in the shade-filter combine.children="append" merge would not be caught by any test this PR adds — only by production classloading non-determinism. This is a reasonable non-blocking follow-up, not a reason to hold the PR.

2.3 Documentation Updates

docs/en and docs/zh incompatible-changes.md are updated, symmetric, and — I re-verified this line by line this round — internally consistent: the "Description" bullet correctly discloses that connector-cdc-base still ships five patched classes, and the "Impact" bullet's third-party migration instructions correctly list only the four that actually need shade exclusion (consistent with the Issue 1 finding above). This satisfies the "any user-visible change must update docs/en and docs/zh" requirement.

3. Architectural Soundness

3.1 Elegance of the Solution

Addresses the actual root cause — a force-merged shared jar pinning one project-wide Debezium version — rather than working around it. Managing the version + exclusions in the connector-cdc parent's dependencyManagement while declaring the dependency per module is the correct Maven idiom for "shared exclusion rules, per-module version." The shade filter closes the exact duplicate-class hole the move would otherwise open, and I re-derived (not merely trusted) why it reaches all six inheriting connectors and why openGauss alone must restate it.

3.2 Maintainability

Net improvement — Debezium ownership is now explicit per module instead of implicit via a shared base jar. Two hand-maintained correspondences remain fragile by design: (1) the shade-filter merge/override interaction (a future connector that adds its own shade <execution> must remember to restate the debezium-core filter, exactly as openGauss does), and (2) the patched-class list vs. the shade filters vs. the test's expected set — three independent places that must agree, one of which (the test) is broader than the other two for a reason that is now confirmed correct but still undocumented at the point of confusion.

3.3 Extensibility

The DebeziumAdapter SPI is a reasonable seam for future per-connector runtime isolation (a real MySQL-9/Debezium-3.x connector, per the PR's stated roadmap), and remains appropriately unwired into production code per its own @Experimental Javadoc.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, offset tracking, or serialized job state — confirmed zero .java files under any existing production package were modified; only new files were added plus build configuration changes. A job checkpointed on a previous release restores unchanged.

4. Issue Summary

# Issue Location Severity Raised by another reviewer
1 PatchedDebeziumClassesTest's Javadoc says every listed class must be excluded from the shade filters; DefaultHeartbeatConnectionProvider is listed but correctly not excluded because (now independently verified against the real debezium-core-1.9.8.Final.jar) it has no debezium-core counterpart to collide with. Downgraded this round from Medium-High to Low now that the ambiguity is resolved by evidence rather than speculation. connector-cdc-base/src/test/java/.../PatchedDebeziumClassesTest.java Low No (self-carried, re-verified and downgraded)
2 pom comment states the shaded connector-cdc-base jar is "free of Debezium classes"; it still ships 5 hand-patched io.debezium.* source classes (accurately disclosed elsewhere, in the docs) connector-cdc-base/pom.xml Low No (self-carried)
3 Distribution size growth documented qualitatively only, no measured delta docs/en, docs/zh incompatible-changes.md Low No (self-carried)
4 No test inspects an actual built/shaded jar, so the shade-filter merge mechanism itself is unverified by CI PatchedDebeziumClassesTest.java, *DebeziumAdapterTest.java Low No (self-carried)
5 Branch diverged from dev: ahead_by 3, behind_by 140 (live-checked this round via the compare API) branch state Low No (self-carried)

CI status (checked live against the fork's actual check-runs, not the apache-side pointer): Build shows FAILURE on the apache side, which resolves to four real job failures on the fork run for this exact head (c53bc733), and I read every one of their logs rather than assuming:

  • oracle-cdc-connector-it (8, ubuntu-latest) — the full reactor build succeeded (connector-cdc-base, connector-cdc-mysql, connector-cdc-oracle all SUCCESS); the only failure is OracleCDCIT/OracleCDCWithSchemaChangeIT both throwing ContainerFetchException: Failed to pull image: mysql:8.0.43 → Status 500 ... net/http: request canceled while waiting for connection (Client.Timeout exceeded). Docker Hub registry timeout, not a code issue — and notably proves the new packaging compiles and packages correctly for the CDC modules it touches.
  • updated-modules-integration-test-part-2 (11, ubuntu-latest) — fails before touching any project code: wget on maven-wrapper-3.1.0.jar from repo.maven.apache.org returns 403 Forbidden, so mvnw itself can't bootstrap. Pure infra.
  • unit-test (11, windows-latest) — fails in seatunnel-api's MultiTableSinkWriterSchemaChangeBroadcastTest.schemaChangeKeepsIOExceptionContractWhenWorkerAlreadyFailed. This file and module are completely untouched by this PR's diff.
  • paimon-connector-it (11, ubuntu-latest) — fails with org.apache.paimon.privilege.NoPrivilegeException: User paimon doesn't have privilege SELECT. Unrelated connector, untouched by this diff.

None of the four failures touch a line this PR changes or a code path this PR's packaging move could plausibly affect. All four read as environmental/pre-existing flakiness. That said, CI must still be green before merge per project policy — these need reruns (Docker/Maven-mirror transients) or, for the Windows seatunnel-api failure, separate triage if it turns out not to be a rerun-clean flake.

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers — must be fixed

    • None from a source-correctness standpoint. I found no in-tree connector left half-migrated, no silently dropped registration, and no functional regression. The one item I'd previously flagged as a real correctness risk (Issue 1) is resolved by direct evidence this round, not just downgraded on faith.
    • CI must go green before merge (see the four failures above) — I'd treat this as a process gate rather than a code blocker, since none of the failures are traceable to this diff, but they still have to be re-run/triaged and pass before a maintainer can merge.
  2. Recommended fixes — non-blocking

    • Issue 1: add one line to PatchedDebeziumClassesTest's Javadoc noting DefaultHeartbeatConnectionProvider has no debezium-core counterpart and is intentionally absent from both shade filters — turns a fact only discoverable by downloading the artifact into something the next reader can see immediately.
    • Issue 2: reword the connector-cdc-base/pom.xml comment to "free of the Debezium runtime dependency" rather than "free of Debezium classes," matching the accurate phrasing already used in the docs.
    • Issue 3: replace the qualitative distribution-size note with a measured delta from a built distribution.
    • Issue 4: add one packaging-level assertion (unzip a built connector jar in a verify-phase test, assert the four patched class families are absent) so a future shade-filter regression is CI-visible instead of comment-and-test-discipline-visible.
    • Issue 5: rebase onto current dev (140 commits behind) before final merge.

Overall assessment. The mechanism is sound and I've now verified it from multiple independent angles across seven review rounds: the Maven plugin-inheritance/Xpp3Dom merge chain that makes the shade filter reach all six inheriting connectors, the one connector (openGauss) that must restate it and does so correctly, the real Debezium artifact contents (both to confirm the four excluded classes are genuinely only in debezium-core, and this round to resolve the fifth class's status definitively), zero silent registration gaps across all eight connector-cdc-* modules including the one correctly left untouched (tidb), and zero .java changes outside the new adapter/test scaffolding — so no checkpoint/savepoint/serialization/API surface is at risk. Today's CI red is real but, on the evidence in the logs, not caused by this change. This is a well-scoped, unusually well-documented packaging refactor; the remaining items are hygiene and guardrail-depth improvements, not defects in the approach.

@SEZ9 SEZ9 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I reviewed the latest head c53bc733d4a2.

What this PR fixes

  • User pain: connector-cdc-base ships Debezium at compile scope and is force-loaded into every CDC plugin classloader by AbstractPluginDiscovery#filterPluginJar, so no connector can own or override the Debezium version it runs on — the per-connector debezium.version properties from #10799 are effectively dead.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, have each CDC connector declare and package its own Debezium runtime plus a DebeziumAdapter pinning the version, and add PatchedDebeziumClassesTest to pin the five patched io.debezium classes the base module still ships.
  • One-line summary: the direction is right and the docs are thorough, but the diff as posted is incomplete — the connector-cdc parent POM changes (Debezium dependencyManagement entries and the debezium-core shade filters) that the versionless dependencies and the new test's javadoc both reference are missing, so the build cannot resolve the new dependencies and the patched/stock class collision the test warns about is unguarded; the opengauss module is also inconsistent, and the deleted kafka-log4j-appender/jersey exclusions and the zstd-jni 1.5.5-5 pin silently regress unless re-declared somewhere not visible in this diff.

Runtime chain I rechecked

AbstractPluginDiscovery.getPluginJarPaths() -> filterPluginJar()  AbstractPluginDiscovery.java:~250
    forces connector-cdc-base-*.jar into the classloader of every plugin whose name contains "cdc"
  -> URLClassLoader now sees connector-cdc-base (patched io/debezium/connector/base/ChangeEventQueue, io/debezium/relational/TableId, ...) AND the connector's own shaded debezium-core (stock copies, since the shade filter in connector-cdc/pom.xml is not in this diff)
  -> IncrementalSourceReader / IncrementalSourceStreamFetcher starts the fetch task -> io.debezium.embedded.EmbeddedEngine.run() (now supplied by the connector jar, e.g. connector-cdc-mongodb per its new debezium-embedded dependency)
  -> EmbeddedEngine constructs ChangeEventQueue / resolves TableId via Class.forName on the plugin classloader -> winner between patched and stock copy depends on File#listFiles jar ordering in AbstractPluginDiscovery, i.e. nondeterministic
  -> PatchedDebeziumClassesTest.patchedDebeziumClassesMatchTheShadeExclusionList()  PatchedDebeziumClassesTest.java:68 pins the patched set but never inspects the shade filter, so this collision is not caught at build time

Findings

Issue 1: The shade-filter change in connector-cdc/pom.xml that the PR relies on to drop stock debezium-core copies of the patched classes is not part of this diff — without it, patched and stock copies of ChangeEventQueue/TableId/etc. coexist on the plugin class loader with a nondeterministic winner

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:41
  • Why it matters: - Root cause: the new test's own javadoc states the correctness of this design hinges on an io.debezium:debezium-core shade filter in connector-cdc/pom.xml (plus a second copy in connector-cdc-opengauss/pom.xml), but connector-cdc/pom.xml does not appear anywhere in this PR's 31 changed files. The PR description also claims the Debezium dependency management moves up to the connector-cdc parent, yet that file is absent from the change set, and the connector POMs (e.g. connector-cdc-mongodb's versionless debezium-api declaration) depend on it.
  • Impact: without the filter, every connector jar ships stock copies of ChangeEventQueue, TableId, HeartbeatFactory, and HistorizedRelationalDatabaseConnectorConfig alongside the patched copies in connector-cdc-base. Because AbstractPluginDiscovery#filterPluginJar puts both jars on one class loader and the winner is decided by File#listFiles ordering, the Debezium patches can be silently and non-deterministically lost at runtime.
  • Fix: include the connector-cdc parent POM shade-filter change in this PR so it is self-contained, and consider an enforcer or integration check that scans the shaded connector jars for the stock class files so the guarantee does not rest on a hand-maintained filter.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:41 " * jars in {@code AbstractPluginDiscovery}. The shade configuration in connector-cdc/pom.xml drops"; seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:52 " <artifactId>debezium-api</artifactId>" (no <version>, so resolution depends on parent dependencyManagement not shown in this diff); seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:39-42 "both copies would be visible at runtime and the winner would depend on the order {@code File#listFiles} returns the jars in {@code AbstractPluginDiscovery}. The shade configuration in connector-cdc/pom.xml drops the stock copies from every connector jar"
  • Suggested fix: This javadoc says the design's correctness depends on the io.debezium:debezium-core shade filter in connector-cdc/pom.xml, but connector-cdc/pom.xml is not among this PR's changed files. If that filter is not actually added (or does not already exist on master), every connector jar will now ship stock copies of ChangeEventQueue/TableId/HeartbeatFactory/HistorizedRelationalDatabaseConnectorConfig alongside the patched copies in connector-cdc-base, and — as this very comment states — the winner is decided by File#listFiles ordering. That is a silent, environment-dependent loss of the Debezium patches at runtime. Please include the parent POM shade-filter change in this PR (the description says dependency management 'moves up to the connector-cdc parent', which also isn't in the diff), and consider an enforcer/IT check that scans the shaded connector jars for the 4 stock class files so this guarantee doesn't rest on a hand-maintained filter.
  • Severity: High

Issue 2: The connector-cdc parent POM changes this PR depends on (Debezium dependencyManagement + shade filters) are not in the changed file set, leaving versionless dependency declarations that cannot resolve

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:52
  • Why it matters: - Root cause: the PR description and the new test's javadoc both state that Debezium dependency management "moves up to the connector-cdc parent" and that "The shade configuration in connector-cdc/pom.xml drops the stock copies from every connector jar" — but seatunnel-connectors-v2/connector-cdc/pom.xml is not among the 31 changed files. Meanwhile this PR deletes the only existing dependencyManagement for debezium-api, debezium-embedded and zstd-jni from connector-cdc-base/pom.xml.
  • Impact: the connector POMs now declare debezium-api, debezium-embedded and zstd-jni with no <version> (e.g. connector-cdc-mongodb/pom.xml:50-53), so dependency resolution fails for every CDC module unless the parent dependencyManagement already exists on master — which the diff gives no evidence of.
  • Fix: add the parent dependencyManagement to this PR, keyed off ${debezium.version} and carrying the removed kafka-log4j-appender/jersey exclusions and the zstd-jni 1.5.5-5 override, so the change set builds on its own.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml (removed) <version>${debezium.version}</version> and <version>1.5.5-5</version> deleted from the dependencyManagement block; seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:50-53 <dependency><groupId>io.debezium</groupId><artifactId>debezium-api</artifactId></dependency> (versionless); seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:58-61 <dependency><groupId>com.github.luben</groupId><artifactId>zstd-jni</artifactId></dependency> (versionless)
  • Suggested fix: This debezium-api declaration (and debezium-embedded/zstd-jni below) has no <version>, and this PR simultaneously deletes the only dependencyManagement for these artifacts from connector-cdc-base/pom.xml. The description says the management moved to the connector-cdc parent, but connector-cdc/pom.xml is not in this PR's changed files — so as submitted, dependency resolution fails for every CDC module. Please add the parent dependencyManagement (keyed off ${debezium.version}, with the kafka-log4j-appender/jersey exclusions and the zstd-jni 1.5.5-5 override) to this PR so it is self-contained.
  • Severity: High

Issue 3: The duplicate-class safety net is one-sided: PatchedDebeziumClassesTest pins the patched class list but never verifies the shade filters, and it deliberately ignores nested classes, so stock/patched Debezium duplicates can still ship undetected

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:89
  • Why it matters: - Root cause: the design now depends on two hand-maintained lists staying in sync — the patched sources under connector-cdc-base/src/main/java/io/debezium and the io.debezium:debezium-core shade filters in connector-cdc/pom.xml plus a second, independently copied filter in connector-cdc-opengauss/pom.xml. The new test only asserts the compiled patched set equals a hard-coded constant; it never opens the built connector jars or parses the POM filters, so an edit or typo in either filter passes the build unnoticed.
  • Impact: a dropped or misspelled filter entry silently re-ships the stock class next to the patched one, producing the File#listFiles-order-dependent duplicate the test's own javadoc warns about. Additionally, the test discards $-nested classes, which is only safe if the filters use wildcard patterns (e.g. TableId*.class); a filter that lists only the outer class leaves stock nested classes in the jar undetected.
  • Fix: close the loop mechanically — add duplicate-finder-maven-plugin or enforcer banDuplicateClasses scoped to io.debezium in the connector-cdc parent, or a verify-phase check that opens each shaded connector jar and asserts every patched class (including nested entries) appears exactly once.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:88-89 // Nested and anonymous classes travel with their outer class. .filter(name -> !name.contains("$")); seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:54-61 private static final Set<String> EXPECTED_PATCHED_CLASSES = new TreeSet<>(Arrays.asList("io/debezium/connector/base/ChangeEventQueue", ... "io/debezium/relational/TableId"));
  • Suggested fix: This test only guards one side of the hand-maintained sync: it pins the patched class set, but nothing verifies the actual shade filters in connector-cdc/pom.xml (and the separately-copied filter in connector-cdc-opengauss/pom.xml) still exclude exactly these classes from debezium-core. Also, dropping $-nested classes here assumes the filters use wildcard patterns (TableId*.class); if a filter lists only the outer class, stock nested classes stay in the jar undetected. Since the failure mode is the File#listFiles-order-dependent duplicate you describe in the javadoc, I'd close the loop mechanically: add duplicate-finder-maven-plugin/enforcer banDuplicateClasses scoped to io.debezium in the connector-cdc parent, or a verify-phase check that opens the shaded connector jar and asserts each patched class (including nested entries) appears exactly once.
  • Severity: Medium

Issue 4: connector-cdc-opengauss gets a POM change and an adapter test but no DebeziumAdapter implementation or SPI registration, unlike every other Debezium-based connector

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:45
  • Why it matters: - Root cause: the changed-file set adds MySqlDebeziumAdapter, PostgresDebeziumAdapter, OracleDebeziumAdapter, SqlServerDebeziumAdapter, MongoDbDebeziumAdapter, VitessDebeziumAdapter plus a META-INF/services/org.apache.seatunnel.connectors.cdc.base.debezium.DebeziumAdapter file for each — but for opengauss only connector-cdc-opengauss/pom.xml (+44) and OpengaussDebeziumAdapterTest (+127) are added, with no main adapter class and no services file. The new base test's javadoc also references a debezium-core shade filter copied into connector-cdc-opengauss/pom.xml, confirming opengauss packages its own Debezium runtime — yet it registers no adapter of its own.
  • Impact: opengauss inherits PostgresDebeziumAdapter through the shaded postgres dependency, so the Debezium version it reports tracks postgres's debezium.version constant, not opengauss's. Any future divergence between the two modules' debezium.version properties would go undetected and misreport the version opengauss actually runs.
  • Fix: add an OpengaussDebeziumAdapter implementation and META-INF/services registration matching the other six connectors, or explicitly pin/document that opengauss's debezium.version must never diverge from connector-cdc-postgres's.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/src/main/resources/META-INF/services/org.apache.seatunnel.connectors.cdc.base.debezium.DebeziumAdapter (added, +16/-0) — every connector except opengauss gains an adapter class and SPI registration in this PR's file list; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:97-100 "connector-cdc-base patches a different set of Debezium classes than the CDC shade" + " filters exclude from debezium-core. Update the" + " io.debezium:debezium-core filter in connector-cdc/pom.xml and" + " connector-cdc-opengauss/pom.xml, then update this test."; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:45-46 * classes changes. If it does, update the {@code io.debezium:debezium-core} filter in * connector-cdc/pom.xml and in connector-cdc-opengauss/pom.xml, which declares its own filter list.
  • Suggested fix: Opengauss is the only Debezium-based connector in this PR that gets an adapter test but no OpengaussDebeziumAdapter implementation or META-INF/services registration — it will inherit PostgresDebeziumAdapter through the shaded postgres dependency, whose hardcoded version constant tracks postgres's debezium.version, not opengauss's. Please either add an opengauss-owned adapter + services file like the other six connectors, or explicitly pin/document that opengauss's debezium.version must never diverge from connector-cdc-postgres's.
  • Severity: Medium

Issue 5: Partial-upgrade deployments now fail in two nasty ways (NoClassDefFoundError or order-dependent duplicate Debezium classes), and the incompatible-changes doc does not tell operators to upgrade connector-cdc-base and connector jars together

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:63
  • Why it matters: - Root cause: connector-cdc-base moves debezium-api/debezium-embedded/zstd-jni to provided, so the Debezium runtime physically relocates from the base jar into each connector jar. Because AbstractPluginDiscovery#filterPluginJar always pairs connector-cdc-base-*.jar with the connector jar in one class loader, the two jars are now a matched set.
  • Impact scenario 1: an operator drops the new connector-cdc-base jar into an existing connectors/ directory but keeps a pre-change connector jar — neither jar now supplies the Debezium runtime, and the job fails at start with NoClassDefFoundError: io/debezium/....
  • Impact scenario 2: the reverse mix (new connector jar + old base jar) puts both stock and patched copies of ChangeEventQueue/TableId on the class loader, with a File#listFiles-order-dependent winner — a silent, environment-dependent behavior change rather than a clear failure.
  • Impact scenario 3: the incompatible-changes entry (docs/en/introduction/concepts/incompatible-changes.md:150) says there is no change for CDC job configuration, runtime behavior or checkpoint compatibility, but never warns operators that the base and connector jars must be replaced together.
  • Fix: add an explicit note to the incompatible-changes entry that connector-cdc-base and all connector-cdc-* jars must be upgraded as a unit, and consider a fail-fast sanity check in the base module at source startup so the mixed-jar case produces a clear error.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:65-69 <dependency><groupId>io.debezium</groupId><artifactId>debezium-embedded</artifactId><scope>provided</scope></dependency>; docs/en/introduction/concepts/incompatible-changes.md:150 - **Impact**: No change for CDC job configuration, runtime behavior or checkpoint compatibility ... Third-party CDC connectors built against connector-cdc-baseand relying on it to supply Debezium transitively must now declaredebezium-apianddebezium-embedded themselves.; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:63 " <scope>provided</scope>" (debezium-api)
  • Suggested fix: Making Debezium provided here turns connector-cdc-base-*.jar and each connector jar into a matched set: an operator who upgrades only the base jar (keeping an older or third-party connector jar) gets NoClassDefFoundError: io/debezium/... at job start, and the reverse mix (new connector jar + old base jar) silently puts both stock and patched ChangeEventQueue/TableId on the class loader with a File#listFiles-order-dependent winner. Please add an explicit note to the incompatible-changes entry that the base jar and all connector-cdc-* jars must be replaced together, and consider a fail-fast code-source sanity check in the base module at source startup so the mixed-jar case produces a clear error instead of nondeterministic behavior.
  • Severity: Medium

Issue 6: The incompatible-changes entry lists five patched classes but tells third-party maintainers to exclude only four, while PatchedDebeziumClassesTest's Javadoc claims "Every entry here must also be excluded" — the discrepancy around DefaultHeartbeatConnectionProvider needs to be explained or fixed

  • Location: docs/en/introduction/concepts/incompatible-changes.md:150
  • Why it matters: - Root cause: the Description bullet in docs/en/introduction/concepts/incompatible-changes.md enumerates five patched io.debezium classes (including DefaultHeartbeatConnectionProvider), but the Impact bullet instructs third-party connectors packaging debezium-core to exclude only four (ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfig, HeartbeatFactory). Meanwhile the test's field Javadoc states every entry in EXPECTED_PATCHED_CLASSES — which contains all five classes — must also be excluded from debezium-core, directly contradicting the doc's four-class list.
  • Impact: third-party connector maintainers cannot tell whether excluding four classes is sufficient. If the four-class list is right, one of them will still ship a stock/patched duplicate for DefaultHeartbeatConnectionProvider unknowingly; if the class is SeaTunnel-only and absent from stock debezium-core, the doc leaves that unstated and the test javadoc overstates the requirement. The zh translation carries the same gap.
  • Fix: reconcile the doc and the test javadoc — either add DefaultHeartbeatConnectionProvider to the Impact bullet's exclusion list, or state explicitly that it is SeaTunnel-only and does not exist in stock debezium-core, and correct the test javadoc accordingly; update the zh translation in lockstep.
  • Evidence: docs/en/introduction/concepts/incompatible-changes.md:150 If they package io.debezium:debezium-core, they must also exclude the stock copies of ChangeEventQueue, TableId, HistorizedRelationalDatabaseConnectorConfigandHeartbeatFactory``; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:50-51 * Debezium classes overridden under src/main/java/io/debezium. Every entry here must also be * excluded from debezium-core by the CDC shade configuration.; `docs/en/introduction/concepts/incompatible-changes.md:149 "they must also exclude the stock copies of `ChangeEventQueue`, `TableId`, `HistorizedRelationalDatabaseConnectorConfig` and `HeartbeatFactory`"`
  • Suggested fix: The Description bullet lists five patched classes, but this Impact bullet tells third-party connectors to exclude only four — DefaultHeartbeatConnectionProvider is missing. If that class is SeaTunnel-only (not present in stock debezium-core), please say so explicitly here so third-party maintainers know the four-class list is complete; note that PatchedDebeziumClassesTest currently claims "Every entry here must also be excluded from debezium-core", which contradicts this list. The zh translation has the same gap and should be updated in lockstep.
  • Severity: Medium

Issue 7: The explicit zstd-jni 1.5.5-5 pin was deleted from connector-cdc-base's dependencyManagement while connector POMs add version-less zstd-jni declarations — if the parent doesn't re-pin it, connectors regress to the older Kafka-transitive native library

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:60
  • Why it matters: - Root cause: the diff removes <version>1.5.5-5</version> for com.github.luben:zstd-jni from connector-cdc-base/pom.xml's dependencyManagement (which existed specifically because, per the deleted comment, the lower version is no longer compatible with Apple M1), and each connector now declares zstd-jni with no version (e.g. connector-cdc-mongodb/pom.xml lines 58-61). The version must therefore come from the connector-cdc parent's dependencyManagement, which is not part of this diff.
  • Impact: if the parent does not re-pin it, Maven resolves the older zstd-jni pulled transitively via Kafka, silently downgrading the bundled native library and reintroducing the Apple M1 incompatibility the pin was added to fix — with no build failure to surface the regression.
  • Fix: pin zstd-jni:1.5.5-5 in the connector-cdc parent's dependencyManagement within this PR, and carry over the explanatory comment so the rationale is not lost.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-mongodb/pom.xml:59-60 " <groupId>com.github.luben</groupId>\n <artifactId>zstd-jni</artifactId>" (no <version> element)
  • Suggested fix: zstd-jni is declared here without a version, and this PR deletes the 1.5.5-5 pin (with its Apple-M1 rationale) from connector-cdc-base's dependencyManagement. The replacement pin in the connector-cdc parent is not visible in this diff — if it's missing, Maven will resolve the older Kafka-transitive zstd-jni, silently downgrading the bundled native library and reintroducing the M1 incompatibility the pin fixed. Please make sure the parent pins zstd-jni:1.5.5-5 in this PR and keep the explanatory comment there.
  • Severity: Medium

Issue 8: PatchedDebeziumClassesTest pins the patched-class set against a hardcoded constant but never verifies the actual shade filters it exists to protect, so a filter regression in the POMs still passes the build

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:52
  • Why it matters: - Root cause: the test compares the compiled classes under src/main/java/io/debezium against the hardcoded EXPECTED_PATCHED_CLASSES constant, but the invariant it documents ("Every entry here must also be excluded from debezium-core by the CDC shade configuration") is enforced only by the javadoc's instruction to manually update connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml.
  • Impact: if someone removes or typos an entry in either POM's io.debezium:debezium-core filter, this test stays green while the shaded connector jar silently ships the stock class alongside the patched one — exactly the class-loader collision the test was written to prevent.
  • Fix: extend the test to parse the io.debezium:debezium-core <filter> sections from both POMs and assert their excludes cover every class in EXPECTED_PATCHED_CLASSES, so both sides of the invariant are machine-checked instead of relying on the javadoc instruction.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:45-46 " * classes changes. If it does, update the {@code io.debezium:debezium-core} filter in connector-cdc/pom.xml and in connector-cdc-opengauss/pom.xml, which declares its own filter list."
  • Suggested fix: This test pins the compiled patched-class set against EXPECTED_PATCHED_CLASSES, but it doesn't actually check the shade filters in connector-cdc/pom.xml / connector-cdc-opengauss/pom.xml — the two hand-maintained lists this constant is supposed to mirror. If a filter entry is dropped from either POM, this test stays green while the connector jar silently ships the stock class next to the patched one. Could we have the test also parse the io.debezium:debezium-core <filter> from both POMs and assert its excludes cover these classes? That closes the loop instead of relying on the javadoc instruction.
  • Severity: Medium

Review conclusion

Conclusion: can merge after the blocking items are fixed

1. Blocking items

  • Issue 1: The shade-filter change in connector-cdc/pom.xml that the PR relies on to drop stock debezium-core copies of the patched classes is not part of this diff — without it, patched and stock copies of ChangeEventQueue/TableId/etc. coexist on the plugin class loader with a nondeterministic winner
  • Issue 2: The connector-cdc parent POM changes this PR depends on (Debezium dependencyManagement + shade filters) are not in the changed file set, leaving versionless dependency declarations that cannot resolve

2. Remaining follow-ups

  • Issue 3: The duplicate-class safety net is one-sided: PatchedDebeziumClassesTest pins the patched class list but never verifies the shade filters, and it deliberately ignores nested classes, so stock/patched Debezium duplicates can still ship undetected
  • Issue 4: connector-cdc-opengauss gets a POM change and an adapter test but no DebeziumAdapter implementation or SPI registration, unlike every other Debezium-based connector
  • Issue 5: Partial-upgrade deployments now fail in two nasty ways (NoClassDefFoundError or order-dependent duplicate Debezium classes), and the incompatible-changes doc does not tell operators to upgrade connector-cdc-base and connector jars together
  • Issue 6: The incompatible-changes entry lists five patched classes but tells third-party maintainers to exclude only four, while PatchedDebeziumClassesTest's Javadoc claims "Every entry here must also be excluded" — the discrepancy around DefaultHeartbeatConnectionProvider needs to be explained or fixed
  • Issue 7: The explicit zstd-jni 1.5.5-5 pin was deleted from connector-cdc-base's dependencyManagement while connector POMs add version-less zstd-jni declarations — if the parent doesn't re-pin it, connectors regress to the older Kafka-transitive native library
  • Issue 8: PatchedDebeziumClassesTest pins the patched-class set against a hardcoded constant but never verifies the actual shade filters it exists to protect, so a filter regression in the POMs still passes the build

Thanks for the updates so far. Happy to discuss the remaining points.

@DanielLeens

Copy link
Copy Markdown
Contributor Author

Thanks for confirming, @SEZ9 - agreed on scope: just the two doc issues, nothing else outstanding on the current head (afb0fada4b).

Confirming the plan: I'll push a commit against both docs/en/introduction/concepts/incompatible-changes.md and docs/zh/introduction/concepts/incompatible-changes.md that (1) corrects the "no longer contains io.debezium classes" claim to state that connector-cdc-base still ships the five patched classes, and (2) adds the mandatory io.debezium:debezium-core shade-exclusion instruction (plus the zstd-jni note) for third-party CDC connectors building outside the tree, so they don't hit the order-dependent class-resolution race against the patched ChangeEventQueue. I'll mark the PR ready for review once that lands - Build run 91769082988 is already green, so nothing else should be blocking merge after the docs fix.

I'll do a fresh pass and update the issue table once the commit is up.

@davidzollo
davidzollo marked this pull request as draft August 27, 2026 15:52
vsantonastaso and others added 4 commits August 27, 2026 12:00
…se to each CDC connector

PR apache#10799 introduced the DebeziumAdapter SPI and per-connector debezium.version
properties, but those properties could not change the Debezium a connector runs
on. connector-cdc-base declared debezium-api/debezium-embedded at compile scope,
so debezium-core reached every connector transitively at the version resolved in
connector-cdc-base's own POM. AbstractPluginDiscovery#filterPluginJar forces
connector-cdc-base into the class loader of every CDC plugin, so the shared
connector-cdc-base jar was the single project-wide Debezium version and no
connector could override it.

Changes:

- connector-cdc-base declares debezium-api, debezium-embedded and zstd-jni as
  provided, so its shaded jar no longer carries Debezium classes.
- The Debezium dependency management moves to the connector-cdc parent. Inherited
  dependency management is interpolated against the properties of the module
  being built, so each connector resolves Debezium at its own debezium.version
  while sharing one copy of the exclusion rules.
- Every Debezium-based connector declares debezium-api, debezium-embedded and
  zstd-jni explicitly. connector-cdc-opengauss and connector-cdc-vitess gain the
  debezium.version property they were missing.
- connector-cdc-tidb is unchanged: it uses no Debezium classes.

Each connector now registers a DebeziumAdapter implementation, so the SPI added
in apache#10799 has real providers and the version a connector ships is introspectable
at runtime. connector-cdc-opengauss deliberately registers none: it runs
Debezium's PostgreSQL connector and shades connector-cdc-postgres, and the shade
plugin merges META-INF/services, so a second provider for the same connector
class would break the exactly-one-match rule. A test pins that.

Per-connector tests assert the adapter resolves uniquely and that its declared
version equals the debezium-core version actually resolved onto the module
classpath, so a pom bump that forgets the adapter fails the build.

Runtime behavior, checkpoint format and CDC job compatibility are unchanged: all
connectors still resolve Debezium 1.9.8.Final, so the classes on a CDC job's
class loader are the same as before, only relocated from the shared jar into the
connector jar.
…tic across connectors

Moving Debezium into the connector jars introduced two version-coherence
problems that the previous commit did not cover.

Duplicate patched classes. connector-cdc-base recompiles five Debezium classes
under src/main/java/io/debezium: ChangeEventQueue, TableId, HeartbeatFactory,
HistorizedRelationalDatabaseConnectorConfig and
DefaultHeartbeatConnectionProvider. Four of them also exist in debezium-core.
While Debezium was packaged inside connector-cdc-base, the shade plugin resolved
that overlap at build time and the patched copies were the only ones present. Now
that each connector packages its own Debezium, the stock copies arrive in the
connector jar while the patched ones stay in connector-cdc-base, and both jars
sit on the same class loader. The winner would depend on the order
File#listFiles returns the jars in AbstractPluginDiscovery, so the patches could
silently stop applying on some machines.

The CDC shade configuration now drops those classes from debezium-core, keeping
connector-cdc-base's patched copies the single definition exactly as before.
connector-cdc-opengauss declares its own filter list at execution level, which
takes precedence over the inherited one, so it restates the filter.
PatchedDebeziumClassesTest pins the set of patched classes against the exclusion
list so a newly patched class cannot be added without updating both.

Version drift between modules. The per-connector tests now assert that every
Debezium artifact on a connector's class path resolves to the single version its
adapter declares, instead of spot-checking debezium-core. This matters most for
connector-cdc-opengauss, which declares its own debezium.version for
debezium-embedded but reaches debezium-connector-postgres through
connector-cdc-postgres: bumping one module and not the other now fails the build
rather than shipping two Debezium versions on one class path.

Documentation now states how far connectors may actually diverge. Packaging
ownership has moved, but connector-cdc-base is still compiled against a single
baseline and is loaded into every CDC connector, using around 85 Debezium types
across relational, pipeline, schema, config and jdbc. Divergence is therefore
safe only within a Debezium line that keeps those APIs binary compatible.
@davidzollo
davidzollo force-pushed the daniel-debezium-per-connector-versions branch from c53bc73 to ef25cb1 Compare August 27, 2026 16:07
@davidzollo
davidzollo marked this pull request as ready for review August 27, 2026 16:07
@davidzollo

Copy link
Copy Markdown
Contributor

Thanks @SEZ9 for the careful review and for keeping the migration boundary precise.

This update is now rebased onto the current dev and marked ready for review. It addresses the documentation blockers in both language versions:

  • connector-cdc-base is now described accurately: it still ships five patched io.debezium classes. Four override stock debezium-core classes; DefaultHeartbeatConnectionProvider is SeaTunnel-only and has no stock class to filter.
  • The third-party migration note now requires declaring debezium-api, debezium-embedded, zstd-jni, and the connector-specific Debezium artifact. It also explains the required outer and nested-class shade exclusions, the need to restate them for a connector-owned shade filter, and the atomic plugin-upgrade requirement.
  • I added a regression guard that reads both actual debezium-core shade filters (connector-cdc/pom.xml and the openGauss override) and verifies they exactly cover the four stock overrides. It also verifies the fifth patched class is absent from the resolved stock Debezium core artifact.

I also investigated the old red workflow: its failures were an unrelated Windows seatunnel-api test, a Docker Hub timeout pulling mysql:8.0.43 for Oracle IT, and a Paimon shutdown timeout. The rebase and push have triggered a fresh current-head CI run; I will use that run as the merge signal rather than treating the older failures as a CDC regression.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @SEZ9 for staying engaged on this across so many rounds, and for keeping the pressure on the migration-boundary documentation - the current incompatible-changes.md wording (upgrade connector-cdc-base and every Debezium connector jar as one set, the corrected five-vs-four patched-class explanation, the explicit "do not exclude DefaultHeartbeatConnectionProvider" note) reads much more precisely than the first pass, and that came directly out of this back-and-forth. I also went back through your 2026-08-25 review specifically: the two High-severity blockers in it (Issue 1/Issue 2, "the shade-filter change in connector-cdc/pom.xml is not part of this diff" / "the parent POM changes are not in the changed file set") do not hold at the current head - seatunnel-connectors-v2/connector-cdc/pom.xml has carried the debezium-core shade filter and the relocated dependencyManagement since the very first commit on this branch (cd39273884e, 2026-08-03), and it is in the diff today (95 lines added, confirmed via git diff <merge-base>..HEAD -- seatunnel-connectors-v2/connector-cdc/pom.xml). I think that review was looking at an incomplete diff view (GitHub's PR diff sometimes needs pagination/"Load diff" for large changesets), not the actual current state of the 31-file change set - I'm calling this out explicitly so it doesn't linger as an open blocker in the thread. Issues 3/7/8 from that same review (nested-class coverage, the zstd-jni pin, machine-checking the shade filter) are all independently resolved by the PatchedDebeziumClassesTest additions in d82872e3084/ef25cb1e3e6, detailed below.

This is my full re-review against the current head, ef25cb1e3e6346ce8bb88f34ea783dcafc4385b8 (rebased onto current dev, ahead_by=4, behind_by=0), and it surfaces one new, concrete, code-breaking regression that no prior round of review (including my own) caught.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base shipped the Debezium runtime (debezium-api, debezium-embedded, transitively debezium-core) at compile scope, and AbstractPluginDiscovery#filterPluginJar/selectPluginJar (seatunnel-plugin-discovery/.../AbstractPluginDiscovery.java:514-546) force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc". That made the one shared jar the single project-wide Debezium version; the per-connector debezium.version properties added in #10799 could not actually change what a connector ran on.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocate the version + exclusion dependencyManagement into the connector-cdc parent (interpolated per leaf module against that module's own debezium.version), have each Debezium-based connector declare and package its own runtime Debezium, add a maven-shade-plugin filter dropping debezium-core's stock copies of the five classes connector-cdc-base patches, and register a (still inert) DebeziumAdapter SPI implementation per connector, guarded by machine-checked tests.
  • One-sentence summary: a packaging refactor that correctly moves Debezium ownership into seven in-tree connectors it was written against, but never accounted for connector-cdc-db2 - a Debezium-based CDC connector that merged into dev after this PR was opened and landed in this branch through the 2026-08-27 rebase - and that connector no longer compiles.

1. Code Change Review

1.1 Core Logic Analysis

Mandatory registration-audit, redone from scratch against the current head rather than trusted from any prior round:

I pulled every touched pom.xml and cross-checked it against every module under seatunnel-connectors-v2/connector-cdc/, not just the seven this PR's diff touches:

Module In this PR's diff? Uses Debezium? Own debezium-api/-embedded/zstd-jni? Result
connector-cdc-base Yes Compiles against it, ships 5 patched classes Flipped to provided Correct - patched classes stay the only definition, stock runtime removed from its shaded jar
connector-cdc-mysql Yes Yes Added Not broken
connector-cdc-postgres Yes Yes Added Not broken
connector-cdc-oracle Yes Yes Added Not broken
connector-cdc-sqlserver Yes Yes Added Not broken
connector-cdc-mongodb Yes Yes Added Not broken
connector-cdc-opengauss Yes Yes Added, restates the shade filter (execution-level filters override rather than merge) Not broken - stockDebeziumCoreOverridesAreExcludedInEveryCdcShadeConfiguration verifies both filters byte-for-byte
connector-cdc-vitess Yes Yes Added Not broken
connector-cdc-tidb No No (io.debezium grep = 0 hits) n/a Correctly untouched
connector-cdc-db2 No Yes, extensively No Broken - does not compile

Issue 1 (new, High, blocking): connector-cdc-db2 no longer compiles because of this PR's provided-scope change, and CI on the current head proves it.

connector-cdc-db2 (merged to dev via #10780 after this PR's original branch point, and pulled into this branch by the 2026-08-27 rebase onto dev) was never migrated. Its pom.xml declares only:

<dependency>
    <groupId>org.apache.seatunnel</groupId>
    <artifactId>connector-cdc-base</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>io.debezium</groupId>
    <artifactId>debezium-connector-db2</artifactId>
    <exclusions>
        <exclusion><groupId>io.debezium</groupId><artifactId>debezium-core</artifactId></exclusion>
        <exclusion><groupId>io.debezium</groupId><artifactId>debezium-api</artifactId></exclusion>
        ...

(seatunnel-connectors-v2/connector-cdc/connector-cdc-db2/pom.xml:36-79, untouched by this PR).

Before this PR, connector-cdc-base declared debezium-api/debezium-embedded at compile scope. Maven's scope-inheritance table resolves provided (db2's own scope for its connector-cdc-base dependency) combined with compile (the transitive scope) to provided for db2 - visible on db2's compile classpath, which is exactly why Db2Dialect, Db2SourceConfig, Db2SourceFetchTaskContext, Db2SnapshotFetchTask, Db2SnapshotSplitReadTask, TableDiscoveryUtils and others could import io.debezium.relational.*, io.debezium.pipeline.*, io.debezium.jdbc.*, io.debezium.heartbeat.*, io.debezium.connector.base.ChangeEventQueue, etc. directly (grep -c "^import io\.debezium" across connector-cdc-db2/src/main/java returns 58 matches). After this PR, connector-cdc-base's own dependency on debezium-api/debezium-embedded is itself provided. Maven never propagates a provided-scope (or test-scope) transitive dependency at all, regardless of the direct dependency's scope - so db2's effective view of debezium-api/debezium-embedded/debezium-core collapses from "provided" to "absent". Since db2 also explicitly excludes debezium-core/debezium-api from its own debezium-connector-db2 dependency (the same pattern the six migrated connectors still use, but which only works for them because they separately re-added a direct debezium-api+debezium-embedded dependency - db2 never got that addition), there is no remaining path for db2 to see any io.debezium.* type at all.

This is not a theoretical scope-algebra argument - it is exactly what the current head's own CI run shows. I pulled the fork's actual job logs for head ef25cb1e3e6, not the apache-side pointer:

[ERROR] .../Db2SourceFetchTaskContext.java:[80,12] error: cannot find symbol
[ERROR]   symbol:   class ErrorHandler
[ERROR] .../Db2SourceConfig.java:[25,29] error: cannot find symbol
[ERROR]   symbol:   class RelationalTableFilters
[ERROR]   location: package io.debezium.relational
[ERROR] .../Db2SnapshotFetchTask.java:[29,25] error: package io.debezium.config does not exist
[ERROR] .../Db2SnapshotSplitReadTask.java:[26,38] error: package org.apache.kafka.connect.errors does not exist
... (>20 more "cannot find symbol" / "package does not exist" errors, all under connector-cdc-db2)
[ERROR] mvn <args> -rf :connector-cdc-db2

(from unit-test (8, ubuntu-latest), unit-test (11, ubuntu-latest), unit-test (8, windows-latest), unit-test (11, windows-latest), updated-modules-integration-test-part-1 (11, ubuntu-latest) and updated-modules-integration-test-part-2 (8 and 11, ubuntu-latest) - all seven currently-failing jobs on run https://github.com/DanielLeens/seatunnel/actions/runs/33091568634, all failing at the exact same connector-cdc-db2 compile step, deterministically, across every JDK/OS combination). This is the real cause of today's red Build check - not the Docker/Windows/Paimon environmental flakes diagnosed in earlier rounds. Those earlier diagnoses were correct for the commits they were made against; the db2 connector simply was not part of the tree yet at that point, and no round of review re-ran the full connector matrix after the rebase pulled it in. This is precisely the kind of shared-registration gap that a move like this has to re-check on every rebase, not just once.

Runtime confirmation of the plugin-loading mechanism (seatunnel-plugin-discovery/src/main/java/org/apache/seatunnel/plugin/discovery/AbstractPluginDiscovery.java:514-546):

Job submits a DB2-CDC source
  -> AbstractPluginDiscovery.getPluginJarPaths()
      -> filterPluginJar(): pluginName contains "cdc"
          -> matches BOTH connector-cdc-db2-*.jar AND connector-cdc-base-*.jar
      -> selectPluginJar(): throws unless exactly 2 jars matched
  -> both jars loaded into ONE URLClassLoader
      -> neither jar packages io.debezium.* any more (connector-cdc-base: provided scope;
         connector-cdc-db2: no own declaration, exclusions strip it from debezium-connector-db2)
      -> class load of Db2Dialect/Db2IncrementalSourceFactory fails with NoClassDefFoundError,
         if the module even finished compiling - which it currently does not.

Even setting the CI compile break aside for a moment, this shows the break is on db2's normal startup path, not a boundary/exception case: Db2SourceFetchTaskContext, Db2Dialect and Db2SourceConfig are constructed on every DB2 CDC job, snapshot or streaming.

Everything I re-verified from prior rounds still holds for the seven connectors this PR does touch: the shade-filter mechanism in connector-cdc/pom.xml:60-179 reaches all six inheriting connectors via <filters combine.children="append">; connector-cdc-opengauss/pom.xml:112-133 restates the identical filter because an execution-level <filters> block overrides rather than merges with the inherited plugin-level one (I diffed both lists - they match exactly); DebeziumAdapterFactory.getAdapter(...) still has zero production call sites, so the SPI itself changes no runtime behavior today; and zero .java files under any existing production package were modified (only new adapter/test files plus build configuration), so checkpoint/savepoint/offset serialization is untouched for every connector that does compile.

1.2 Compatibility Impact

Partially incompatible, and the current doc overstates how safe it is. For the seven Debezium-based connectors this PR actually touches: fully compatible, no config/API/checkpoint change, all still resolve debezium.version=1.9.8.Final. For third-party/out-of-tree connectors: honestly documented now (upgrade base+connector jars together, five-vs-four patched class list corrected, explicit "do not exclude DefaultHeartbeatConnectionProvider" note). But for connector-cdc-db2, an in-tree connector, the current incompatible-changes.md claim "No change for CDC job configuration, runtime behavior or checkpoint compatibility" (docs/en/introduction/concepts/incompatible-changes.md, new "Impact" bullet) is incorrect - db2 does not build at all at this head, which is a correctness regression, not a no-op.

1.3 Performance / Side-Effect Analysis

Unchanged from what's documented: the Debezium runtime is now duplicated into each of the seven migrated connector jars instead of shared via connector-cdc-base, growing the distribution (still only a qualitative note, no measured delta - Issue 3 below). No CPU/memory/GC/concurrency change on any job path for connectors that build successfully.

1.4 Error Handling and Logging

No new error-handling paths in production code - this PR only touches build/packaging configuration and new SPI/test scaffolding. Nothing to flag here beyond Issue 1 above (which is a build-time failure, not a runtime error-handling gap).

2. Code Quality Assessment

2.1 Coding Standards

License headers present on all new files. Comments throughout - especially the pom comments explaining the classloader-merge rationale and the shade-filter override/merge distinction - are unusually good at explaining why, not just what. PatchedDebeziumClassesTest's class- and method-level Javadoc clearly states the invariant each test protects. No issues here.

2.2 Test Coverage and Test Stability

Rating: Stable. PatchedDebeziumClassesTest (connector-cdc-base/src/test/.../PatchedDebeziumClassesTest.java) and the eight per-connector *DebeziumAdapterTest classes are all deterministic: they inspect compiled .class output, parse the two source POMs with a hardened DocumentBuilderFactory (external entities disabled), and open the resolved debezium-core artifact's code source to check for a class's presence. No Thread.sleep, no network I/O, no fixed ports, no shared mutable static state, no @DisplayName. This is a genuine improvement over the prior round: stockDebeziumCoreOverridesAreExcludedInEveryCdcShadeConfiguration (lines 140-159) now mechanically parses both io.debezium:debezium-core shade filters and asserts they equal the expected exclude set (built from STOCK_DEBEZIUM_CORE_OVERRIDES with both the bare and $*-wildcard nested-class patterns), which closes the "one-sided safety net" and "ignores nested classes" gaps raised in the 2026-08-25 review - the wildcard patterns are now asserted, not just written and hoped-for. defaultHeartbeatConnectionProviderIsNotInStockDebeziumCore (lines 166-185) converts what was previously a one-time manual curl+unzip verification into a standing, dependency-upgrade-proof CI check.

Coverage gap that remains (Issue 4, Low, carryover): none of these tests open an actual built/shaded connector jar, so a future regression that reaches the shade-filter merge only at package time (e.g., a combine.children typo, or a plugin-configuration inheritance change elsewhere in the build) is still not CI-visible through these tests alone. This is a reasonable non-blocking follow-up.

The coverage gap that is not carryover: none of these tests, nor any existing test in the repository, would have caught Issue 1 - there is no guardrail (module-set assertion, reactor "does connector-cdc-db2 declare its own debezium-api" check, or otherwise) that would fail when a new Debezium-based CDC connector merges into dev without picking up this PR's per-connector pattern. The CI failure caught it this time only because the compile genuinely breaks; a subtler future case (a new connector that compiles fine against connector-cdc-base's test-scope visibility but fails only at runtime) would not be caught by anything this PR adds.

2.3 Documentation Updates

docs/en and docs/zh incompatible-changes.md are updated and symmetric (7 lines added to each, verified via git diff --stat). Content is materially more complete than the first pass - see 1.2 for the one remaining accuracy problem (the "no change for runtime behavior" claim, given Issue 1).

3. Architectural Soundness

3.1 Elegance of the Solution

This is a long-term fix for the actual root cause (a force-merged shared jar pinning one project-wide Debezium version), not a workaround, and the dependencyManagement-in-parent / dependency-in-leaf pattern is the correct Maven idiom for "shared exclusion rules, per-module version." The shade filter and its opengauss restatement are now mechanically verified rather than resting on comment discipline alone.

3.2 Maintainability

Net improvement for the seven connectors this PR covers, but the pattern this PR establishes is per-connector, hand-applied, and un-enforced at the "does every Debezium-based connector module opt in" level. connector-cdc-db2 is the concrete proof that this migration step is easy to miss when a new connector lands via a routine, unrelated feature PR. I'd treat closing Issue 1 as also an opportunity to add a cheap structural guard (e.g., a module-list-based test in connector-cdc-base or connector-cdc that fails when a new connector-cdc-* module under Debezium's line does not declare its own debezium-api), so this class of gap cannot recur silently a second time.

3.3 Extensibility

DebeziumAdapter remains a reasonable seam for future per-connector runtime isolation and stays correctly unwired into production code per its @Experimental Javadoc.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, offset tracking, or serialized job state for any connector that builds - zero .java files under existing production packages were modified. This does not extend to connector-cdc-db2, which currently cannot produce a runnable artifact at all at this head.

4. Issue Summary

# Issue Location Severity Raised by another reviewer
1 connector-cdc-db2 was never migrated to declare its own debezium-api/debezium-embedded/zstd-jni; after connector-cdc-base's dependencies move to provided, db2 loses all io.debezium.* visibility and fails to compile - confirmed live in the current head's CI (7/7 currently-failing jobs, all at the same connector-cdc-db2 compile step) seatunnel-connectors-v2/connector-cdc/connector-cdc-db2/pom.xml High No (new finding this round)
2 The updated incompatible-changes.md "Impact" bullet states there is no runtime-behavior change, which is not true for connector-cdc-db2 given Issue 1 docs/en/introduction/concepts/incompatible-changes.md; docs/zh/introduction/concepts/incompatible-changes.md Medium (tracks Issue 1; resolves once Issue 1 is fixed) No
3 Distribution size growth documented only qualitatively, no measured delta docs/en, docs/zh incompatible-changes.md Low Yes (SEZ9, 2026-08-25, "Issue 6"; Daniel, 2026-08-24, "Issue 3")
4 No test inspects an actual built/shaded jar, so the shade-filter merge mechanism at package time is still not directly CI-verified (the POM-level filter-content check added this round substantially narrows, but does not close, this gap) PatchedDebeziumClassesTest.java Low Yes (SEZ9, "Issue 8"; Daniel, "Issue 4") - partially addressed this round
5 No structural guard fails the build when a future new Debezium-based CDC connector omits this PR's per-connector declaration pattern (the exact class of bug behind Issue 1) connector-cdc-base / connector-cdc test scope Medium No

5. Merge Recommendation

Conclusion: Not recommended for merge

  1. Blockers - must be fixed

    • Issue 1 (High): Add the same per-connector debezium-api/debezium-embedded/zstd-jni declarations (and, if a DebeziumAdapter is desired for parity with the other seven, an adapter + META-INF/services registration + test) to connector-cdc-db2/pom.xml, matching the pattern already applied to mysql/postgres/oracle/sqlserver/mongodb/vitess. This is a small, mechanical, low-risk change - db2's own debezium.version should default from the parent the same way vitess/opengauss now do, since db2 wasn't given its own property before either. After the fix, rerun the fork's unit-test and updated-modules-integration-test-* jobs (all seven that are currently failing at the connector-cdc-db2 compile step) to confirm the reactor builds clean, since this is the actual, verifiable CI signal for this fix - not a rerun-and-hope situation.
    • Issue 2 (Medium, bundled with Issue 1): once db2 is fixed, no further doc change should be needed since the "no runtime behavior change" claim will then be true again for all in-tree connectors; if a doc change is still needed after the Issue 1 fix, call out db2 explicitly the way the note already calls out the general "upgrade as a unit" requirement.
  2. Recommended fixes - non-blocking

    • Issue 3: replace the qualitative distribution-size note with a measured delta from a built distribution.
    • Issue 4: add one packaging-level assertion (unzip a built connector jar in a verify-phase test, assert the four patched class families are absent) so a shade-filter regression at package time is CI-visible, not just POM-content-visible.
    • Issue 5: add a lightweight structural check (e.g., enumerate connector-cdc-* modules that depend on any debezium-connector-* artifact and assert each also declares its own debezium-api) so a future connector cannot silently repeat Issue 1.

On the other reviewer: @SEZ9's 2026-08-25 review raised two High-severity blockers (missing connector-cdc/pom.xml changes, unresolvable versionless dependencies) that do not hold against the actual diff at any commit on this branch, including the one that review cites - I'd treat those two as resolved/moot rather than carried forward, for the reasons in my opening paragraph. The Medium/Low items in that same review (nested-class filter coverage, the zstd-jni pin, machine-checking the shade filters) are real and are now addressed by d82872e3084/ef25cb1e3e6.

Overall assessment. The core mechanism - moving Debezium ownership per connector, closing the resulting duplicate-class hole with a mechanically-verified shade filter, and documenting the third-party migration path - is sound, well-tested for the seven connectors it targets, and does not touch any existing production .java file, so there is no checkpoint/savepoint/serialization risk for connectors that build. But the PR set out explicitly to make "every CDC connector that used to depend on connector-cdc-base's shared Debezium dependency" keep working, and after the 2026-08-27 rebase pulled in a connector that landed on dev in the interim, that promise is no longer true - connector-cdc-db2 cannot compile, and the currently-red Build check on this exact head is direct, reproducible proof of it, not an unrelated flake. This is a small, mechanical fix to apply, and I'd expect a fast follow-up round once it lands.

@SEZ9 SEZ9 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I reviewed the latest head ef25cb1e3e63.

What this PR fixes

  • User pain: connector-cdc-base ships Debezium at compile scope and is force-loaded into every CDC plugin's class loader by AbstractPluginDiscovery#filterPluginJar, so the per-connector debezium.version properties added in #10799 are inert — no connector can actually run a different Debezium.
  • Fix approach: demote debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base so each connector packages its own Debezium runtime, keep the five patched io.debezium classes in the base jar, guard the four stock-class shade exclusions with the new PatchedDebeziumClassesTest, and document the break in both incompatible-changes.md files.
  • One-line summary: the ownership move is directionally right and the pinning test is a good idea, but the versionless provided declarations make correctness depend entirely on the connector-cdc parent pom's dependencyManagement shipping atomically with this change, there is no runtime fail-fast for missing or duplicate Debezium classes on a CDC class loader, and the patched-vs-stock class race is guarded only by hand-maintained excludes in two hardcoded POMs.

Runtime chain I rechecked

AbstractPluginDiscovery.createPluginClassLoader()
  -> AbstractPluginDiscovery.getPluginJarPaths() -> filterPluginJar()  AbstractPluginDiscovery.java (cdc branch: accepts <pluginJarPrefix>*.jar AND connector-cdc-base*.jar)
    -> URLClassLoader over [connector-cdc-<x>.jar, connector-cdc-base.jar]
      -> IncrementalSource startup -> DebeziumSourceFunction/SourceReader creates io.debezium.embedded.EmbeddedEngine
        -> loads io.debezium.connector.base.ChangeEventQueue, io.debezium.relational.TableId, io.debezium.heartbeat.HeartbeatFactory, io.debezium.relational.HistorizedRelationalDatabaseConnectorConfig
          -> pre-PR: single copy from connector-cdc-base.jar (294 io.debezium core/embedded classes)
          -> post-PR: patched copy in connector-cdc-base.jar vs stock copy in connector jar's shaded debezium-core; winner decided by File#listFiles jar order unless the shade excludes in connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml strip the stock outer + $* nested classes
      -> HeartbeatFactory.createHeartbeat() -> io.debezium.heartbeat.DefaultHeartbeatConnectionProvider (SeaTunnel-only, supplied solely by connector-cdc-base.jar — must NOT be excluded)
Guard path (build-time only, no runtime equivalent):
  PatchedDebeziumClassesTest.compiledPatchedDebeziumClassesMatchExpectedSet()  PatchedDebeziumClassesTest.java:~100
    -> SourceOptions.class.getProtectionDomain().getCodeSource().getLocation() (assumes exploded target/classes, no null check)
    -> Files.walk(io/debezium) compared against EXPECTED_PATCHED_CLASSES  PatchedDebeziumClassesTest.java:60-75
  shade-exclude verification parses connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml via DocumentBuilderFactory (default config, only these two hardcoded POMs)

Findings

Issue 1: connector-cdc-base drops its Debezium dependencyManagement and declares versionless provided dependencies, making build correctness depend entirely on the connector-cdc parent pom's management and shade exclusion lists landing atomically with this change

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:66
  • Why it matters: - Root cause: connector-cdc-base/pom.xml removes its <dependencyManagement> entries for debezium-api, debezium-embedded and zstd-jni, and re-declares them as provided with no <version> element. Version resolution and the shade exclusion lists that PatchedDebeziumClassesTest parses now live only in the connector-cdc parent pom (connector-cdc/pom.xml).
  • Impact: this module is no longer self-contained. Any partial cherry-pick, backport, or downstream fork that takes the connector-cdc-base change without the matching parent pom change gets versionless Debezium dependencies that cannot resolve, and a pin test that parses exclusion lists that do not exist. The coupling is invisible from this module's pom alone.
  • Suggested action: confirm the connector-cdc parent pom change ships in the same commit set as this module change, and consider adding a brief comment next to the versionless declarations pointing at the parent's dependencyManagement so the coupling is explicit for backporters.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:58 * <p>The exclusion lists are maintained by hand in connector-cdc/pom.xml and; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:59 * connector-cdc-opengauss/pom.xml. This test reads both lists so changing either the patched source; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:@@ -44,53 (removed) <exclusion><groupId>org.apache.kafka</groupId><artifactId>kafka-log4j-appender</artifactId></exclusion>
  • Suggested fix: This hunk removes the module's <dependencyManagement> entries for debezium-api/debezium-embedded/zstd-jni and re-declares them provided with no <version>. Version resolution now depends entirely on the connector-cdc parent pom's dependencyManagement, and PatchedDebeziumClassesTest (line 58) parses that same connector-cdc/pom.xml for the shade exclusion lists. Please confirm the parent pom change lands atomically with this one, and consider a short comment here pointing at the parent's management — a partial cherry-pick or backport of this module alone would otherwise fail in confusing ways (unresolvable versions, pin test asserting against missing exclusions).
  • Severity: High

Issue 2: connector-cdc-base's shaded jar becomes Debezium-free with no runtime fail-fast; mixed old/new plugin jars and third-party CDC connectors fail with an opaque NoClassDefFoundError

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:71
  • Why it matters: - Root cause: with connector-cdc-base now declaring debezium-api/debezium-embedded/zstd-jni as provided, runtime correctness depends entirely on the deployer having a matched set of jars. The PR's own docs and test javadoc acknowledge the two failure modes: a missing Debezium runtime (old connector jar + new base jar, or a third-party connector that never redeclared Debezium) and a duplicate patched class where the winner depends on the order File#listFiles returns the jars in AbstractPluginDiscovery.
  • Impact: an operator who upgrades only connector-cdc-base gets a bare NoClassDefFoundError: io/debezium/... at source startup with no hint that the fix is a matched jar set. The docs declare mixed jars unsupported, but nothing in the code detects the condition.
  • Suggested action: add a fail-fast check in the base module (e.g., during DebeziumAdapter resolution, Class.forName("io.debezium.embedded.EmbeddedEngine") and throw a descriptive error naming the missing debezium-embedded dependency and linking incompatible-changes.md).
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:71 <scope>provided</scope> (debezium-embedded); seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:76 <scope>provided</scope> (zstd-jni); docs/en/introduction/concepts/incompatible-changes.md:224 mixing pre-change and post-change plugin jars in one plugin directory is unsupported and can leave the runtime with a missing or duplicate Debezium class
  • Suggested fix: Making debezium-embedded provided here means the shaded connector-cdc-base jar no longer supplies io.debezium runtime classes to the CDC plugin class loader that AbstractPluginDiscovery#filterPluginJar builds. Anyone who upgrades connector-cdc-base but leaves an older (or third-party) connector jar in the plugins dir will get a bare NoClassDefFoundError: io/debezium/... with no explanation. Since the docs already declare mixed jars unsupported, please also add a fail-fast check in the base module (e.g., at DebeziumAdapter resolution, Class.forName("io.debezium.embedded.EmbeddedEngine") and throw a descriptive error naming the missing debezium-embedded dependency and linking incompatible-changes.md). That turns an opaque class-loading failure into an actionable message.
  • Severity: High

Issue 3: The patched-vs-stock Debezium class race (ChangeEventQueue, TableId, HeartbeatFactory, HistorizedRelationalDatabaseConnectorConfig) is guarded only by hand-maintained shade excludes and a pom-parsing unit test — there is no runtime detection, so a mispackaged jar loads a nondeterministic class definition silently

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:58
  • Why it matters: - Root cause: with Debezium now shipped inside each connector jar while connector-cdc-base retains five patched io.debezium classes, both a patched and a stock copy of four classes can be visible on the same plugin class loader. The test's own javadoc states the winner depends on the order File#listFiles returns the jars in AbstractPluginDiscovery — filesystem-dependent, nondeterministic class resolution.
  • The only safeguards are hand-maintained <exclude> patterns in two hardcoded in-tree poms plus PatchedDebeziumClassesTest, which runs at build time in this repository only. Third-party connectors, downstream repackagings, and mixed pre-/post-change deployments get no protection at all.
  • Impact: if a stock copy of ChangeEventQueue (a backpressure-critical class) or TableId survives into a connector jar, the loaded definition varies silently by filesystem — the worst kind of production bug.
  • Suggested action: add a runtime assertion in connector-cdc-base that the patched classes resolve from the base jar's code source, and consider relocating the patched classes into an org.apache.seatunnel.* package behind the DebeziumAdapter SPI so duplicate FQCNs cannot exist at all.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:58 * <p>The exclusion lists are maintained by hand in connector-cdc/pom.xml and; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:66 <scope>provided</scope>; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:58-59 * <p>The exclusion lists are maintained by hand in connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml. This test reads both lists
  • Suggested fix: This test is a good build-time guard, but it only protects in-tree poms. Third-party connectors, downstream repackagings, and operators mixing pre-/post-change jars (all called out in the docs) get no protection: if the stock ChangeEventQueue/TableId copies survive into a connector jar, the class that actually loads depends on File#listFiles order — silent, filesystem-dependent behavior on a backpressure-critical class. Please add a runtime fail-fast in connector-cdc-base (e.g. assert ChangeEventQueue.class.getProtectionDomain().getCodeSource() resolves to the connector-cdc-base jar during source initialization and throw with a clear message otherwise), and consider relocating the patched classes into an org.apache.seatunnel.* package behind the DebeziumAdapter SPI so duplicate FQCNs cannot exist at all.
  • Severity: Medium

Issue 4: PatchedDebeziumClassesTest resolves the compiled classes via ProtectionDomain code source and sibling pom.xml relative paths, which breaks when the code source is a jar or the working directory differs

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:108
  • Why it matters: - Root cause: the test locates the patched Debezium classes via SourceOptions.class.getProtectionDomain().getCodeSource().getLocation().toURI() and asserts Files.isDirectory(debeziumRoot). This only works when the classpath entry is an exploded target/classes directory; if the module classes are on the classpath as a jar (aggregated test runs, surefire useSystemClassLoader variations, or IDE configurations referencing the packaged artifact), the directory assertion fails for an environmental reason rather than a real pin violation.
  • The shade-exclude checks read connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml via relative paths, which additionally couples the test to the working directory.
  • Impact: false failures in non-default build environments erode trust in a guard test that exists precisely to catch dangerous packaging mistakes.
  • Suggested action: add a jar-entry fallback (or an explicit assumption with a clear message) for the class enumeration, and anchor the pom reads explicitly (e.g., resolve from a system property or basedir) instead of relying on the working directory.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:110-112 Assertions.assertTrue(Files.isDirectory(debeziumRoot), "Expected compiled Debezium overrides under " + debeziumRoot);; seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/test/java/org/apache/seatunnel/connectors/cdc/base/debezium/PatchedDebeziumClassesTest.java:103-109 Path classesRoot = Paths.get(SourceOptions.class.getProtectionDomain().getCodeSource().getLocation().toURI());
  • Suggested fix: getCodeSource().getLocation() only points at an exploded target/classes directory in the plain mvn test layout; when the module classes end up on the classpath as a jar the Files.isDirectory(debeziumRoot) assertion on line 110 fails for an environmental reason rather than a real pin violation. Since JarFile is already imported, please add a jar-entry fallback (or at least an assumption with a clear message) so this guard test doesn't produce false failures in aggregated/IDE runs. The same applies to the relative-path reads of connector-cdc/pom.xml and connector-cdc-opengauss/pom.xml mentioned in the class javadoc — anchor them explicitly instead of relying on the working directory.
  • Severity: Medium

Issue 5: Downstream Maven consumers of connector-cdc-base lose not only transitive Debezium but also the curated exclusion rules (kafka-log4j-appender, glassfish jersey, zstd-jni M1 override), and the migration doc does not tell them to restate those exclusions

  • Location: docs/en/introduction/concepts/incompatible-changes.md:224
  • Why it matters: - Root cause: this change deletes the dependencyManagement block from connector-cdc-base/pom.xml (the debezium-embedded exclusions for kafka-log4j-appender, org.glassfish.jersey.core:*, and the zstd-jni 1.5.5-5 override) and moves it to the connector-cdc parent pom. In-tree connectors inherit that parent, but third-party connectors depending on connector-cdc-base as a library do not — inherited dependency management does not flow through a plain dependency edge.
  • Impact: a third-party author who follows the migration note and naively redeclares debezium-embedded re-introduces the log4j-1.x appender and the Apple-M1-broken zstd-jni release — regressions the deleted exclusions specifically guarded against.
  • Suggested action: add the exact <exclusions> block to the migration note (en + zh), or publish the shared management as an importable BOM so external consumers can import it.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:53-56 AbstractPluginDiscovery#filterPluginJar), so any Debezium artifact packaged here would become a single project-wide Debezium version that no connector could override. Keeping these dependencies provided keeps stock Debezium artifacts out of the shaded connector-cdc-base jar; docs/en/introduction/concepts/incompatible-changes.md:224 Third-party CDC connectors built against connector-cdc-baseand relying on it to supply Debezium transitively must now declaredebezium-api, debezium-embedded, zstd-jni, and their connector-specific Debezium artifact themselves.
  • Suggested fix: This bullet tells third parties to declare debezium-api/debezium-embedded/zstd-jni themselves, but the exclusion rules that used to travel with connector-cdc-base's compile-scope debezium-embedded (kafka-log4j-appender, org.glassfish.jersey.core:*, and the Apple-M1 zstd-jni override) now live only in the connector-cdc parent's dependencyManagement, which external consumers do not inherit. A third party who redeclares debezium-embedded naively re-introduces the log4j-1.x appender and the M1-broken zstd-jni. Please add the exact <exclusions> block to this migration note (en + zh), or publish the shared management as an importable BOM.
  • Severity: Medium

Issue 6: The incompatible-changes entries are single wall-of-text paragraphs and don't state the first affected SeaTunnel release; the third-party shade guidance would be far more usable as a code block

  • Location: docs/en/introduction/concepts/incompatible-changes.md:224
  • Why it matters: - Root cause: each new bullet in docs/en/introduction/concepts/incompatible-changes.md (and the zh mirror) packs the description, upgrade rule, third-party migration steps, and the exact patched-class list into one multi-hundred-character sentence chain, and none of the bullets names the SeaTunnel version in which the break lands.
  • Impact: this document is the first thing operators read before upgrading; a user maintaining a third-party CDC connector has to reverse-engineer the four maven-shade-plugin <exclude> patterns from prose, and cannot tell from the doc which release the requirement starts in.
  • Suggested action: state the first affected release, split the Impact into sub-bullets (upgrade-as-a-set / new dependency declarations / shade exclusions), and provide a fenced XML snippet with the exact exclude patterns in both language versions.
  • Evidence: docs/zh/introduction/concepts/incompatible-changes.md:210 - **影响**:CDC 作业配置、运行时行为和 checkpoint 兼容性均不受影响——... 其 maven-shade-plugin 配置必须同时排除 ...; docs/en/introduction/concepts/incompatible-changes.md:226 - **How far connectors may actually diverge**: ... It uses around 85 Debezium types across io.debezium.relational, io.debezium.pipeline, io.debezium.schema, io.debezium.configandio.debezium.jdbc ...; docs/zh/introduction/concepts/incompatible-changes.md:213 - **连接器实际可以偏离到什么程度**:... 它使用了约 85 个 Debezium 类型 ...
  • Suggested fix: The content here is accurate (thanks for the five-vs-four patched-class correction and the explicit DefaultHeartbeatConnectionProvider note from earlier rounds), but as a single paragraph it's hard for a third-party connector author to act on. Could you (1) state the first SeaTunnel release this applies to, (2) split the Impact into sub-bullets (upgrade-as-a-set / new dependency declarations / shade exclusions), and (3) add a fenced XML snippet with the exact <exclude> patterns (io/debezium/relational/TableId.class, io/debezium/relational/TableId$*.class, etc.) so users can copy-paste instead of reconstructing them from prose? Same treatment for the zh mirror at line 210.
  • Severity: Low

Issue 7: The single module-local pin of the native zstd-jni library (1.5.5-5) was removed from connector-cdc-base's dependencyManagement; per-connector declaration risks future version drift of a JNI artifact across the eight CDC connector jars

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:76
  • Why it matters: - Root cause: this change deletes <version>1.5.5-5</version> for com.github.luben:zstd-jni from connector-cdc-base and switches the base declaration to provided; every connector pom now declares zstd-jni itself.
  • Risk (not a defect in the current state, where all connectors resolve the same version via the parent's management): zstd-jni bundles native binaries, so any future drift is worse than for a pure-Java dependency — one connector resolving an older release would re-introduce the Apple-M1 incompatibility the deleted comment guarded against, and a native-code CVE would need eight coordinated fixes instead of one.
  • Suggested action: keep a single pin in the connector-cdc parent's dependencyManagement (with the M1 rationale comment carried over) and add an enforcer convergence rule for the CDC modules so a per-connector override fails the build.
  • Evidence: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:76 <scope>provided</scope>
  • Suggested fix: With the 1.5.5-5 pin removed from this module's dependencyManagement, zstd-jni is now declared independently by eight CDC connector poms. All connectors currently resolve the same version through the parent's management, but since this is a native JNI artifact, future drift is riskier than usual (the deleted comment explains the Apple-M1 incompatibility of older releases, and a native-code CVE would then need eight fixes instead of one). Please keep a single pin in the connector-cdc parent's dependencyManagement — carrying over the M1 rationale comment — and add an enforcer convergence rule for the CDC modules so a per-connector override fails the build.
  • Severity: Low

Issue 8: PR description's user-facing-change section contradicts the added pom comment and docs about whether connector-cdc-base still ships io.debezium classes

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-base/pom.xml:59
  • Why it matters: - Root cause: the PR description states "connectors/connector-cdc-base-*.jar no longer contains io.debezium classes", but the code comment added in connector-cdc-base/pom.xml and both incompatible-changes.md entries correctly say the jar intentionally still contains five patched io.debezium classes.
  • Impact: the PR description typically becomes the squash-merge commit message and feeds release-note tooling, so the inaccurate wording would propagate into the changelog and mislead operators auditing jar contents.
  • Suggested action: update the description to say the jar no longer ships stock Debezium artifacts while retaining the five patched classes, matching the pom comment and docs.
  • Evidence: docs/en/introduction/concepts/incompatible-changes.md:223 ... The stock Debezium runtime now lives in each connector's own jar instead, while connector-cdc-basestill keeps five patchedio.debezium classes.
  • Suggested fix: This comment (and the docs) correctly say the base jar still ships five patched io.debezium classes, but the PR description's user-facing-change section still claims the jar "no longer contains io.debezium classes". Since the description becomes the squash commit message, please align it with this wording (no stock Debezium artifacts, five patched classes retained) before merge.
  • Severity: Low

Review conclusion

Conclusion: can merge after the blocking items are fixed

1. Blocking items

  • Issue 1: connector-cdc-base drops its Debezium dependencyManagement and declares versionless provided dependencies, making build correctness depend entirely on the connector-cdc parent pom's management and shade exclusion lists landing atomically with this change
  • Issue 2: connector-cdc-base's shaded jar becomes Debezium-free with no runtime fail-fast; mixed old/new plugin jars and third-party CDC connectors fail with an opaque NoClassDefFoundError

2. Remaining follow-ups

  • Issue 3: The patched-vs-stock Debezium class race (ChangeEventQueue, TableId, HeartbeatFactory, HistorizedRelationalDatabaseConnectorConfig) is guarded only by hand-maintained shade excludes and a pom-parsing unit test — there is no runtime detection, so a mispackaged jar loads a nondeterministic class definition silently
  • Issue 4: PatchedDebeziumClassesTest resolves the compiled classes via ProtectionDomain code source and sibling pom.xml relative paths, which breaks when the code source is a jar or the working directory differs
  • Issue 5: Downstream Maven consumers of connector-cdc-base lose not only transitive Debezium but also the curated exclusion rules (kafka-log4j-appender, glassfish jersey, zstd-jni M1 override), and the migration doc does not tell them to restate those exclusions
  • Issue 6: The incompatible-changes entries are single wall-of-text paragraphs and don't state the first affected SeaTunnel release; the third-party shade guidance would be far more usable as a code block
  • Issue 7: The single module-local pin of the native zstd-jni library (1.5.5-5) was removed from connector-cdc-base's dependencyManagement; per-connector declaration risks future version drift of a JNI artifact across the eight CDC connector jars
  • Issue 8: PR description's user-facing-change section contradicts the added pom comment and docs about whether connector-cdc-base still ships io.debezium classes

Thanks for the updates so far. Happy to discuss the remaining points.

@DanielLeens

Copy link
Copy Markdown
Contributor Author

Thanks @SEZ9, this is another careful pass and every one of the eight issues has a concrete, checkable claim behind it - appreciated. Responding to each, with where I agree, where I'd push back on severity, and what I'm actually doing about it.

Context first, since it changes what "blocking" means right now: the Build check on this exact head (ef25cb1e3e6) is currently red for a reason unrelated to any of the eight issues below. My own re-review earlier today (2026-08-28T01:50 UTC) found that connector-cdc-db2 - a Debezium-based CDC connector merged into dev after this branch was opened, pulled in by the 2026-08-27 rebase - was never migrated to declare its own debezium-api/debezium-embedded, so it no longer compiles at all (confirmed against the fork's own job logs, not a guess). That is the actual current blocker and needs to land before any of the items below can be validated against a green build, so I'm folding the doc/test fixes I'm accepting from this review into the same follow-up commit as the db2 fix rather than pushing several small commits.

Issue 1 (versionless provided deps, atomic coupling to the parent pom) - agree on the fix, pushing back on High. The coupling is real, but I'd characterize it as a discoverability gap, not a silent-failure risk: connector-cdc-base/pom.xml, connector-cdc/pom.xml's relocated dependencyManagement, and the shade filters are all in the same commit set of this same PR - there's no partial-apply path within the PR itself. A downstream cherry-pick that took only the base module's diff would fail loudly at the very next mvn compile (unresolved artifact version, since a versionless provided dependency has nothing to fall back to) - not silently degrade. Still, a pointer comment costs nothing and helps exactly the backport scenario you're describing, so I'll add one next to the versionless declarations in connector-cdc-base/pom.xml pointing at connector-cdc/pom.xml's management block.

Issue 2 (no runtime fail-fast for missing Debezium classes) - agree, will add. The failure mode you describe is real, though it's only reachable through a configuration the docs already call unsupported (mixing pre- and post-change plugin jars in one plugins dir). Still, turning an opaque NoClassDefFoundError into a message that names the missing dependency and points at incompatible-changes.md is cheap and strictly better for whoever hits it despite the docs. Will add a Class.forName("io.debezium.embedded.EmbeddedEngine") probe at DebeziumAdapter resolution.

Issue 3 (patched-vs-stock class race, no runtime detection) - agree it's not fully closed, but it's narrower than it reads. stockDebeziumCoreOverridesAreExcludedInEveryCdcShadeConfiguration (added in the last couple of commits) now mechanically parses both shade-filter lists and asserts they match the expected exclude set byte-for-byte, including the $* nested-class patterns - that catches a regression in the filter content at build time, for every connector in this repo. What it can't catch is a regression in the filter mechanism itself (a combine.children typo elsewhere, or a plugin-config inheritance change) - your runtime ProtectionDomain/code-source assertion idea would close that. I'd rather stage that, plus the bigger FQCN-relocation idea, as a dedicated follow-up rather than grow this PR's diff further - it's a packaging-strategy change, not a scope change. Will open a tracking issue referencing this thread so it doesn't get lost.

Issue 4 (test fragile to jar-classpath / working-directory) - agree, straightforward, will fix. PatchedDebeziumClassesTest's Files.isDirectory check on the ProtectionDomain code source does assume an exploded target/classes layout, and the two pom reads do rely on the working directory. Will add a jar-entry fallback for the class enumeration and anchor the pom reads to an explicit basedir instead of CWD.

Issue 5 (downstream Maven consumers lose the curated exclusions) - agree, doc fix. Correct that kafka-log4j-appender, the glassfish jersey exclusion, and the zstd-jni M1 override now live in connector-cdc's dependencyManagement, which a third party depending on connector-cdc-base as a plain library dependency does not inherit. Will add the exact <exclusions> block to both incompatible-changes.md migration notes (en/zh) so it can be copy-pasted rather than reconstructed.

Issue 6 (wall-of-text docs, no version stated) - agree on structure. Will split the Impact bullet into sub-bullets (upgrade-as-a-set / new dependency declarations / shade exclusions) and add a fenced XML snippet with the literal exclude patterns, both docs. On the release version: this PR is still pre-merge and blocked on the db2 fix above, so I'd rather not hardcode a version number that may shift before it actually lands - will fill that in once we're closer to a release cut rather than commit to a guess now.

Issue 7 (zstd-jni pin, native-lib drift risk) - one factual correction, then agree on the ask. The 1.5.5-5 pin (with the Apple-M1 rationale comment) wasn't deleted - it moved to connector-cdc/pom.xml's dependencyManagement alongside debezium-api/debezium-embedded, same idiom, same file, lines 94-98 at this head. So today every connector still resolves through exactly one place, which is what you're asking for. What's genuinely missing is a build-time guard against a future per-connector override drifting away from that single pin - agree that's worth having. Will add a maven-enforcer dependencyConvergence-style rule scoped to the CDC modules as a non-blocking follow-up.

Issue 8 (PR description contradicts the docs/pom comment on whether the jar still ships io.debezium classes) - confirmed and fixed. You're right, the description said "connector-cdc-base-*.jar no longer contains io.debezium classes" while the pom comment and both incompatible-changes.md entries correctly say five patched classes remain. I've updated the description just now to say the jar no longer contains the stock Debezium runtime classes while the five patched classes stay, matching the actual docs.

Summary of what happens next: the db2 fix (mechanical, matching the pattern already applied to the other seven connectors) is the real blocker and lands first; Issues 1 (comment), 4, 5, 6 and 8 are folded into that same follow-up since they're all doc/test-robustness fixes with no runtime behavior change; Issues 2, 3 and 7 are accepted non-blocking follow-ups I'll track separately so they don't get lost in a growing diff. Thanks again for staying on this - the shade-filter and patched-class discipline in this thread has meaningfully improved over the last several rounds, and Issue 8 in particular was a real accuracy bug in my own PR description that's now fixed.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base shipped the Debezium runtime (debezium-api, debezium-embedded, transitively debezium-core) at compile scope, and AbstractPluginDiscovery#filterPluginJar/selectPluginJar force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc". That made one shared jar the single project-wide Debezium version — no individual connector could actually diverge, even though the debezium.version property added in #10799 looked configurable per module.
  • Fix approach: flip debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocate the version + exclusion dependencyManagement into the connector-cdc parent, have each of seven Debezium-based connectors declare and package its own runtime Debezium, add a machine-checked maven-shade-plugin filter that drops debezium-core's stock copies of the five classes connector-cdc-base patches, and register a (still inert) DebeziumAdapter SPI per connector.
  • One-sentence summary: a sound packaging refactor for the seven connectors it was written against, still blocked by one concrete, previously-identified regression (connector-cdc-db2 no longer compiles) that has not moved in the last four days.

Re-verification note (this is not a fresh full review)

I gave this PR a full, from-scratch re-review on 2026-08-28 (#pullrequestreview- at that timestamp) and found one new High-severity, code-breaking issue: connector-cdc-db2 — a Debezium-based CDC connector merged into dev after this branch was opened, pulled in by the 2026-08-27 rebase — was never migrated to declare its own debezium-api/debezium-embedded, so it no longer compiles under this PR's provided-scope change. I replied the same day (2026-08-29) committing to push a follow-up commit folding in that db2 fix together with several accepted non-blocking items from @SEZ9's parallel review.

Checking today (2026-08-31, three days later), per the mandatory "fetch all reviews/comments/CI first" step of this review pass:

  • Head is unchanged: git log -1 on a fresh fetch of this exact PR ref still resolves to ef25cb1e3e6346ce8bb88f34ea783dcafc4385b8, last commit timestamp 2026-08-27T16:07:09Z — no commit has landed since my 2026-08-29 reply.
  • No new review or comment from anyone since my own 2026-08-29 reply.
  • CI is still failing at the exact same run (https://github.com/DanielLeens/seatunnel/actions/runs/33091568634, created 2026-08-27T16:07:24Z) — same seven jobs red (unit-test on JDK 8/11 × ubuntu/windows, updated-modules-integration-test-part-1/2), same root cause.
  • I re-pulled connector-cdc-db2/pom.xml directly from the current head (not trusting my 08-28 notes) to confirm the blocker is still literally present:
    <dependency>
        <groupId>org.apache.seatunnel</groupId>
        <artifactId>connector-cdc-base</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>io.debezium</groupId>
        <artifactId>debezium-connector-db2</artifactId>
        <exclusions>
            <exclusion><groupId>io.debezium</groupId><artifactId>debezium-core</artifactId></exclusion>
            <exclusion><groupId>io.debezium</groupId><artifactId>debezium-api</artifactId></exclusion>
            ...
    Still no own debezium-api/debezium-embedded/zstd-jni declaration — the exact gap identified on 08-28. gh pr diff --name-only confirms the PR's changed-file set is still exactly the same 31 files, and connector-cdc-db2/** is not among them.
  • The docs/en and docs/zh incompatible-changes.md "Impact" bullet still reads "No change for CDC job configuration, runtime behavior or checkpoint compatibility — every in-tree connector still resolves Debezium 1.9.8.Final" — still inaccurate while connector-cdc-db2 fails to build (this is Issue 2 below, unchanged).

Since nothing about the code has changed, I am not re-deriving the shade-filter plugin-inheritance chain, the DebeziumAdapterFactory call-site check, or the openGauss/Postgres coupling analysis a third time — those were derived in full in my 2026-08-16 review and re-confirmed in full in my 2026-08-28 review, both still visible on this thread, and nothing in the diff or in dev's recent history has touched them since. Sections 1–3 below summarize those stable conclusions; only the Issue table and CI diagnosis reflect today's independent re-check.

1. Code Change Review

1.1 Core Logic Analysis (stable since 2026-08-28, independently re-confirmed today)

For the seven connectors this PR actually touches (mysql, postgres, oracle, sqlserver, mongodb, opengauss, vitess): the provided-scope flip in connector-cdc-base, the relocated dependencyManagement in the connector-cdc parent, and each connector's own debezium-api/debezium-embedded/zstd-jni declaration correctly restore per-connector Debezium ownership without changing the resolved class set (all still pin 1.9.8.Final). The debezium-core shade filter (connector-cdc/pom.xml, <filters combine.children="append">) correctly reaches the six inheriting connectors, and connector-cdc-opengauss correctly restates the identical filter at execution level (an execution-level <filters> block overrides rather than merges with the inherited plugin-level one). DebeziumAdapterFactory.getAdapter(...) still has zero production call sites, so the SPI itself changes no runtime behavior. connector-cdc-tidb is correctly untouched (zero io.debezium references).

connector-cdc-db2 is the one connector this analysis does not hold for, confirmed again today: it depends on connector-cdc-base (now provided-scope for Debezium) and excludes debezium-core/debezium-api from its own debezium-connector-db2 dependency without ever re-adding them directly — the same pattern the seven migrated connectors also use, but which only works for them because they separately re-added debezium-api+debezium-embedded. Maven never propagates a provided-scope transitive dependency, so db2's effective view of every io.debezium.* type collapses to nothing. This breaks Db2Dialect, Db2SourceConfig, Db2SourceFetchTaskContext, Db2SnapshotFetchTask, Db2SnapshotSplitReadTask and others, all constructed on every DB2 CDC job's normal startup path (snapshot or streaming) — not a boundary or recovery-only path.

1.2 Compatibility Impact

Partially incompatible, unchanged classification. Fully compatible for the seven Debezium-based connectors this PR migrates (no config/API/checkpoint change, same resolved Debezium version before/after). Honestly documented for third-party/out-of-tree connectors. Incorrect for connector-cdc-db2, an in-tree connector: the docs' "no change to runtime behavior" claim is false for it today, since it cannot produce a runnable artifact at all at this head.

1.3 Performance / Side-Effect Analysis

Unchanged: no CPU/memory/GC/concurrency change on any job path for connectors that build. The Debezium runtime is now duplicated into each of the seven migrated connector jars instead of shared, growing the distribution by an undocumented (only qualitative) amount — carried-over Low issue.

1.4 Error Handling and Logging

No new error-handling paths in production code (build/packaging + inert SPI/test scaffolding only). Nothing to flag beyond the Issue 1 compile failure below, which is a build-time failure, not a runtime error-handling gap.

2. Code Quality Assessment

2.1 Coding Standards

Unchanged: license headers present on all new files, comments explain why (classloader-merge rationale, shade-filter override/merge distinction) rather than restating what, Javadoc present on every new class/field/method that needs it. No issues here.

2.2 Test Coverage and Test Stability

Rating: Stable. All new test classes (PatchedDebeziumClassesTest + one *DebeziumAdapterTest per connector) are deterministic — compiled-class inspection, hardened XML parsing of the two source POMs (external entities disabled), ServiceLoader discovery, resolved-artifact code-source checks. No Thread.sleep, network I/O, fixed ports, floating-point comparisons, or shared mutable static state. No flakiness risk in what these tests do exercise.

Real, still-open gap: none of these tests would have caught, and still would not catch, a new Debezium-based CDC connector landing without this PR's per-connector pattern (exactly what happened with db2) — there is no structural/reactor-level guard asserting "every connector depending on debezium-connector-* also declares its own debezium-api". This is Issue 3 below (carried over from 08-28's Issue 5).

2.3 Documentation Updates

docs/en/docs/zh incompatible-changes.md remain updated and symmetric. The one remaining accuracy problem is the "no change to runtime behavior" claim given the still-open db2 compile failure (Issue 2 below).

3. Architectural Soundness

3.1 Elegance of the Solution

Precise, long-term fix for the actual root cause (a force-merged shared jar pinning one project-wide Debezium version), not a workaround. The dependencyManagement-in-parent / dependency-in-leaf pattern is the correct Maven idiom here, and the shade filter is now mechanically verified (stockDebeziumCoreOverridesAreExcludedInEveryCdcShadeConfiguration) rather than resting on comment discipline alone.

3.2 Maintainability

Net improvement for the seven connectors this PR covers, but the migration pattern is per-connector, hand-applied, and currently unenforced at the "does every Debezium-based connector module opt in" level — connector-cdc-db2 is the live proof that this is easy to miss when a new connector lands via an unrelated, routine feature PR, and it is still missed four days after being identified.

3.3 Extensibility

DebeziumAdapter remains a reasonable seam for future per-connector runtime isolation, correctly unwired into production code per its @Experimental Javadoc.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, offset tracking, or serialized job state for any connector that builds — zero .java files under existing production packages are modified by this diff. Does not extend to connector-cdc-db2, which cannot produce a runnable artifact at this head.

4. Issue Summary

# Issue Location Severity Raised by another reviewer
1 connector-cdc-db2 was never migrated to declare its own debezium-api/debezium-embedded/zstd-jni; it no longer compiles after connector-cdc-base's Debezium deps moved to provided. Re-confirmed today against the live head (ef25cb1e3e6): the pom is byte-for-byte unchanged from 08-28, connector-cdc-db2/** is still absent from the PR's 31-file diff, and the fork's unit-test/updated-modules-integration-test-* jobs are still red at this exact compile step. seatunnel-connectors-v2/connector-cdc/connector-cdc-db2/pom.xml High No (originally raised by me, 2026-08-28; still unresolved)
2 The incompatible-changes.md "Impact" bullet still claims no runtime-behavior change, which remains false for connector-cdc-db2 given Issue 1 docs/en/introduction/concepts/incompatible-changes.md; docs/zh/introduction/concepts/incompatible-changes.md Medium (tracks Issue 1; resolves once Issue 1 is fixed) No
3 No structural guard fails the build when a future new Debezium-based CDC connector omits this PR's per-connector declaration pattern (the exact class of bug behind Issue 1) connector-cdc-base / connector-cdc test scope Medium No
4 Distribution size growth documented only qualitatively, no measured delta docs/en, docs/zh incompatible-changes.md Low Yes (SEZ9, 2026-08-25; me, 2026-08-24/08-28)
5 No test inspects an actual built/shaded jar, so the shade-filter merge mechanism at package time is still not directly CI-verified (the POM-level filter-content check substantially narrows, but does not close, this gap) PatchedDebeziumClassesTest.java Low Yes (SEZ9; me, prior rounds)

5. Merge Recommendation

Conclusion: Not recommended for merge

This is my own PR, so GitHub blocks self-approval regardless of my conclusion — posting as a comment so another maintainer can act on the recommendation below. It agrees with, and does not soften, my own 2026-08-28 full review's conclusion.

  1. Blockers — must be fixed

    • Issue 1 (High): add the same per-connector debezium-api/debezium-embedded/zstd-jni declarations (and a DebeziumAdapter + META-INF/services + test for parity, if desired) to connector-cdc-db2/pom.xml, matching the pattern already applied to the other seven connectors. This is small and mechanical — I committed to pushing it on 2026-08-29 and it is still outstanding three days later, which is the actual reason this PR remains not-mergeable today, not a new design problem. After the fix, the fork's unit-test and updated-modules-integration-test-* jobs need to go green on the new head before this can move to "ready to merge after fixes".
    • Issue 2 (Medium, bundled with Issue 1): once db2 is fixed, no further doc change should be needed since the "no runtime behavior change" claim becomes true again for all in-tree connectors.
  2. Recommended fixes — non-blocking

    • Issue 3: add a lightweight structural check (enumerate connector-cdc-* modules depending on any debezium-connector-* artifact, assert each also declares its own debezium-api) so this exact class of gap cannot recur silently.
    • Issue 4: replace the qualitative distribution-size note with a measured delta.
    • Issue 5: add one packaging-level assertion against a built/shaded connector jar.

On the record so far: @SEZ9's 2026-08-28 review (8 items) was already responded to in full in my 2026-08-29 comment — the db2 fix (this Issue 1) was scoped to land in the same follow-up commit as the accepted items from that review (their Issues 1, 4, 5, 6, 8 in my numbering there). None of that has been pushed yet; today's check is a pure re-verification, and it confirms the situation is exactly as described in that comment, just three days further along with no code movement.

Overall assessment: the core mechanism is sound and well-tested for the seven connectors it was designed for, and introduces no checkpoint/savepoint/serialization risk for any connector that builds. The blocker is small, well-understood, and has had a committed fix plan sitting unshipped since 2026-08-29 — the fastest path to green is simply pushing the db2 pom change that mirrors the other seven connectors' migration.

This PR moves Debezium ownership from connector-cdc-base to each CDC
connector, but connector-cdc-db2 was added to dev by apache#10780 after this
branch was cut and never received the debezium-api, debezium-embedded,
and zstd-jni dependencies the other connectors (mysql, sqlserver,
postgres, oracle, opengauss, mongodb, vitess) already declare. Without
them, connector-cdc-base's now-provided-scope copies are absent from
this connector's classpath and it fails to compile.

Adds the same three dependencies with no explicit version, so they
resolve through connector-cdc's dependencyManagement at the inherited
debezium.version, matching the existing per-connector pattern exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

connector-cdc-base shipped the Debezium runtime (debezium-api, debezium-embedded, transitively debezium-core) at compile scope, and AbstractPluginDiscovery#filterPluginJar/selectPluginJar force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc". That made one shared jar the single project-wide Debezium version — no individual connector could actually diverge, even though the debezium.version property added in #10799 looked configurable per module. This PR flips debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocates the version + exclusion dependencyManagement into the connector-cdc parent, and has each Debezium-based CDC connector declare and package its own runtime Debezium.

Re-review at the new head bf91ef1f42df. One real commit since my previous full review (2026-09-01, head ef25cb1e3e63): [Fix][Connector-V2] Add owned Debezium runtime deps to connector-cdc-db2. This is the exact fix for the High-severity blocker I first raised on 2026-08-28 and re-confirmed unresolved in every round since: connector-cdc-db2 — merged into dev after this branch was cut — was never migrated and no longer compiled once connector-cdc-base's Debezium deps moved to provided. I re-pulled the full diff and the complete current state of every touched module fresh at this head, not carried forward from memory.

1. Code Change Review

1.1 Core Logic Analysis

The db2 fix is correct and mechanically identical to the working pattern already verified for the other seven connectors. The commit adds exactly three unversioned dependency declarations to connector-cdc-db2/pom.xml — debezium-api, debezium-embedded, com.github.luben:zstd-jni — with no <version> tag, so they resolve through connector-cdc/pom.xml's dependencyManagement at the inherited debezium.version (1.9.8.Final), exactly the idiom already used by connector-cdc-mongodb (and the other six). I diffed db2's pom against mongodb's pom side-by-side to confirm the declarations are structurally identical, not just similar in spirit.

I also independently re-verified the two things this fix depends on rather than trusting the commit message:

  • Shade filter coverage: connector-cdc/pom.xml's <build><plugins><plugin>maven-shade-plugin> block (not pluginManagement) declares the debezium-core exclusion filter with combine.children="append". connector-cdc-db2/pom.xml has no shade-plugin override of its own, so it inherits this filter the same way the six non-opengauss connectors already do (opengauss needed its own restatement only because it overrides the filter at execution level, which db2 does not). db2 needs no additional shade configuration.
  • PatchedDebeziumClassesTest still covers db2 correctly: this test only reads two shade-filter lists — connector-cdc/pom.xml's own list and connector-cdc-opengauss/pom.xml's execution-level restatement — to assert both cover the same four patched classes. Since db2 inherits the first list rather than declaring its own, this test's existing coverage already extends to db2 with no test change required.

Registration-matrix check across every CDC connector in the repo (not just the ones the PR description names), independently enumerated via git ls-tree rather than trusting the PR's own file list:

Module Uses Debezium? Migrated to own debezium-api/embedded/zstd-jni?
connector-cdc-mysql Yes Yes (prior commits)
connector-cdc-postgres Yes Yes (prior commits)
connector-cdc-oracle Yes Yes (prior commits)
connector-cdc-sqlserver Yes Yes (prior commits)
connector-cdc-mongodb Yes Yes (prior commits)
connector-cdc-opengauss Yes Yes (prior commits)
connector-cdc-vitess Yes Yes (prior commits)
connector-cdc-db2 Yes Yes — fixed this round
connector-cdc-tidb No N/A — independently confirmed zero io.debezium references in both its pom.xml and its entire src/ tree at this head

I also walked dev's history since this branch's merge-base (e6caf5a4, 171 commits behind) specifically for anything touching seatunnel-connectors-v2/connector-cdc/: seven commits landed, all fixes/features to connectors already on the migrated list above (mongodb, postgres, oracle) — no new CDC connector module was added to dev in that window. So the registration matrix is now complete: every Debezium-based CDC connector in the repository, including ones that landed after this branch was originally cut, is covered.

One real, non-blocking inconsistency found on this pass: the other seven connectors each also got a dedicated XxxDebeziumAdapter.java + META-INF/services/...DebeziumAdapter registration + a XxxDebeziumAdapterTest; db2 got only the pom dependency fix, with no Db2DebeziumAdapter counterpart. Since DebeziumAdapterFactory.getAdapter(...) still has zero production call sites (confirmed unchanged this round — the SPI remains @Experimental and inert), this has no runtime effect today. But it does mean db2 is the one Debezium-based connector without adapter-SPI parity, which is worth closing before the SPI is ever wired into a real code path (see Issue 6 below).

Also observed: unlike its seven siblings, db2's pom does not declare its own local <properties><debezium.version> override — it inherits the parent's default (1.9.8.Final), which today resolves to the identical version every other connector uses, so there is no compatibility gap. It does mean db2 cannot yet independently diverge to a different Debezium line the way the docs describe as the point of this migration, until that property is added locally (see Issue 7 below).

1.2 Compatibility Impact

Now fully compatible for all in-tree connectors, correcting my prior "partially incompatible" classification. With db2 fixed, the incompatible-changes.md claim — "No change for CDC job configuration, runtime behavior or checkpoint compatibility — every in-tree connector still resolves Debezium 1.9.8.Final" — is now actually true for every connector in the repository, not just the seven this PR originally targeted. No further doc change is required; this was correctly anticipated in my 2026-09-01 review.

1.3 Performance / Side-Effect Analysis

Unchanged from prior rounds: no CPU/memory/GC/concurrency change on any job path for any connector. The Debezium runtime is now duplicated into each of the eight migrated connector jars instead of shared once, growing the distribution by an amount still only qualitatively documented (carried-over Low issue, unaffected by this round's fix).

1.4 Error Handling and Logging

No new error-handling paths. This round's change is a pure build/packaging fix — no runtime code is touched.

2. Code Quality Assessment

2.1 Coding Standards

The new dependency block in connector-cdc-db2/pom.xml carries the same explanatory comment style already used in the other seven connectors, correctly adapted to name connector-cdc-db2 specifically rather than being copy-pasted with a stale module name. License headers unaffected (pom-only change).

2.2 Test Coverage and Test Stability

Rating: Stable. No new test classes in this commit; the existing PatchedDebeziumClassesTest (deterministic, no Thread.sleep/network I/O/shared mutable state, per my prior rounds' analysis) already extends its coverage to db2 as explained above with no changes needed.

Still-open gap, unchanged: there remains no structural/reactor-level guard that fails the build when a future new Debezium-based CDC connector lands without this PR's per-connector declaration pattern — this is precisely the class of bug that made db2 an unmigrated blocker for two weeks. Issue 5 below (carried over, previously Issue 3) tracks this; the SPI-parity gap in Issue 6 is a related but distinct instance of the same underlying "the pattern is applied by hand, per-connector, with no enforcement" risk.

2.3 Documentation Updates

docs/en/docs/zh incompatible-changes.md are unchanged by this commit and correctly so — as noted in 1.2, no further doc update is needed now that db2 is fixed.

3. Architectural Soundness

3.1 Elegance of the Solution

The db2 fix is exactly the minimal, mechanical change needed — no scope creep, no attempt to also retrofit the adapter-SPI parity gap into this same commit, which keeps the fix easy to review and verify in isolation.

3.2 Maintainability

The core dependencyManagement-in-parent / dependency-in-leaf pattern remains a net maintainability improvement for the connectors it fully covers. The adapter-SPI and debezium.version-property gaps on db2 (Issues 6, 7) are exactly the kind of partial-migration residue that makes this pattern harder to audit at a glance — a reviewer scanning "does every CDC connector look the same" will now find db2 structurally different from its seven siblings in two small but real ways.

3.3 Extensibility

DebeziumAdapter remains correctly unwired into production code. Db2's missing property override (Issue 7) is the one place where this round's fix, while functionally correct today, doesn't yet deliver the stated extensibility goal ("each CDC connector declares the Debezium version it ships") for that specific connector.

3.4 Historical-Version Compatibility

No impact on checkpoint/savepoint, offset tracking, or serialized job state — this commit touches only connector-cdc-db2/pom.xml, zero .java files.

CI Status

The apache-side Build check shows pending/queued. I checked the fork (DanielLeens/seatunnel) run for this exact head (bf91ef1f42df) directly: it moved from queued to in_progress during this review, but the jobs that would actually validate this fix — unit-test and updated-modules-integration-test-*, the same jobs that were red on the previous head specifically because of the db2 compile failure — had not started yet at review time; only License header, Code style, and Dead links had completed (all success, unsurprising for a pom-only change). I am explicitly not claiming a green build for this head; I'm reporting what I verified by direct source and dependency-graph inspection (the pom declarations, the shade-filter inheritance chain, and the test's coverage of that inheritance), which is strong evidence the fix is correct, separately from the CI signal which simply hasn't reached the relevant jobs yet. No rerun is needed — this run is actively progressing on its own.

4. Issue Summary

# Issue Location Severity Status
1 connector-cdc-db2 was never migrated to declare its own debezium-api/debezium-embedded/zstd-jni; failed to compile after connector-cdc-base's Debezium deps moved to provided seatunnel-connectors-v2/connector-cdc/connector-cdc-db2/pom.xml High Fixed this round (bf91ef1f42df), verified against source and the shade-filter/test inheritance chain
2 The incompatible-changes.md "Impact" bullet claimed no runtime-behavior change while db2 could not compile docs/en|zh/introduction/concepts/incompatible-changes.md Medium Resolved — claim is now true for all in-tree connectors, no doc change needed
3 Distribution size growth documented only qualitatively, no measured delta docs/en, docs/zh incompatible-changes.md Low Still open (carried over)
4 No test inspects an actual built/shaded jar; the shade-filter merge mechanism at package time is not directly CI-verified PatchedDebeziumClassesTest.java Low Still open (carried over)
5 No structural guard fails the build when a future new Debezium-based CDC connector omits this PR's per-connector declaration pattern — the exact class of bug behind Issue 1 connector-cdc-base / connector-cdc test scope Medium Still open (carried over; db2's two-week-unmigrated window is a live demonstration of this exact risk)
6 connector-cdc-db2 has no Db2DebeziumAdapter + META-INF/services registration + adapter test, unlike its seven Debezium-based siblings seatunnel-connectors-v2/connector-cdc/connector-cdc-db2 Low New this round — no runtime impact today since DebeziumAdapter has zero call sites, but should be closed before the SPI is ever wired in
7 connector-cdc-db2 does not declare its own local <properties><debezium.version> override, unlike its seven siblings; it inherits the parent default, which happens to match today but means db2 cannot yet independently diverge as the migration's docs describe seatunnel-connectors-v2/connector-cdc/connector-cdc-db2/pom.xml Low New this round

5. Merge Recommendation

Conclusion: Ready to merge after fixes

This is my own PR, so GitHub blocks self-approval regardless of my conclusion; posting as a comment so an independent maintainer can act on the recommendation below.

  1. Blockers — must be fixed

    • CI has not yet produced a result from the jobs that actually validate this fix (unit-test, updated-modules-integration-test-*) on this exact head. My source-level verification of the db2 pom change and its dependency on the already-proven shade-filter inheritance gives me high confidence this is correct, but I want the actual compile/test signal before calling this mergeable — this is a "wait for the run to finish" gate, not a design concern.
  2. Recommended fixes — non-blocking

    • Issue 5: add a lightweight structural check (enumerate connector-cdc-* modules depending on any debezium-connector-* artifact, assert each also declares its own debezium-api) so this exact class of gap — which just cost two weeks on db2 — cannot recur silently for the next new CDC connector.
    • Issue 6: add Db2DebeziumAdapter + META-INF/services + test for parity with the other seven connectors, before the SPI is ever given a production call site.
    • Issue 7: add db2's own <properties><debezium.version> declaration to match its siblings and actually deliver the per-connector override capability the docs describe.
    • Issue 3: replace the qualitative distribution-size note with a measured delta.
    • Issue 4: add one packaging-level assertion against a built/shaded connector jar.

Overall assessment: the actual blocker from every prior round — db2's broken compile — is fixed with the same minimal, correct pattern already proven for seven other connectors, and I independently re-verified both the dependency resolution and the shade-filter inheritance rather than trusting the commit message. I also independently confirmed no other CDC connector, old or newly landed in dev since this branch diverged, was missed. The two new Low-severity findings (adapter-SPI parity, missing local version property) are real but non-blocking gaps in an otherwise-complete migration; the only actual gate left before "ready to merge" is a completed, green CI run on this exact head.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base shipped the Debezium runtime (debezium-api, debezium-embedded, transitively debezium-core) at compile scope, and AbstractPluginDiscovery#filterPluginJar/selectPluginJar force-loads connector-cdc-base-*.jar into the class loader of every plugin whose name contains "cdc". That made one shared jar the single project-wide Debezium version — no individual connector could actually diverge, even though the debezium.version property added in #10799 looked configurable per module.
  • Fix approach: flips debezium-api/debezium-embedded/zstd-jni to provided in connector-cdc-base, relocates the version + exclusion dependencyManagement into the connector-cdc parent, and has each Debezium-based CDC connector declare and package its own runtime Debezium.
  • One-sentence summary: each CDC connector now genuinely owns and can independently version its Debezium runtime instead of silently sharing one project-wide jar.

Re-review at the new head 26f64641. One new commit since my previous full review (2026-09-13 00:45 UTC, head bf91ef1f42df): Merge branch 'dev' into daniel-debezium-per-connector-versions. I re-read the entire diff against dev from scratch, not just the incremental delta.

0. Disposition of Prior Blockers

This round's only new commit is a merge, but unlike a trivial no-op merge, it does bring in substantive dev changes — I checked carefully whether any of them touch this PR's own scope. git diff bf91ef1f42df 26f646419 --stat shows 49 files changed, but scoping it to what actually matters for this PR:

  • No connector-cdc-* pom.xml file is touched by the merge — confirmed via git diff bf91ef1f42df 26f646419 --stat -- '**/pom.xml', which lists only 36 unrelated new-connector poms (connector-amazondocumentdb, connector-azurecosmosdb, connector-deeplake, connector-facebook-ads, connector-firebase, connector-google-ads, four connector-http-* variants, connector-mem0, connector-nebulagraph, connector-snmp, connector-tiktok-ads, plus their dist/e2e wiring) — none of them Debezium-based CDC connectors. connector-cdc-db2/pom.xml, the file this PR's last commit fixed, is byte-identical (git diff ... -- connector-cdc-db2/pom.xml is empty).
  • The .java changes the merge does bring into connector-cdc-base and several already-migrated connectors (SchemaChangeResolver, SnapshotSplitAssigner, IncrementalSourceReader, PostgresRelationSchemaChangeResolver, a new Db2Dialect.toTableId(TablePath) override, etc. — ~2,600 lines) are real production changes from already-merged, unrelated dev PRs (schema-change-resolution work for mysql/postgres/oracle/mongodb/db2), not anything authored by this PR. I spot-checked the one db2-specific addition, Db2Dialect.toTableId(TablePath) (implementing a new DataSourceDialect interface method introduced by that merge), and it's a self-contained, unrelated feature addition — nothing here interacts with the Debezium dependency-scope change this PR makes.
  • No new Debezium-based CDC connector module landed in dev during this window — the pom diff above confirms this directly, so the registration matrix I completed last round (all 8 Debezium-based connectors migrated, connector-cdc-tidb confirmed Debezium-free) remains complete and doesn't need re-walking from scratch.

Issue 1 (High, connector-cdc-db2 compile failure) — fix unchanged, still not CI-confirmed after two rounds — see CI section, this is worth flagging as a process gap, not just a status update.
Issue 2 (Medium, stale incompatible-changes.md claim) — still resolved, unchanged.
Issues 3, 4, 5, 6, 7 — all unchanged, not touched by this round's merge (distribution-size documentation, packaging-level shade test, structural migration guard, db2 adapter-SPI parity, db2's missing local debezium.version property).

1. Code Change Review

1.1 Core Logic Analysis

No change to this PR's own logic this round — the merge is a no-op for every file this PR owns (all connector-cdc/pom.xml, connector-cdc-base/pom.xml, and the eight migrated connectors' pom.xml/XxxDebeziumAdapter.java/META-INF/services registrations are byte-identical to the previous round). The core design (dependency ownership moved to provided in the base, dependencyManagement centralized in the connector-cdc parent, per-connector dependency declarations resolving through it) remains what I traced and verified in prior rounds.

1.2 Compatibility Impact

Fully compatible, unchanged. With db2 fixed (still confirmed this round via unchanged pom + inherited shade-filter), the incompatible-changes.md claim remains true for every connector in the repository. This round's merge doesn't touch compatibility surface for this PR's own scope.

1.3 Performance / Side-Effect Analysis

Unchanged from prior rounds: no CPU/memory/GC/concurrency change on any job path. Distribution-size growth remains only qualitatively documented (Issue 3, carried over).

1.4 Error Handling and Logging

No new error-handling paths from this round's diff for this PR's own files.

2. Code Quality Assessment

2.1 Coding Standards

Unchanged assessment; no new production code owned by this PR this round.

2.2 Test Coverage and Test Stability

Rating: Stable, unchanged. PatchedDebeziumClassesTest's coverage of db2 (via inheriting connector-cdc/pom.xml's shade-filter list) is unaffected by this round's merge. The still-open structural-guard gap (Issue 5) is unchanged — worth restating given the merge window I just checked: no new Debezium-based connector landed, so the gap wasn't exercised again this round, but it also wasn't closed.

2.3 Documentation Updates

Unchanged; incompatible-changes.md not touched by this round.

3. Architectural Soundness

3.1-3.4

Unchanged from my last round's assessment — see prior review for the full trace. This round's merge doesn't touch anything relevant to elegance, maintainability, extensibility, or historical-version compatibility for this PR's own scope.

CI Status — a process concern worth being direct about

I need to correct something from my own last round. I wrote then: "this run is actively progressing on its own... no rerun is needed." That run (34726679909, head bf91ef1f42df) was subsequently marked cancelled — superseded by this round's merge push — before the jobs that actually validate the db2 fix (unit-test, updated-modules-integration-test-*) ever started. So my confidence that CI was "on its way to confirming" the fix did not pan out; it simply never got the chance to run. I want to be upfront about that rather than quietly re-asserting confidence this round.

The current head's run (34763680066, head 26f646419) is queued and has not started at review time. This means the db2 compile fix — the one High-severity blocker from four rounds ago — has now gone through three consecutive commits (bf91ef1f42df, and now 26f646419) without a single completed CI run reaching the jobs that would actually prove it compiles and passes tests. My source-level verification (pom dependency resolution through the parent's dependencyManagement, shade-filter inheritance, PatchedDebeziumClassesTest's coverage) remains unchanged and I still have high confidence in it — I traced it fresh again this round against the current head, not carried forward from memory — but "high confidence from source inspection" is not the same thing as an actual green run, and it's now been long enough without one that I think it's worth naming explicitly rather than repeating the same "should be fine, CI just hasn't caught up" framing a third time.

4. Issue Summary

# Issue Location Severity Status
1 connector-cdc-db2 was never migrated to declare its own debezium-api/debezium-embedded/zstd-jni; failed to compile after connector-cdc-base's Debezium deps moved to provided connector-cdc-db2/pom.xml High Fixed at source level (unchanged this round); still not CI-confirmed after three consecutive heads
2 incompatible-changes.md "Impact" bullet claimed no runtime-behavior change while db2 could not compile docs/en|zh/introduction/concepts/incompatible-changes.md Medium Resolved, unchanged
3 Distribution size growth documented only qualitatively docs/en, docs/zh incompatible-changes.md Low Still open
4 No test inspects an actual built/shaded jar PatchedDebeziumClassesTest.java Low Still open
5 No structural guard fails the build when a future new Debezium-based CDC connector omits the per-connector declaration pattern connector-cdc-base / connector-cdc test scope Medium Still open; not re-exercised this round since no new Debezium connector landed in the merge window
6 connector-cdc-db2 has no Db2DebeziumAdapter + META-INF/services registration + adapter test, unlike its seven siblings connector-cdc-db2 Low Still open
7 connector-cdc-db2 doesn't declare its own local debezium.version property override connector-cdc-db2/pom.xml Low Still open

5. Merge Recommendation

Conclusion: Ready to merge after fixes

This is my own PR, so GitHub blocks self-approval regardless of my conclusion; posting as a comment so an independent maintainer can act on the recommendation below.

  1. Blockers — must be fixed:

    • CI still has not produced a completed result from the jobs that actually validate the db2 fix (unit-test, updated-modules-integration-test-*), across three consecutive heads now. I'd like an actual green run before calling this unconditionally mergeable — my source-level confidence is high and unchanged, but it needs to be backed by a real result at this point rather than reasserted a third time.
  2. Recommended fixes — non-blocking:

    • Issue 5: add a lightweight structural check (enumerate connector-cdc-* modules depending on any debezium-connector-* artifact, assert each also declares its own debezium-api) so this exact class of gap cannot recur silently for the next new CDC connector.
    • Issue 6: add Db2DebeziumAdapter + META-INF/services + test for parity with the other seven connectors.
    • Issue 7: add db2's own debezium.version property declaration to match its siblings.
    • Issue 3: replace the qualitative distribution-size note with a measured delta.
    • Issue 4: add one packaging-level assertion against a built/shaded connector jar.

Overall assessment: the substance of this migration remains sound and unchanged — I re-verified the full registration matrix and confirmed this round's merge brings in no new Debezium-based connector and touches none of this PR's own files. The one thing I want to be honest about is process, not code: the db2 fix has now gone three heads without CI actually validating it, and my own prior-round assumption that "the run is progressing, no action needed" turned out to be wrong once that run got superseded. I'm not downgrading my source-level confidence in the fix itself, but I don't want to keep restating "should be fine" in place of an actual result — let's get one completed run on the current head before treating this as unconditionally ready.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up on the CI state only — no new commit and no other new activity since my last full review at this exact head (26f646419).

CI fact changed since my last review. My previous round's only remaining blocker was "CI still has not produced a completed result from the jobs that actually validate the db2 fix, across three consecutive heads." That run has since completed, and it did not come back clean — Build finished with FAILURE.

Diagnosis. The failing jobs are updated-modules-integration-test-part-3 (8, ubuntu-latest) and updated-modules-integration-test-part-3 (11, ubuntu-latest) (fork run 34763680066), both failing on the same test: OpengaussCDCIT.testAddFieldWithRestore, an Awaitility ConditionTimeoutException on the restored-row assertion. I checked the raw job logs for a classloading/shading-related root cause (NoClassDefFoundError, NoSuchMethodError, missing-service exceptions) given that this PR does rewrite connector-cdc-opengauss's Debezium dependency scope and shaded-jar filters (including excluding the patched HistorizedRelationalDatabaseConnectorConfig/TableId/ChangeEventQueue/HeartbeatFactory classes from that module's own shading step) — I found none; the container starts, the job runs its full ~520-540s duration, and only the final assertion times out.

Importantly, I also found the identical failure — same test class, same method, same day — in the CI run of a completely unrelated PR that does not touch CDC/Debezium code at all (it's a read-only REST diagnostics endpoint). Both PRs are also missing the same 5 commits behind current dev, none of which touch CDC/Debezium/Opengauss. That cross-PR correlation is fairly strong evidence this is a pre-existing, environment-sensitive flake in OpengaussCDCIT.testAddFieldWithRestore rather than something this PR's shading-filter change caused. That said, I'm not fully closing this out as "unrelated" on that evidence alone — this PR does directly modify the shaded-class filter list for exactly the Debezium schema-history/relational classes this schema-change-and-restore test exercises in this exact module, so I'd rather see one clean, confirmed rerun on this head (or a rerun with -Dtest=OpengaussCDCIT in isolation) than assume it away. If it passes on rerun, that closes the loop; if it fails again specifically on this PR while passing on dev/other PRs, that would flip my read of it.

Blocker status, updated: Issue 1 (High) stays open, but the substance changes from "still waiting on a completed run" to "the completed run failed, on a test that plausibly intersects this PR's own module changes, and I want a clean confirmed rerun before calling this unconditionally CI-clean." Everything else from my last round (the recommended non-blocking fixes — Issues 3-7, and the disposition of the dev merge commit) is unchanged.

Overall conclusion is unchanged: Ready to merge after fixes, gated on Issue 1. Self-approval remains unavailable on this account regardless of the CI outcome; an independent maintainer still needs to give the final sign-off.

🤖 Generated with Claude Code

@DanielLeens

Copy link
Copy Markdown
Contributor Author

Synced dev (3ac2d1c) into this branch as 93e442c to pick up the current CI matrix and the merged upstream fixes. Plain merge commit, no conflicts; the diff against dev is still the same 32 files (per-connector Debezium pom declarations, DebeziumAdapter SPI implementations and tests, incompatible-changes.md entries); none of the pom files conflicted and I did not hand-edit any of them. One thing to watch in CI: dev now contains the new connector-cdc-pg-base module from #11029, which depends on connector-cdc-base as provided and imports io.debezium without declaring Debezium itself, so it previously picked Debezium up transitively from connector-cdc-base. With this PR making those dependencies provided in connector-cdc-base, that module may need its own Debezium declaration; that is a pom change outside this sync and is left for the PR owner to decide. Compile and tests are left to the fork Build run per the validation policy: https://github.com/DanielLeens/seatunnel/actions/runs/41163009007

…t connector-cdc-base no longer exports it

connector-cdc-pg-base was added on dev after this PR was written and relied on the Debezium classes that connector-cdc-base used to export at compile scope. Debezium is now provided in connector-cdc-base, which is not transitive, so declare debezium-api and debezium-embedded here with provided scope. They are not bundled: connector-cdc-postgres and connector-cdc-opengauss keep owning the Debezium runtime.
@DanielLeens

Copy link
Copy Markdown
Contributor Author

已处理上一条评论中标记的 connector-cdc-pg-base 依赖缺口,新 head 为 51f37b1ae00(仅 1 个文件:connector-cdc-pg-base/pom.xml,+29 行,已获维护者明确同意修改该 pom)。

根因(基于 pom 作用域的推断,不是实测构建失败): dev 上新增的 connector-cdc-pg-base 的 PgBaseIncrementalSource 及其测试直接 import io.debezium.*,此前靠 connector-cdc-base 的 compile 作用域传递获得 Debezium。本 PR 将 connector-cdc-base 中的 Debezium 改为 provided,而 provided 不会传递,因此该模块在本 PR 上存在编译失败风险。

修复: 在 connector-cdc-pg-base/pom.xml 中显式声明 debezium-api、debezium-embedded(版本由父 pom 的 dependencyManagement 按本模块 ${debezium.version} 解析,本模块按本 PR 约定声明 debezium.version=1.9.8.Final),作用域为 provided:只参与编译与测试,不会被打入 jar,Debezium 运行时仍由 connector-cdc-postgres / connector-cdc-opengauss 各自持有。XML 已用 xmllint 校验。

验证范围: 本地仅做了 pom 的 XML 语法校验;按 Apache SeaTunnel 的验证规则未在本地编译或运行测试,以 fork 上新 head 的 Build 为准(run 见下)。在该 run 通过之前,不能认为该修复已被验证。

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up. I re-read the complete current 33-file diff and the plugin-loading/CDC callers. The DB2 dependency repair remains present and I do not find a new supported-deployment source blocker. The current merge facts have changed, however: GitHub now reports actual merge conflicts, and this head's fork Build was cancelled.

What this PR solves

  • User pain: connector-cdc-base bundles the stock Debezium runtime and is loaded into every CDC plugin, preventing a connector from owning its runtime version.
  • Fix: make those base dependencies provided, declare runtime dependencies in each connector, filter duplicate stock classes and add adapter/packaging guards.
  • In one sentence: move Debezium packaging ownership to the plugin while preserving shared SeaTunnel patches. For example, MySQL can own its supported binary-compatible runtime instead of inheriting the base jar's stock payload.

1. Code Change Review

1.1 Core Logic Analysis

Local branch seatunnel-review-11639 matches 51f37b1ae0091c5162a4e80df5cf6c04dd5a6fdd. Full Git diff/source searches and static XML inspection covered all changed POMs, adapters, service registrations, tests and both language docs. Adapter/test variants were also compared across modules, including OpenGauss's deliberately inherited PostgreSQL provider. No local build, test, container or job ran; no built-jar contents are claimed verified.

Before, base declared compile-scope Debezium artifacts. After:

<artifactId>debezium-embedded</artifactId>
<scope>provided</scope>

Each runtime-owning connector declares compile-scope api/embedded/zstd; parent dependencyManagement retains version resolution and exclusions. The shade filter explicitly excludes each patched stock outer class and its $* nested classes.

plugin discovery -> filterPluginJar (AbstractPluginDiscovery.java:514)
 -> connector jar + shared connector-cdc-base jar in CDC plugin loader
 -> source/readers/stream fetch -> existing Debezium runtime calls
 -> stock core/embedded/connector classes now supplied by connector jar
 -> four replaced core class families supplied only by patched base classes
 -> SeaTunnel-only DefaultHeartbeatConnectionProvider remains in base
Maven parent management -> per-module dependencies -> inherited shade filters
 OpenGauss own filter list -> explicitly restated core exclusions
UT guards -> compiled patch set + both source POM filters + artifact metadata
SPI registration -> DebeziumAdapterFactory unique-provider lookup in adapter tests

Key findings:

  • The normal CDC plugin path reaches the packaging change; this is not only a property rename.
  • Base still contains five patched classes; four replace stock classes, while DefaultHeartbeatConnectionProvider has no stock counterpart.
  • DB2 now declares all three runtime dependencies. Pg-base declares compile/test-needed Debezium as provided; PostgreSQL/OpenGauss package the runtime.
  • All current version declarations remain 1.9.8.Final, with zstd-jni centrally pinned at 1.5.5-5. OpenGauss tests intentionally use exactly one inherited PostgreSQL adapter, avoiding duplicate providers for one connector class.
  • A whole-tree call-site search finds adapter-factory invocations in tests, not normal production execution. These providers are preparatory metadata/SPI infrastructure; they do not yet isolate the base's direct Debezium API usage or enable arbitrary major-version divergence.

1.2 Compatibility Impact

Partially incompatible packaging contract, with migration documented in both incompatible-changes files. No CDC option/default, checkpoint or wire format is changed. Operators must upgrade the base and deployed CDC jars as one release set; mixed old/new sets are unsupported. Third-party connectors must explicitly supply runtime dependencies and stock-patch exclusions. Binary-incompatible Debezium upgrades still require moving direct base usage behind the SPI; this PR does not complete that work.

1.3 Performance / Side Effects

No new record-loop allocations, worker threads, locks, network requests, retries or resource ownership. Runtime/compression/Kafka dependencies are duplicated per plugin, increasing disk/download footprint and potentially loaded-class/native-library pressure across multiple plugin loaders. The description's approximate size estimates are not independently measured here; the guide remains qualitative (existing nonblocking Issue 3). Same-runtime source semantics are retained, but packaged-artifact CI must validate class resolution.

1.4 Error Handling and Logging

No new supported matched-release source blocker found. SPI lookup clearly rejects zero/multiple matches, but is not invoked by the ordinary production path, so it is not a runtime packaging fail-fast guard. Mispackaged third-party/mixed jars can still fail opaquely or resolve duplicates; they should not be described as supported.

Community reconciliation:

  • I agree with @SEZ9's duplicate-patch concern: both parent and OpenGauss filters are now actually present, and the test parses outer/nested exclusions. I respectfully disagree that the current change is incomplete because a partial cherry-pick omitting the parent could fail: the parent dependencyManagement/filter change is in this very PR's full diff. Such an incomplete backport is not a defect in the matched change set.
  • I also would not make a new runtime fail-fast redesign a blocker for the explicitly unsupported mixed-jar deployment. It is useful defensive work, but putting it only at adapter resolution would not cover normal source startup, based on the actual call-site search above.
  • +1 to @SEZ9's packaging-test limitation: the current checks inspect exploded classes, source POMs and classpath pom.properties, not actual assembled plugin jars. This is the same optional scope as Daniel's existing Issue 4, not another numbered duplicate.
  • +1 to the third-party migration guidance improvement: authors outside the parent hierarchy should also carry the curated exclusions/native-version policy. A copyable XML example would help; this is a nonblocking docs improvement, not proof that in-tree dependencies currently regress.

2. Code Quality Assessment

2.1 Coding Standards

Adapter responsibilities, pinned-version constants and patched-class invariants have comments; service entries name their corresponding provider classes and have ASF headers. The XML parser disables external entities/DOCTYPE processing. DB2 parity remains optional Issues 6/7: no adapter/provider test and no module-local version property, though it resolves the parent baseline.

2.2 Test Coverage and Test Stability

Stability rating: Stable in the normal Maven module layout; no sleep, port, async timing or external-service assertion is added. Streams/JarFiles close, sets compare without iteration-order assumptions, and filesystem separators are normalized.

The patch-set test assumes SourceOptions resolves from an exploded classes directory and locates source POMs by walking ancestors of user.dir. +1 to @SEZ9's nondefault-layout concern: a packaged-code-source or unrelated working directory can cause a deterministic environment failure. This limits test portability rather than showing a scheduling flake. Existing Issue 4's packaging-phase guard would cover the real deliverable; these unit tests alone do not.

Tests map adapters to exact connector names and check all listed Debezium artifact metadata versions. OpenGauss pins compatibility with the inherited PostgreSQL adapter. Existing Issue 5 recommends a structural dependency guard for future modules so DB2's earlier omission cannot recur. No current-head connector E2E result is inferred from previous heads.

2.3 Documentation Updates

Both language guides now accurately distinguish five patches/four stock exclusions, matched-release upgrades, native/runtime duplication and major-version limits. They agree. Copyable external dependency/exclusion guidance and a first-release heading remain useful optional improvements; source POM changes do not flow transitively as dependencyManagement to arbitrary external consumers.

3. Architectural Soundness

3.1 Elegance

A coherent staged packaging change, not full Debezium abstraction. It preserves shared patches while making connector runtime ownership explicit.

3.2 Maintainability

Central management retains native-version and exclusion policy; repeated OpenGauss filters are mechanically checked. Add a packaged-artifact invariant before expanding version diversity.

3.3 Extensibility

Binary-compatible connector divergence becomes possible. Major-version divergence still requires actual SPI adoption by production base code. Versioned shared runtime jars are an alternative if distribution growth becomes unacceptable, with a larger plugin-discovery design cost.

3.4 Historical-Version Compatibility

Configuration/checkpoint formats remain; plugin jars must migrate as a set. No real rolling-upgrade or packaged-classloader test was executed locally.

4. Issue Summary

No. Existing nonblocking improvement Location Severity
5 Guard future CDC runtime dependency declarations connector-cdc parent/test scope Medium
3 Quantify distribution-size impact incompatible-changes guides Low
4 Check actual shaded plugin artifacts PatchedDebeziumClassesTest.java:101 Low; overlaps @SEZ9's packaging concern
6 DB2 adapter/SPI/test parity connector-cdc-db2 Low
7 DB2 local version declaration connector-cdc-db2/pom.xml Low

Earlier source Issue 1 (DB2 compile dependency omission) and Issue 2 (incorrect impact statement) remain fixed. @SEZ9's earlier approval and later change requests are acknowledged explicitly above: the current full parent/filter/migration changes address their original supported-deployment concern, while runtime defensive checks and artifact-test limitations remain follow-ups rather than silently dismissed facts.

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers: resolve the actual merge conflicts against current dev, then obtain current-head CI and independent write-capable maintainer review. No remaining Daniel source blocker is asserted for the matched release set.
  2. Suggested improvements, nonblocking: existing Issues 3-7 and @SEZ9's artifact/runtime/migration hardening discussed above.

The previous OpenGauss-failure statement is not the current CI fact. The exact-head fork Build is completed/cancelled, and Apache has no adopted Build check in this snapshot. A cancelled run does not establish that the earlier OpenGauss restore failure still occurs.

GitHub now reports mergeable_state=dirty. Please sync with the latest dev and resolve those conflicts, then let CI validate the resolved head; this is an actual conflict gate, independently recorded from ordinary behind/diverged metadata. It is not advice to add unrelated dependencies or weaken failing assertions.

Overall, keep the staged ownership design and its documented binary-compatibility boundary. Daniel has READ permission and cannot approve his own PR or dismiss another review; an independent maintainer must make the final effective review decision after conflicts and CI are clear.

Snapshot 2026-11-01T14:24:01.847093+00:00: head 51f37b1ae0091c5162a4e80df5cf6c04dd5a6fdd, base dev; compare diverged, ahead 10 / behind 9; merge dirty / DIRTY, review decision REVIEW_REQUIRED. Drift is recorded separately; it does not itself request synchronization.

Resolve the incompatible-changes.md conflict caused by apache#13065 splitting the
guide into per-release sections: keep dev's 2.3.13 Transform Changes and move
this PR's Debezium ownership entry under the dev section's Dependency Upgrades.
connector-cdc-base now declares Debezium as provided, so the debezium-core
and debezium-api exclusions that apache#12477 added to the OceanBase CDC
dependency on connector-cdc-mysql left the OceanBase jar without the stock
Debezium runtime. Drop the exclusions; the connector-cdc parent shade
filter still removes the stock classes that connector-cdc-base patches.
Extend TestOceanBaseCdcPackagingIT to assert the runtime is bundled and
document the OceanBase upgrade note.
@DanielLeens

Copy link
Copy Markdown
Contributor Author

Pushed b793521df7b to fix an OceanBase CDC runtime gap that this PR introduces once it is combined with #12477 (already in the merge base).

Problem. connector-cdc-oceanbase/pom.xml excluded io.debezium:debezium-core and debezium-api from its connector-cdc-mysql dependency, with the note that connector-cdc-base provides them. This PR makes connector-cdc-base declare Debezium as provided, so the base jar now ships only its five patched io.debezium classes. With the exclusions still in place, nothing put the stock Debezium runtime (io.debezium.config.Configuration, io.debezium.engine.DebeziumEngine, ...) into the OceanBase jar, and its MySqlIncrementalSource-based source would fail with NoClassDefFoundError. The existing TestOceanBaseCdcPackagingIT did not catch this: it only checks for classes shadowing connector-cdc-base, and an empty Debezium set passes that check trivially.

Fix.

  • Removed the two exclusions, so OceanBase bundles Debezium the same way connector-cdc-mysql now does. The connector-cdc parent shade filter added in this PR still drops the stock ChangeEventQueue / TableId / HistorizedRelationalDatabaseConnectorConfig / HeartbeatFactory classes, so the patched Serializable TableId from connector-cdc-base stays the only definition. The two existing packaging assertions (no shadowed classes, TableId serializable when the connector jar comes first) remain the guard for that.
  • Extended TestOceanBaseCdcPackagingIT with packagedJarShipsStockDebeziumRuntime, which asserts the packaged jar contains io/debezium/config/Configuration.class (debezium-core) and io/debezium/engine/DebeziumEngine.class (debezium-api). It fails on the previous head.
  • Added an OceanBase line to the EN/ZH incompatible-changes entry.

TiDB CDC was also checked: it only uses SourceOptions, StartupMode and TemporalConversions from cdc-base and no Debezium types, so it is unaffected.

Only spotless was run locally; the fork CI run on b793521df7b is the verification.

@DanielLeens DanielLeens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self re-check of the current head b793521df7be42989fd189c2ca8b268f8b7c7e14. Since my last self-review on 51f37b1ae0 there are two commits: e7ea0feab6 (a plain dev merge that cleared the merge conflicts reported last time) and b793521df7b (the OceanBase CDC packaging fix). The rest of the 35-file diff is unchanged from the version I already reviewed, so this note focuses on the delta and on whether the earlier conclusion still holds.

What Problem Does This PR Solve?

  • User pain point: connector-cdc-base used to ship the stock Debezium runtime and is force-loaded into every CDC plugin class loader, so no connector could own its Debezium version.
  • Fix approach: base declares Debezium as provided, each Debezium-based connector declares and bundles its runtime, the connector-cdc parent shade filter drops the four stock classes that base patches, and adapter/packaging tests guard the invariants.
  • New in this head: once #12477 (OceanBase CDC, already in the merge base) is combined with this PR, the OceanBase jar would have shipped no stock Debezium at all. This head closes that gap.

1. Code Change Review

1.1 Core Logic Analysis

connector-cdc-oceanbase/pom.xml:39-50 drops the debezium-core / debezium-api exclusions on its connector-cdc-mysql dependency:

<artifactId>connector-cdc-mysql</artifactId>
<version>${project.version}</version>
<!-- Keep debezium-core and debezium-api: connector-cdc-base declares Debezium as provided ... -->

Why it was needed: those exclusions were written when connector-cdc-base supplied Debezium at compile scope. With base now provided, nothing else put io.debezium.config.Configuration or io.debezium.engine.DebeziumEngine into the OceanBase jar, and the MySqlIncrementalSource-based source would hit NoClassDefFoundError on the normal startup path. So this is a real regression this PR would have introduced, not a hypothetical one, and it is fixed in the right place (the OceanBase module, no shared class touched).

plugin discovery -> filterPluginJar (AbstractPluginDiscovery) -> [connector-cdc-oceanbase.jar, connector-cdc-base.jar]
 -> OceanBase source (MySqlIncrementalSource path) -> io.debezium.* runtime classes
    stock core/api/embedded: now bundled transitively via connector-cdc-mysql into the OceanBase jar
    ChangeEventQueue / TableId / HistorizedRelationalDatabaseConnectorConfig / HeartbeatFactory:
      excluded by the connector-cdc parent shade filter (connector-cdc/pom.xml:151-164),
      so connector-cdc-base's patched copies (incl. Serializable TableId) stay the only definition

OceanBase has no module-level shade filter, so it inherits the parent filter list unchanged; the stock patched classes cannot reappear.

1.2 Compatibility Impact

No CDC option, default, checkpoint or wire-format change. OceanBase still resolves Debezium 1.9.8.Final. The only operator-visible effect is the one already documented for the other connectors: the OceanBase jar must be upgraded together with connector-cdc-base. The EN and ZH incompatible-changes.md entries both gained a matching OceanBase line (docs/en/introduction/concepts/incompatible-changes.md:504, docs/zh/introduction/concepts/incompatible-changes.md:426), and they agree.

1.3 Performance / Side Effects

The OceanBase jar grows by the Debezium runtime payload, the same per-connector duplication already called out under Issue 3. No hot-path, thread, lock or resource-lifecycle change.

1.4 Error Handling and Logging

Nothing new. Previous items stand.

2. Code Quality Assessment

2.1 Code Standards

The new pom comment explains why the exclusions were removed and how the patched classes stay unshadowed. The new test method carries a short why-comment. Fine.

2.2 Test Coverage

TestOceanBaseCdcPackagingIT.java:60-71 adds packagedJarShipsStockDebeziumRuntime, which asserts the packaged jar contains io/debezium/config/Configuration.class and io/debezium/engine/DebeziumEngine.class. It has real failure power: on 51f37b1ae0 (exclusions still present) both entries are absent and it goes red. It complements the two existing assertions, which only proved the absence of shadowed classes and therefore passed trivially on an empty Debezium set; that blind spot is now closed.

Stability: Stable. It reads the packaged artifact via the failsafe connector.jar property; no timing, ports or containers. Note it is a failsafe test, so it only runs in jobs with -DskipIT=false (the root pom defaults skipIT=true); the plain unit-test job will not execute it.

2.3 Documentation

EN/ZH parity confirmed for the new OceanBase bullet.

3. Architecture

The fix keeps the staged design intact: ownership moves into the plugin, shared patches stay in base. One gap the OceanBase module exposes is Issue 8 below: OceanBase now ships Debezium but does not own a debezium.version, has no adapter, and is not covered by the per-connector drift tests.

4. Issue Summary

No. Issue Location Severity Status
1 DB2 compile dependency omission connector-cdc-db2/pom.xml Blocker (earlier) Fixed
2 Incorrect impact statement incompatible-changes.md Blocker (earlier) Fixed
3 Quantify distribution-size impact incompatible-changes guides Low Open, non-blocking
4 Check actual shaded plugin artifacts for all connectors PatchedDebeziumClassesTest.java:101 Low Partially addressed for OceanBase only
5 Guard future CDC runtime dependency declarations connector-cdc parent / tests Medium Open, non-blocking
6 DB2 adapter/SPI/test parity connector-cdc-db2 Low Open, non-blocking
7 DB2 local version declaration connector-cdc-db2/pom.xml Low Open, non-blocking
8 OceanBase does not own its Debezium version connector-cdc-oceanbase/pom.xml:39-50 Low New, non-blocking

Issue 8: OceanBase bundles Debezium without owning its version

  • Location: seatunnel-connectors-v2/connector-cdc/connector-cdc-oceanbase/pom.xml:39-50
  • Problem: OceanBase gets debezium-api / debezium-embedded / zstd-jni only transitively through connector-cdc-mysql. In the OceanBase build those versions are re-managed by the inherited parent dependencyManagement at the parent fallback debezium.version (connector-cdc/pom.xml:54), while debezium-connector-mysql arrives at whatever connector-cdc-mysql declares.
  • Risk: today everything is 1.9.8.Final, so nothing breaks. If connector-cdc-mysql later bumps its own debezium.version, OceanBase would silently ship a mixed core / connector-mysql pair, and no adapter drift test in the OceanBase module would catch it (the shaded jar also inherits MySqlDebeziumAdapter's service entry, which would then report a version that does not match the bundled core).
  • Best improvement: either declare debezium.version plus explicit debezium-api / debezium-embedded / zstd-jni in the OceanBase pom like the other connectors, or add a one-line assertion to TestOceanBaseCdcPackagingIT that the bundled debezium-core and debezium-connector-mysql pom.properties versions match. Fine as a follow-up.
  • Severity: Low
  • Raised by another reviewer: No

All of @SEZ9's earlier points remain as reconciled in my previous self-review; nothing in this delta reopens them.

5. Merge Recommendation

Conclusion: Ready to merge after fixes

  1. Blockers: only CI and process. The merge conflicts from the last check are resolved (mergeable_state is now blocked, i.e. review-required, not dirty). The fork Build for this exact head (run 41572360686) is still queued, so there is no current-head CI result yet; it must go green, including the CDC integration jobs that execute TestOceanBaseCdcPackagingIT. No non-CI source blocker remains.
  2. Non-blocking: Issues 3-8.

As the author I cannot approve this PR; an independent maintainer needs to give the effective review once CI is green. The branch is 3 commits behind dev, which is not itself a reason to sync.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants