You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[refactor](fe) Merge fe-connector-api into fe-connector-spi
### What problem does this PR solve?
Problem Summary:
The connector plugin contract was split across two modules whose names were
inverted relative to common usage: `fe-connector-api` held the types a plugin
IMPLEMENTS (`Connector`, `ConnectorMetadata` and its Ops sub-interfaces, the
scan / write / procedure providers, handles, pushdown and ddl value types) —
which is what "SPI" normally names — while `fe-connector-spi` held mostly the
engine services a plugin CONSUMES (`ConnectorContext`, `ConnectorStorageContext`,
`ConnectorConf`), which is what "API" normally names. Both modules' package-info
already documented the inversion and asked readers to "read the content, not the
name". The `fe-connector-metastore-api` / `-metastore-spi` pair right next to
them follows the opposite (standard) convention, so one directory carried two
contradictory naming rules.
Swapping the two names does not fix it. A strict split by "who implements" is
circular: `ConnectorProvider.create` takes a `ConnectorContext` (spi -> api),
while `ConnectorContext.createSiblingConnector` returns a `Connector`
(api -> spi). That cycle is exactly why Trino keeps its whole plugin surface in
a single `trino-spi` module, and this framework is modelled on Trino.
This commit does the same: `fe-connector-api` is merged into
`fe-connector-spi`, and `org.apache.doris.connector.api` becomes
`org.apache.doris.connector.spi`. The merge is dependency-neutral — every module
that depended on `fe-connector-api` (hive, hudi, maxcompute, paimon, trino,
fe-core) already depended on `fe-connector-spi` as well.
Two consequences worth calling out:
- The connector plugin API version is bumped 2.0 -> 3.0. Every type on the
contract changed its fully-qualified name, so a plugin built against 2.0 must
be refused by `ApiVersionGate` at load time rather than fail later with
`NoClassDefFoundError`. Both recorded baselines
(`connector-metadata-methods.txt`, `connector-plugin-surface.txt`) and the
version pinned in `ConnectorPluginSurfaceTest` are updated in this commit.
`ConnectorPluginManagerTest` used to hardcode the compatible major as a
literal; it now derives both the stale and the current major from the
kernel's declared version, because what that test asserts is the gate
(stale refused, current admitted), not which number is current.
`ConnectorPluginSurfaceTest` remains the one deliberate speed bump.
- `ChildFirstClassLoader.DEFAULT_PARENT_FIRST_PACKAGES` listed only
`org.apache.doris.connector.api.`, so `ConnectorProvider` and
`ConnectorContext` were not parent-first. After the merge the single
`org.apache.doris.connector.spi.` entry covers the whole contract.
The merged `package-info.java` keeps the existing Rule 1-7 design rules; Rule 4
now explains why both directions live in one module instead of disclaiming the
inverted names. README.md and AGENTS.md are updated accordingly.
### Release note
None
### Check List (For Author)
- Test: Unit Test
- Full FE build passes, including fe-core test compilation.
- Every unit test under `org.apache.doris.connector` passes: 314 test
classes / 1421 cases, 0 failures, 0 errors, across the 58-module
reactor (fe-core included).
- Checkstyle passes across the whole fe reactor.
- Both architecture gates pass: `check-fe-connector-imports.sh` (plus its
self-test) and `check-fe-core-metadata-funnel.sh`.
- No regression test run: this is a pure rename/merge with no behavior
change.
- Behavior changed: No
- Does this need documentation: No
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: fe/fe-connector/README.md
+18-14Lines changed: 18 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,8 +40,13 @@ Roles only — one line each. For anything deeper, read the module's javadoc
40
40
41
41
| Module | Role |
42
42
|---|---|
43
-
|`fe-connector-api`| The engine <-> connector contract: `ConnectorMetadata` with its Ops sub-interfaces, plus handle / pushdown / mvcc / scan / write / ddl / procedure / event / rest types. The javadoc here **is** the API reference. |
44
-
|`fe-connector-spi`| Bootstrap contract: `ConnectorProvider` (discovery identity + factory) and `ConnectorContext` (what the engine hands a connector, including sibling-connector creation). |
43
+
|`fe-connector-spi`| The whole engine <-> connector contract, in both directions. What a connector implements: `ConnectorProvider` (discovery identity + factory), `Connector`, `ConnectorMetadata` with its Ops sub-interfaces, plus handle / pushdown / mvcc / scan / write / ddl / procedure / event / rest types. What the engine implements and hands down: `ConnectorContext` (including sibling-connector creation), `ConnectorStorageContext`, `ConnectorSession`, `ConnectorConf`. The javadoc here **is** the API reference. |
44
+
45
+
The two directions are one module on purpose: the boundary is bidirectional
46
+
(`ConnectorProvider.create` takes a `ConnectorContext`, and `ConnectorContext`
47
+
hands back a `Connector`), so splitting it by "who implements" would be
48
+
circular. Trino makes the same call with `trino-spi`. Contrast the metastore
49
+
layer below, where the split is acyclic and the usual api/spi convention holds.
45
50
46
51
**Metastore layer** (how connectors reach a metastore without hand-parsing
47
52
endpoint properties)
@@ -99,8 +104,8 @@ exactly one `ConnectorMetadata` instance per catalog on the statement's
99
104
`ConnectorStatementScope` and closes it deterministically at statement end.
100
105
Scan planning follows the same shape: the generic `PluginDrivenScanNode`
101
106
(fe-core) delegates all per-source planning to the connector's
102
-
`ConnectorScanPlanProvider` — note the interface lives in fe-connector-api
103
-
(`api.scan`), not in fe-core.
107
+
`ConnectorScanPlanProvider` — note the interface lives in fe-connector-spi
108
+
(`spi.scan`), not in fe-core.
104
109
105
110
**Classloading.** Plugins load child-first, each carrying its own runtime
106
111
closure. Wherever engine code crosses into a plugin — or a bundled library
@@ -119,17 +124,16 @@ This document never lists SPI methods. The truth lives in code, behind four
119
124
mechanisms:
120
125
121
126
1.**Javadoc is the API reference.** Start at `ConnectorMetadata` (and its
122
-
Ops sub-interfaces) in fe-connector-api, and `ConnectorProvider` in
123
-
fe-connector-spi. Every SPI method has a default body, so each
124
-
sub-interface's class javadoc states its minimum implementation set,
125
-
lifecycle, and threading rules.
127
+
Ops sub-interfaces) and `ConnectorProvider`, both in fe-connector-spi.
128
+
Every SPI method has a default body, so each sub-interface's class javadoc
129
+
states its minimum implementation set, lifecycle, and threading rules.
126
130
2.**`@ConnectorMustImplement`** is the machine-readable half of the minimum
127
131
implementation set: it marks the default methods a connector is
128
132
nevertheless expected to override, with `when` naming the capability that
129
133
triggers the obligation. A unit test pins the annotated set, so promoting
0 commit comments