This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch master-catalog-spi-review-21 in repository https://gitbox.apache.org/repos/asf/doris.git
commit cef4f3fb88bea4cc6b33b6d8a89b7d782211cf72 Author: morningman <[email protected]> AuthorDate: Tue Jul 28 14:28:27 2026 +0800 [doc](catalog) add task space for fe-core datasource/property cleanup Records the investigation into whether fe-core datasource/property/{common,metastore} can be deleted or relocated to fe-connector / fe-filesystem, and the resulting plan. Verdicts differ per package and must not be treated as one job: - metastore/ (4 files, 333 lines) plus the orphaned ConnectionProperties is unreachable dead code. Its registry holds only TRINO_CONNECTOR, whose factory never parses anything; both doors into it are unreachable; nothing there is Gson-persisted; and its successor (fe-connector-metastore-api MetaStoreProperties + MetaStoreProviders + Connector.deriveStorageProperties) is already live. Nothing needs to be migrated -- only deleted. - common/ (2 files, 237 lines) serves internal storage (cold-storage StoragePolicy, cloud StorageVault, TVF/backup/export), not external datasources. No fe-connector or fe-filesystem module imports it, every relocation target is dependency- or classloader-illegal, and reusing the fe-filesystem-s3-base twin is a behavior change rather than a refactor. Only its provably dead half is removed. The space follows the hive-catalog-shade-removal layout and adds an open-decisions file, since one decision (fail-loud vs fail-silent on the null-supplier branch) gates the main deletion. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> --- plan-doc/fecore-property-cleanup/HANDOFF.md | 92 +++++++ plan-doc/fecore-property-cleanup/README.md | 76 ++++++ plan-doc/fecore-property-cleanup/design.md | 278 +++++++++++++++++++++ plan-doc/fecore-property-cleanup/open-decisions.md | 115 +++++++++ plan-doc/fecore-property-cleanup/progress.md | 90 +++++++ plan-doc/fecore-property-cleanup/tasklist.md | 177 +++++++++++++ 6 files changed, 828 insertions(+) diff --git a/plan-doc/fecore-property-cleanup/HANDOFF.md b/plan-doc/fecore-property-cleanup/HANDOFF.md new file mode 100644 index 00000000000..8e40b1db753 --- /dev/null +++ b/plan-doc/fecore-property-cleanup/HANDOFF.md @@ -0,0 +1,92 @@ +# 🤝 Session Handoff — 清理 fe-core `datasource/property/{common,metastore}` + +> **滚动文档**:每次 session 结束**覆盖式更新**,**只保留下一个 session 必须的上下文**。 +> 完成的明细**不落这里**(在 `git log` + [`progress.md`](./progress.md) 里)。 +> 空间索引 [`README.md`](./README.md) · 设计 [`design.md`](./design.md) · +> 清单 [`tasklist.md`](./tasklist.md) · 待拍板 [`open-decisions.md`](./open-decisions.md) + +--- + +# 🆕 下一个 session 第一件事 = **拿 OD-1 的拍板**(`open-decisions.md`) + +## 状态:**调研已完成,代码一行未动。** + +**基线 HEAD** = `3468d905eb3`(分支 `catalog-spi-review-21`,2026-07-28)。 +本空间是**新建**的,尚无任何本任务的 commit。 + +--- + +## 📍 你现在需要知道的三件事 + +### 1️⃣ 两个包的答案不一样,别当成一件事做 + +| 包 | 裁决 | +|---|---| +| `metastore/`(4 文件 333 行)+ `ConnectionProperties.java`(140 行) | **整体删除** —— 运行期不可达,接班人 `fe-connector-metastore-api` 早已上线,**没有东西需要搬** | +| `common/`(2 文件 237 行) | **留在 fe-core** —— 它服务的是**内部存储**(冷存 StoragePolicy / 云上 StorageVault),不是外部数据源;只砍掉死的一半 | + +### 2️⃣ 🔴 一个必须记住的「别再犯」 + +调研初判说 `common/` 和 `fe-filesystem-s3-base` 的 +`S3CredentialsProviderType`/`S3CredentialsProviderFactory` 是重复造轮子、可以直接替换 —— +**这个判断被对抗验证两轮推翻了**。两份实现有**两条活的行为差异**(`design.md` §3.3): + +1. 发给 hadoop 的凭证串会**多出** `ProfileCredentialsProvider` +2. 模式串接受面会**放宽**(空串 / `ENVIRONMENT` / `WEB_IDENTITY_TOKEN_FILE` 从抛异常变成接受) + +而**全仓没有任何测试钉住那个串** ⇒ 换掉会**绿着上线一个回归**。 +**下次看到「这两个类长得一样,合并掉吧」的念头,先来读 `design.md` §3.3。** + +### 3️⃣ ⛔ 卡在哪:OD-1 没拍板 + +`open-decisions.md` **OD-1**:删掉 metastore 后,`resolveDerivedStorageDefaults()` 的 +null-supplier 分支要 **fail-loud(`throw`,推荐)** 还是 **fail-silent(`return emptyMap()`)**? + +这条**直接决定 FPC-03 的代码怎么写**,不定不能开工。 +(OD-2 是「FPC-02 做不做」,不阻塞任何东西,可以随时定。) + +--- + +## ▶️ 建议的执行顺序 + +``` +1. 拿 OD-1 拍板 ← 就是这一步,先做 +2. FPC-02(删 AWS 死构造臂,~146 行) ← 可选、解耦,先做也行、跳过也行 +3. FPC-03(主删除:5 文件 473 行 + CatalogProperty 瘦身 ~45 行) ← 依赖 OD-1 +4. FPC-04(可选清扫,另起提交) +``` + +--- + +## ⚠️ 开工前必读的三条纪律(本仓库已知踩坑) + +1. **删除类改动不能只信增量编译** —— `fe-core/target/classes` 里确实躺着无源文件的陈旧 `.class`。 + 每步先 `rm -rf fe-core/target/{classes,test-classes}`。 +2. **全反应堆必须含测试源**(禁 `-Dmaven.test.skip=true`),且必须 `-Dcheckstyle.skip=true` + (否则 checkstyle 扫 generated-sources 退化成平方级,构建卡死 60+ 分钟); + checkstyle 改为**只对改动模块**单独跑 `checkstyle:check`。 +3. **`fe-connector-api` 的录制基线要显式跑** —— + `mvn -pl fe-connector/fe-connector-api test`。全反应堆 `test-compile` **不跑 surefire**, + 这是本分支已经红过好几批没人发现的盲区。 + 本任务**预期不需要刷基线**(该模块不依赖 fe-core),**红了就停手,别顺手刷。** + +--- + +## 🔎 尚未验证(如实声明,别当成已完成) + +- **没跑过任何 maven 构建** —— `tasklist.md` 里的验证命令是方子,不是结果 +- **没查 apache/doris master** 是否有 `StorageAdapter.getAwsCredentialsProvider()` 的调用者 + (FPC-02 的 rebase 冲突风险是陈述不是实测) +- **没跑 e2e**(需要集群) +- `ExternalCatalog.buildHadoopConfiguration(Map)` 的调用者没枚举 ⇒ FPC-04 明确排除它 + +--- + +## 📎 参考:完整英文调研报告 + +29k 字的原始报告(逐条 `file:line` 证据 + 三份独立设计的分歧点 + 六项对抗验证的两轮判决) +在调研 session 的 scratchpad: +`/tmp/claude-1000/-mnt-disk1-yy-git-wt-catalog-spi/6983e5ef-36cf-4f14-a048-139ffc1c1b51/scratchpad/property-common-metastore-report.md` + +⚠️ **scratchpad 是 session 级的,可能已经不在了。** 本空间的 `design.md` 已经把其中**结论性、 +需要长期保留**的部分中文化落盘 —— 以 `design.md` 为准,那份报告只是溯源用。 diff --git a/plan-doc/fecore-property-cleanup/README.md b/plan-doc/fecore-property-cleanup/README.md new file mode 100644 index 00000000000..81dba34156c --- /dev/null +++ b/plan-doc/fecore-property-cleanup/README.md @@ -0,0 +1,76 @@ +# 📦 任务空间 — 清理 fe-core `datasource/property/{common,metastore}` + +> **独立任务空间**,与 catalog-spi 主线(`plan-doc/HANDOFF.md`)并行但**不混流**。 +> 目标:按架构目标「**fe-core 不持有任何属性解析**」,处置 fe-core +> `org.apache.doris.datasource.property` 下的 `common/`(AWS 凭证模式)与 `metastore/`(元存储属性)两个包。 + +--- + +## 🚩 一句话结论(2026-07-28 基线 `3468d905eb3`,8 路侦察 + 3 路独立设计 + 6 项对抗验证) + +**两个包的答案不一样,别当成一件事做。** + +| 包 | 裁决 | 一句话理由 | +|---|---|---| +| `metastore/`(4 文件 333 行) | **整体删除**(连带孤儿 `ConnectionProperties.java`) | 运行期**不可达**;接班人 `fe-connector-metastore-api` 早已上线跑着 → **没有任何东西需要搬** | +| `common/`(2 文件 237 行) | **留在 fe-core,砍掉死的一半** | 它服务的是**内部存储**(冷存 StoragePolicy / 云上 StorageVault),**不是外部数据源**;三个候选目的地全部堵死;「复用 fe-filesystem 现成那份」是**行为变更**不是重构 | + +**⚠️ 最容易踩的坑**:`common/` 看起来和 `fe-filesystem-s3-base` 的 +`S3CredentialsProviderType`/`S3CredentialsProviderFactory` 是重复造轮子 —— **调研初判就是这么错的**。 +两份实现有**两条活的行为差异**(见 `design.md` §3.3),且全仓**没有任何测试**钉住发出的凭证串, +换掉会绿着上线一个回归。 + +--- + +## 📂 本空间文件 + +| 文件 | 用途 | 更新方式 | +|---|---|---| +| [`design.md`](./design.md) | **设计文档** —— 两个包的判据、证据链、被否方案、风险 | 稳定文档,改动需在 progress 留痕 | +| [`tasklist.md`](./tasklist.md) | **Task list** —— 唯一进度清单,`FPC-NN` 勾选 | 每完成一项随 commit 勾 `[x]` | +| [`open-decisions.md`](./open-decisions.md) | **待拍板** —— 动手前需要用户定的事 | 拍板后就地标 ✅ 并写明结论 | +| [`HANDOFF.md`](./HANDOFF.md) | **交接文档** —— 只写「下一个 session 第一件事做什么」 | 每 session 结束**覆盖式**更新 | +| [`progress.md`](./progress.md) | **进度记录** —— append-only 日志(日期 / commit / 结论 / 踩坑) | 只追加,不覆盖 | + +--- + +## ▶️ 新 session 开场流程(必须遵守) + +``` +1. Read plan-doc/fecore-property-cleanup/HANDOFF.md ← 上次留言 + 下一步 +2. Read plan-doc/fecore-property-cleanup/tasklist.md ← 勾到哪了 +3. Read plan-doc/fecore-property-cleanup/open-decisions.md ← 有没有还没拍板的 +4. 需要背景/为什么时才 Read design.md(别默认全读,它是稳定参考不是状态) +5. 用一句话向用户复述:"上次做完了 X,下一步是 FPC-NN,对吗?" +6. 等用户确认后开始 +``` + +**⚠️ 行号信 HEAD 不信文档** —— 本空间所有 `file:line` 是 **2026-07-28 / `3468d905eb3`** 基线, +代码动了就以 `grep` 为准。 + +--- + +## 🔗 与其它空间的关系 + +- **主线** = `plan-doc/HANDOFF.md`(catalog-spi 迁移)。本任务是主线「fe-core 去属性解析」的收尾一环。 +- **`../metastore-storage-refactor/`(已 CLOSED)** = 本任务的**前置**:正是那条子线**生产**出了 + `fe-connector-metastore-api` / `fe-connector-metastore-spi`(`MetaStoreProviders.bind` + 5 个 provider)。 + 本任务只是把它留在 fe-core 的**旧壳**扫掉。⛔ 那个目录是历史留存,别去读它的规划文档。 +- **继承主线两条铁律**:**fe-core 只出不进**(铁律 A) + **禁 deletion-scaffolding 式搬迁**(铁律 B)。 + 本任务全程 fe-core **只减不增**,两条天然满足。 +- 协作规范沿用 [`../AGENT-PLAYBOOK.md`](../AGENT-PLAYBOOK.md)。 + +--- + +## 📌 范围边界(误判比漏判贵) + +**在范围内**: +- `fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/`(4 文件) +- `fe/fe-core/src/main/java/org/apache/doris/datasource/property/ConnectionProperties.java`(删 metastore 后成孤儿) +- `fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/`(只砍死代码,**不搬迁**) +- `CatalogProperty.java` 的连带瘦身 + +**明确不在范围**(理由见 `design.md` §6): +- `datasource/property/constants/`、`datasource/property/fileformat/` —— **压根不是外部数据源逻辑** +- `StorageAdapter.checkAzureOauth2OnlyForIcebergRest()` —— 是真的架构违规,但需**单独一刀 + e2e** +- 把 `common/` 搬去任何模块 —— 三条独立理由否决,见 `design.md` §3 diff --git a/plan-doc/fecore-property-cleanup/design.md b/plan-doc/fecore-property-cleanup/design.md new file mode 100644 index 00000000000..d0b7b10bd7a --- /dev/null +++ b/plan-doc/fecore-property-cleanup/design.md @@ -0,0 +1,278 @@ +# 设计文档 — 清理 fe-core `datasource/property/{common,metastore}` + +> **稳定参考文档**,不是状态。状态看 [`tasklist.md`](./tasklist.md) / [`HANDOFF.md`](./HANDOFF.md)。 +> **基线**:2026-07-28 / `3468d905eb3` / 分支 `catalog-spi-review-21`。 +> **⚠️ 行号信 HEAD 不信文档。** +> +> **调研方法**:8 路并行侦察(172 条 finding)→ 3 路独立设计(minimal / architectural / risk 三种视角) +> → 6 项对抗验证(**2 项被推翻**,被推翻的用修正版)→ 综合。承重结论由本文作者逐条复核代码。 + +--- + +## 1. 现状盘点 + +`fe/fe-core/src/main/java/org/apache/doris/datasource/property/` 共 21 个文件 2105 行,四个子域: + +| 子域 | 文件数 / 行数 | 是不是「外部数据源」 | 本任务处置 | +|---|---|---|---| +| `metastore/` | 4 / 333 | ✅ 是 | **删** | +| `ConnectionProperties.java` | 1 / 140 | ✅ 是(`metastore/` 的基类) | **删**(孤儿) | +| `common/` | 2 / 237 | ❌ **不是**(内部存储) | **留**,砍死代码 | +| `constants/` + `fileformat/` | 14 / 1395 | ❌ 不是 | 不在范围(§6) | + +--- + +## 2. `metastore/` — 判死证据链 + +### 2.1 结构:已经是空壳 + +``` +metastore/MetastoreProperties.java 191 行 ← 注册表 + Type 枚举 + 基类 +metastore/MetastorePropertiesFactory.java 36 行 ← 接口 +metastore/AbstractMetastorePropertiesFactory.java 74 行 ← 子类型分发骨架 +metastore/TrinoConnectorPropertiesFactory.java 32 行 ← 唯一注册的工厂 +``` + +- `MetastoreProperties.java:86-93` 的静态注册表**只剩 `Type.TRINO_CONNECTOR`**。 + hms / iceberg / paimon 三家的工厂在 "Design S7" 时被**主动摘掉**,注释明说 + 「Type 枚举值保留(好让走岔的 `create()` 响亮报错),但工厂**故意不注册**」。 +- 而这唯一的 `TrinoConnectorPropertiesFactory.java:28-31` override 了 `create()`,直接 + `return new MetastoreProperties(Type.TRINO_CONNECTOR, props)` —— **连 + `initNormalizeAndCheckProps()` 都不调**。⇒ 零解析。 +- 连带后果:`AbstractMetastorePropertiesFactory.createInternal`(`:58`)**零调用者**, + `ConnectionProperties.initNormalizeAndCheckProps()` 的 `@ConnectorProperty` 反射绑定 + **永不执行**,`getDerivedStorageProperties()`(`:157-159`)是写死的 `emptyMap()` 且**零子类**, + `getExecutionAuthenticator` / `asLegacyAuthenticator` / `StorageAuthenticatorBridge`(`:168-190`) + **零调用者**。 + +**⚠️ 别被 `Type.TRINO_CONNECTOR` 骗了**:`type=trino-connector` 的目录是走 +**fe-connector-trino 插件**的,根本不经这个工厂。 + +### 2.2 可达性:两道门都关死 + +全仓唯一 import 者 = `CatalogProperty.java:21`。它开了两道门: + +**门一(`checkMetaStoreAndStorageProperties`,`CatalogProperty.java:307-321`)—— 死。** +全仓仅有它自己的声明,**零调用者**(含 `regression-test/` groovy)。⇒ `:310` 的 +`MetastoreProperties.create()` 不可达。 + +**门二(`resolveDerivedStorageDefaults`,`CatalogProperty.java:264-272`)—— 被门挡死。** +```java +Supplier<Map<String,String>> pluginSupplier = pluginDerivedStorageDefaultsSupplier; +if (pluginSupplier != null) { return pluginSupplier.get(); } // ← 插件目录永远走这条 +MetastoreProperties msp = getMetastoreProperties(); // ← 只有 supplier==null 才走 +``` +- `PluginDrivenExternalCatalog.java:177` **无条件**安装这个 supplier(Design S8)。 +- fe-core main 源里 `ExternalCatalog` 的具体子类**只剩 3 个**:`PluginDrivenExternalCatalog`、 + `RemoteDorisExternalCatalog`、`TestExternalCatalog`;后两个**完全不碰 storage/hadoop/metastore**。 + +**门二的时序窗口(曾被判 HIGH 风险,复核后判为不可达)**: +`PluginDrivenExternalCatalog.java:150` 构造连接器时,`DefaultConnectorContext` 已经接上了 +`catalogProperty` 的 storage supplier(`:206-208`),而派生 supplier 要到 `:177` 才安装。 +⇒ 理论上存在「supplier 还是 null 就访问 storage」的窗口。 +**但实测不可达**: +- `PaimonConnector` 构造函数(`:151-166`)只传方法引用 `this::pluginAuthenticator`(**惰性 memoize**, + 见 `:174-186`),不碰 storage; +- `IcebergConnector` 构造函数(`:215+`)同样只传 `this::pluginAuthenticator`; +- `HiveConnector` / `HudiConnector` 构造函数中**无任何** `storage()` / `getStorageProperties` 调用; +- `IcebergConnectorProvider.validateProperties`(`:78-82`)/ `PaimonConnectorProvider`(`:94-98`) + 显式传 `Collections.emptyMap()`,javadoc 明写「验证不需要 storage」。 + +⇒ **窗口存在于代码形状上,但今天没有任何路径走进去。** 它只影响「删除后是 fail-loud 还是 +fail-silent」这个选择,见 [`open-decisions.md`](./open-decisions.md) OD-1。 + +### 2.3 持久化:无坑 + +这条必须查,因为本仓库有前科(`EsTable`/`EsResource`:删掉 Gson 注册的持久化类 → +`RuntimeTypeAdapterFactory` 对未注册 clazz 硬抛 → **老镜像 FE 起不来**)。 + +- `property/` 树下**无任何** `@SerializedName` / Gson / `Serializable` / `Writable`; +- `CatalogProperty` 只持久化 `resource`(`:56`)和 `properties`(`:59`); + `metastoreProperties`(`:94`)**无注解**,被 `GsonUtilsBase.HiddenAnnotationExclusionStrategy` 跳过; +- 不是任何 `RuntimeTypeAdapterFactory` 的基类或子类型; +- `gensrc/thrift`、`gensrc/proto`、所有 `META-INF/services`、所有 `.groovy`/`.out`/`.sh`、 + 两个 build gate 里**全无踪迹**。 + +⚠️ 注意区分:`InitCatalogLog.Type` / `InitDatabaseLog.Type` 里的 `TRINO_CONNECTOR` 是**另一个** +枚举(那个是持久化的,**别碰**);`MetastoreProperties.Type` 只在运行期计算,不持久化。 + +### 2.4 接班人已上线 ⇒ 没有东西需要搬 + +| fe-core(要删的) | 连接器侧接班人(已在跑) | +|---|---| +| `MetastoreProperties` | `org.apache.doris.connector.metastore.MetaStoreProperties`(fe-connector-metastore-api) | +| `MetastorePropertiesFactory` + `AbstractMetastorePropertiesFactory` 注册表 | `MetaStoreProvider` / `MetaStoreProviders.bind(…)` / `bindForType(…)`(fe-connector-metastore-spi) | +| `getDerivedStorageProperties()` | `Connector.deriveStorageProperties(Map)`(fe-connector-api),实现见 `IcebergConnector.java:1161-1198` | +| `ConnectionProperties` 的 `@ConnectorProperty` 反射绑定 | `AbstractMetaStoreProperties` + `MetaStoreParseUtils`(fe-connector-metastore-spi) | + +消费点:`PaimonConnectorProvider.java:96`、`IcebergConnectorProvider.java:80`、 +`HiveConnector.java:720`、`HudiConnector.java:296`。 + +### 2.5 `ConnectionProperties` 顺带成孤儿 + +- 全仓唯一子类就是 `MetastoreProperties`(`:46`)。 +- `StorageAdapter.java:856` 那处引用 —— **是 javadoc 散文,不是代码**: + `* {@code ConnectionProperties.equals}: logically identical configurations must share one`, + 位于 `StorageAdapter` 自己 `equals()` 上方的注释块里。 + ⇒ **`StorageAdapter` 不需要改就能编过**;改它只是为了不留悬空引用(cosmetic)。 + +--- + +## 3. `common/` — 判「留」证据链 + +### 3.1 它不是外部数据源代码 + +| 消费者 | 服务的功能 | +|---|---| +| `StorageAdapter.java:21-22` | 内部存储适配 | +| `S3ThriftAdapter.java:20` | 发给 BE 的 S3 thrift 参数 | +| `CloudObjectStoreAdapter.java:23` | 云上 `StorageVault`(`StorageVaultMgr.java:150`) | +| `AzureGuessRoutingParityTest.java:21`(测试) | 上述的对齐测试 | + +再往上:冷存 `StoragePolicy`(`PushStoragePolicyTask.java:91`)、TVF / backup / export。 + +**零个 `fe-connector-*` 模块、零个 `fe-filesystem-*` 模块 import 它。** 它不在这次迁移的射程内。 + +### 3.2 三个候选目的地全部堵死 + +| 目的地 | 为什么不行 | +|---|---| +| `fe-filesystem-s3-base` | ① fe-core 对它**任何 scope 的依赖都没有**(`grep s3-base fe/fe-core/pom.xml` 退出码 1);② 它是 IMPL 层,`fe/fe-filesystem/README.md:47-63` 禁止 fe-core 依赖;③ **最要命**:`FileSystemPluginManager.java:88-90` 把 `org.apache.doris.filesystem.` 设为 **parent-first** → 把 s3-base jar 放进 `fe/lib` 会**静默遮蔽** s3/gcs/minio/ozone 各插件自带的同名类(本仓库已知的 split-brain 类坑) | +| `fe-filesystem-api` | 按约定只放 JDK 类型;而工厂 import 了 `software.amazon.awssdk.auth.credentials.*`(`AwsCredentialsProviderFactory.java:25-32`) | +| `fe-foundation` | 不带 AWS SDK;且 `org.apache.doris.foundation.` **不在** `ConnectorPluginManager.CONNECTOR_PARENT_FIRST_PREFIXES` 里(`:73-74`),而各连接器 zip 都打包 `fe-foundation.jar` → 潜在 duplicate-Class LinkageError | + +### 3.3 ⚠️ 「复用 fe-filesystem 现成那份」是行为变更,不是重构 + +**这是本次调研初判出错、被对抗验证两轮推翻的地方,务必记住。** + +仓库里现在有**四份** AWS 凭证模式实现: + +| | 位置 | +|---|---| +| A | fe-core `property/common/{AwsCredentialsProviderMode,AwsCredentialsProviderFactory}` | +| B | `fe-filesystem-s3-base` `{S3CredentialsProviderType,S3CredentialsProviderFactory}` | +| C | `fe-connector-iceberg` `AwsCredentialsProviderModes`(注释自称 "self-contained twin",因连接器不能 import fe-core) | +| D | `fe-connector-metastore-iceberg` `IcebergRestMetaStoreProperties` 内联的模式检查 | + +**A → B 替换会带来两条活的行为差异**: + +1. **发给 hadoop 的凭证 provider 串会多一项** + `,software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider` + (B 的 `S3CredentialsProviderFactory.java:115` 有,A 的 `:116-129` 没有)。 + 活链路:`StorageAdapter.java:101-105`(`S3_CREDENTIAL_KEYS`)→ `:678-694` 跳过 SPI 的值并重新推导 + → `:730` / `:739` 写 `fs.s3a.assumed.role.credentials.provider` / `fs.s3a.aws.credentials.provider` + → `CatalogProperty.java:383` 消费。 + +2. **模式串接受面放宽** + B(`S3CredentialsProviderType.java:45,:51,:56`)接受空串、`ENVIRONMENT`、`WEB_IDENTITY_TOKEN_FILE`; + A(`AwsCredentialsProviderMode.java:48,:69-72`)对这些**抛异常**。 + 而 `StorageAdapter.java:169-170` 的注释明说这个严格是**故意的**。报错文案也不同。 + +**而 `fe/` 和 `regression-test/` 里没有任何测试钉住发出的那个串** ⇒ 这个回归会**绿着上线**。 + +**已确认是死代码的差异**(对抗第一轮夸大、第二轮修正): +- A 与 B 的 `DEFAULT` 链差异,在 fe-core 侧**只能**经 `StorageAdapter.getAwsCredentialsProvider()` + (`:391`)到达,而该方法**零调用者**(全仓只有它自己的声明和 javadoc)。 + ⇒ 「匿名访问突然开始签名」这个吓人的场景**今天不可能发生**。 +- `AwsCredentialsProviderFactory.getV2ClassName(mode)`(单参,`:141-162`)同样零调用者。 + +**真正对齐的部分**:只有发给 BE 的 `AWS_CREDENTIALS_PROVIDER_TYPE` 值(`StorageAdapter.java:614,:623`) +—— 两个枚举声明了相同的 7 个常量且 `getMode() == name()`。 + +### 3.4 能砍的:约 146 行纯死代码 + +- `StorageAdapter.getAwsCredentialsProvider()`(`:383-416` 含 javadoc)+ + `staticAwsCredentialsProvider(...)`(`:418-429`)+ `s3AwsCredentialsProvider(...)`(`:431-458`) +- `AwsCredentialsProviderFactory.createV2(mode,boolean)`(`:46-68`)+ + `createDefaultV2(boolean)`(`:80-99`)+ 单参 `getV2ClassName(mode)`(`:136-162`) + +**必须保留**:`StorageAdapter.getAwsCredentialsProviderMode()`(`:379-381`)和 `s3CredentialsMode` 字段 +(被 `AzureGuessRoutingParityTest` 钉住,且喂 `:614` 那个**活的** BE 值); +`AwsCredentialsProviderFactory.getV2ClassName(mode, boolean)`(`:101-134`)和两个 env 探针(`:70-78`); +`StorageAdapter` 的 `InstanceProfileCredentialsProvider` import(`:42`,`:731` 在用)。 + +--- + +## 4. 🔴 checkstyle 是硬门禁(两份候选设计栽在这) + +`fe/pom.xml:114` 在**父 `<build><plugins>`** 里声明 `maven-checkstyle-plugin`, +`fe/pom.xml:177-183` 把 `check` 绑到**每个模块的 `validate` 阶段**; +`fe/check/checkstyle/checkstyle.xml:27` 设 `severity=error`、`:167` 开 `UnusedImports`; +`suppressions.xml` 对这两个路径都没有豁免。 + +⇒ **漏删一个 import,不带 flag 的 `mvn test` 会在跑任何测试之前就中止。** + +三份候选设计里**有两份的 import 清单是错的**。修正后的精确清单见 `tasklist.md` 各任务。 + +--- + +## 5. 风险登记 + +| 级别 | 风险 | 缓解 | +|---|---|---| +| 🟠 中 | 删除后,理论上的 null-supplier 窗口从 **fail-loud** 变 **fail-silent**(返回 `emptyMap` → 丢掉 iceberg `warehouse→fs.defaultFS` 桥接 → 而且因为 setter 故意不重置缓存,错误的 `StorageBindings` 会被**永久缓存**) | 窗口今天不可达(§2.2)。处置方式见 [`open-decisions.md`](./open-decisions.md) **OD-1**(推荐:null 分支 `throw`,精确保留今天行为) | +| 🟠 中 | 删除类改动**不能只信增量编译**——`fe-core/target/classes` 里确实存在无源文件的陈旧 `.class` | 每步 `rm -rf fe-core/target/{classes,test-classes}` + 全反应堆 `clean test-compile`(**含测试源,禁 `-Dmaven.test.skip=true`**)+ 全仓 `grep -rIn`(不是符号 grep) | +| 🟠 中 | checkstyle `UnusedImports` 门禁(§4) | 每步把 `checkstyle:check` 当**阻塞项**跑 | +| 🟠 中 | `fe-connector-api` 的录制基线(`ConnectorMetadataSurfaceTest` ↔ `connector-metadata-methods.txt`)是本分支已知盲区——全反应堆 test-compile **不跑 surefire** | FPC-03 的验收**显式**跑 `-pl fe-connector/fe-connector-api test`。**预期不需要刷基线**(该模块不依赖 fe-core,72 行基线只用 `connector.api.*`/`java.*` 类型)——**一旦红了就停手** | +| 🟢 低 | FPC-02 删的 `StorageAdapter.getAwsCredentialsProvider()` 是上游 `f499c78c67c`(#66004)整体带进来的;若 apache/doris master 有或将有调用者,下次 rebase 会 modify/delete 冲突 | FPC-02 可整项丢弃,不影响其它任务。落地前 grep 一次上游 master | + +--- + +## 6. 兄弟目录:明确不在范围 + +### `fileformat/`(11 文件 ~1318 行)— 命名不当,不是架构违规 +解析的是 LOAD(broker/routine/mysql)、`SELECT … INTO OUTFILE`、`COPY INTO`、文件 TVF 的读写选项, +直接吐 fe-thrift 类型(`FileFormatProperties.java:21-25` import `TFileAttributes`/`TFileCompressType`/ +`TFileFormatType`/`TResultFileSinkOptions`)。24 个消费者**全在 fe-core**,零连接器 / 零 fe-filesystem 引用。 +**它挂在 `datasource.property` 下只是名字取错了**,改名是 24 文件的纯 churn,无架构收益。 + +### `constants/`(3 文件)— 不是数据源属性 +`AIProperties` 是 AI/LLM 模型资源;`RemoteDorisProperties` 是 Doris-to-Doris 目录的**纯键名常量、零解析** +(import 者只有 `catalog/AIResource.java:22`、`AIResourceTest.java:24`、 +`datasource/doris/RemoteDorisExternalCatalog.java:26`)。 +🔎 **顺带发现一个可独立清理项**(另开 ticket,不并入本任务): +`constants/BaseProperties.getCloudCredential(...)` **零调用者**,其唯一作用是当 `AIProperties.java:28` 的空父类。 + +### `StorageAdapter.checkAzureOauth2OnlyForIcebergRest()`(`:822-843`)— 真违规,但要单独一刀 +它在 **storage 路径**上读 **metastore 命名空间**的键(`type` / `iceberg.catalog.type`), +是货真价实的 ARCH-GOAL 违规。但它是上游 #66004 的代码,带着**刻意的大小写敏感怪癖**, +需要自己的一刀 + e2e。**在此记下,免得丢。** + +--- + +## 7. 被否方案(存档,免得下次重走) + +1. **把 `common/` 搬去 `fe-filesystem-s3-base` / `fe-filesystem-api` / `fe-foundation`** — §3.2 三条独立理由。 +2. **把 fe-core 三个 adapter 改指向 `fe-filesystem-s3-base` 的现成实现,然后删 `common/`** + (architectural 视角的完整 S3–S5 轨道:把 `S3CredentialsProviderType` 上提到 `fe-filesystem-api`、 + 调和 `hadoopClassName`、再删 `common/`)。 + —— 它**在架构上是对的终局,本文不称其为错**,但:① 需要用户对 + `Config.aws_credentials_provider_version` 的 v1 分支(`Config.java:3740`)和「最终发哪条 DEFAULT 链」 + 两个问题拍板;② 会改 FE→hadoop 的线上串和持久化的 `AWS_CREDENTIALS_PROVIDER_TYPE` 别名族; + ③ **对 catalog-SPI 迁移零收益**(消费者是内部存储代码)。**另开 ticket 跟踪。** +3. **给 fe-core 加新 SPI**(如在 `S3CompatibleFileSystemProperties` 上加 + `hadoopCredentialsProviderClassName(boolean)`)—— 为了让 fe-core **继续留在**凭证生意里而发明的 + additive SPI,违背方向;且六个 S3 方言无从作答(`AbstractDelegatingS3Properties.java:216-224` + 把 role/external-id 写死为空)。 +4. **让 fe-core 依赖 `fe-connector-metastore-api`** —— FPC-03 之后 fe-core 不再持有任何 metastore 代码, + 这个依赖零用户,还会把 `fe-kerberos` 拖上 fe-core 的 classpath。 + 🔎 顺带记一笔:该模块 `pom.xml:64` 的注释「This module is compiled into fe-core」**是过时的假话** + —— 没有任何 pom 声明它,它是打进 paimon/iceberg/hms/hudi 各插件 zip 的。 +5. **收敛第三份 AWS twin**(`fe-connector-iceberg/AwsCredentialsProviderModes`)—— 它确实是**第三个变体** + (未知模式回退 DEFAULT 而非抛异常;DEFAULT 不发类名),有自己的测试钉着, + 收敛需要新增「连接器 → fe-filesystem-api」依赖。不做。 + +--- + +## 8. 诚实声明:尚未验证的部分 + +- **没跑任何 maven 构建**:`tasklist.md` 里的验证命令是开好的方子,**不是已跑的结果**。 +- **没查 apache/doris master** 是否有 `StorageAdapter.getAwsCredentialsProvider()` 的调用者 + ⇒ FPC-02 的 rebase 冲突风险是**陈述**不是实测。 +- **没跑 e2e**(需要集群)。相关套件(`regression-test/suites/aws_iam_role_p0/*`)只做了 grep: + 用的是 `INSTANCE_PROFILE` / `CONTAINER` / `ANONYMOUS` 这类规范名,A/B 两个枚举都同样接受; + 全仓唯一的 `v1` 行是注释掉的(`test_tvf_anonymous.groovy:30`)。 +- **`ExternalCatalog.buildHadoopConfiguration(Map)` 的调用者没有枚举** ⇒ FPC-04 明确排除它。 +- **FE 侧的 hadoop s3a client 是否真的会去实例化 `fs.s3a.aws.credentials.provider` 里点名的类**, + 没有端到端追到底:发出与传播链路追到了(`StorageAdapter.java:730,:739` → `CatalogProperty.java:383` + → `ExternalCatalog.java:209,:252`),**终端消费者没追**。这决定了 §3.3 差异 (1) 的严重程度上限。 diff --git a/plan-doc/fecore-property-cleanup/open-decisions.md b/plan-doc/fecore-property-cleanup/open-decisions.md new file mode 100644 index 00000000000..683d99ddb14 --- /dev/null +++ b/plan-doc/fecore-property-cleanup/open-decisions.md @@ -0,0 +1,115 @@ +# 需要拍板的决策清单 + +> 只放**动手前必须先定**的事。每条给出背景、选项、代价、推荐值。 +> 拍板后**就地**标 ✅ 并写明结论与日期,**不要删除条目**(历史要留痕)。 +> 细节与证据在 [`design.md`](./design.md),这里不重复。 +> +> 下面条目里的「我」指做调研的那个 session,「你」指做决定的人。 + +--- + +## ⬜ OD-1 —— 删掉 metastore 后,null-supplier 分支该 fail-loud 还是 fail-silent? + +**阻塞**:[`tasklist.md`](./tasklist.md) **FPC-03**。不定这条就不能写 FPC-03 的代码。 + +### 背景(先讲清楚在说什么) + +`CatalogProperty` 有个方法叫 `resolveDerivedStorageDefaults()`,作用是**给存储配置补默认值**。 +比如你建了个 iceberg hadoop 目录,只写了 `warehouse=hdfs://myns/wh`,没写 `fs.defaultFS`, +那么系统要能自己推出 `fs.defaultFS=hdfs://myns` —— 这个推导就叫「派生存储默认值」。 + +今天它的逻辑是**二选一**: + +```java +if (pluginSupplier != null) { + return pluginSupplier.get(); // ← 路 A:问连接器要(插件目录走这条) +} +return getMetastoreProperties().getDerivedStorageProperties(); // ← 路 B:fe-core 自己算(要删的) +``` + +FPC-03 要删掉路 B。问题是:**路 A 的 `pluginSupplier` 为 null 时怎么办?** + +### 为什么会有「supplier 为 null」这种时刻 + +`PluginDrivenExternalCatalog` 的初始化顺序是: + +``` +:150 createConnectorFromProperties() ← 先造连接器 + └─ :206-208 造 DefaultConnectorContext,此时已经把 catalogProperty 的 + storage supplier 接上去了 +:177 setPluginDerivedStorageDefaultsSupplier(...) ← 后装派生 supplier +``` + +也就是说,**在第 150 行到第 177 行之间存在一个窗口**:context 已经能访问 storage 了, +但派生 supplier 还是 null。 + +**这个窗口今天走不进去**(我逐个复核过): +- `PaimonConnector` 构造函数(`:151-166`)只传了个方法引用 `this::pluginAuthenticator`, + 那是**惰性**的(`:174-186` double-check 之后才真正计算),构造时不碰 storage +- `IcebergConnector` 构造函数(`:215+`)同样 +- `HiveConnector` / `HudiConnector` 构造函数里**没有任何** `storage()` 调用 +- `validateProperties` 显式传 `Collections.emptyMap()`,javadoc 明写「验证不需要 storage」 + +**所以这不是一个今天存在的 bug,而是一个「万一将来有连接器在构造期碰 storage,会怎样」的问题。** + +### 关键:删除会把「响亮报错」变成「静默出错」 + +| | supplier 为 null 时会发生什么 | +|---|---| +| **今天** | 走路 B → `MetastoreProperties.create(props)` → 注册表里没有 iceberg/paimon/hms 的工厂 → **抛 `IllegalArgumentException`**,FE 日志里响亮报错 | +| **删完之后(若写 `return emptyMap()`)** | 静默返回空 → 丢掉 `warehouse → fs.defaultFS` 的桥接 → **而且**因为 `setPluginDerivedStorageDefaultsSupplier`(`:279-281`)**故意不重置缓存**,这个错误的 `StorageBindings` 会被**永久缓存**到下次 ALTER | + +一个 HA nameservice 的 hadoop iceberg 目录会因此**绑不上 HDFS**,而且不报错。 + +### 三个选项 + +| | 做法 | 代价 | +|---|---|---| +| **A(推荐)** | null 分支写 `throw new IllegalStateException("...")` | **精确保留今天的行为**(今天走到这里就是抛)。不新增 fe-core 能力(是替换现有 throw,不是发明新逻辑),守铁律 A。守 Rule 12「fail loud」。<br>⚠️ 唯一不精确之处:今天属性图为**空**时 `getMetastoreProperties()` 返回 null 而**不抛**(`:327-329`),最终得到 `emptyMap`;选 A 会变成抛。但空属性图的目录本来也没有存储可派生,且 `RemoteDorisExternalCatalog`/`TestExternalCatalog` 根本不走 storage 路径 | +| **B** | null 分支写 `return Collections.emptyMap()` | 代码最简,但**把响亮失败变成静默降级**。违 Rule 12。今天不可达,但一旦将来某个连接器在构造期碰 storage,这就是个查半天的幽灵 bug | +| **C(不推荐)** | 照调研报告原方案,把 `setPluginDerivedStorageDefaultsSupplier` 那段**语句提前**到造连接器之前 | **修不干净**:提前之后 lambda 捕获的 `connector` 字段**仍然是旧值/null**(`connector = newConnector` 发生在 `createConnectorFromProperties()` 返回之后),所以窗口内照样拿到 `emptyMap`。是 churn 不是修复。<br>⚠️ 这是调研报告把它列为 HIGH 风险前置项的方案,我复核后判定**无效**,特此记下免得下次又被报告带偏 | + +### 我的推荐 + +**选 A。** 理由:它是唯一「零行为变更 + 守 fail-loud」的写法,代码量和 B 一样是一行, +而且把一个**今天靠人工审计才知道不可达**的窗口,变成**万一走进去会立刻自曝**。 + +> **拍板结果**:(待填) +> **日期**:(待填) + +--- + +## ⬜ OD-2 —— FPC-02(删 AWS 死构造臂)做不做? + +**不阻塞任何其它任务**,可以随时决定,也可以永远不做。 + +### 背景 + +`StorageAdapter.getAwsCredentialsProvider()`(`:383-458`,含两个私有 helper)和 +`AwsCredentialsProviderFactory` 的 `createV2` / `createDefaultV2` / 单参 `getV2ClassName` +加起来约 **146 行,零调用者**(我复核过:全仓只有它自己的声明和 javadoc)。 + +### 唯一的顾虑 + +这段代码是从上游 `f499c78c67c`(#66004)**整体带进来**的。如果 apache/doris master 上有、 +或将来加了调用者,下次 rebase 就会撞 modify/delete 冲突。 + +**我没有查上游 master**(`design.md` §8 已如实声明)。 + +### 选项 + +| | 做法 | +|---|---| +| **A(推荐)** | 先 grep 一次上游 master;无调用者就删。删了 146 行死代码,且让 `common/` 的存活理由更清晰(剩下的都是真在用的) | +| **B** | 不做。反正是死代码,留着不碍事,省掉一次潜在的 rebase 冲突 | + +**我的推荐:A**,但**低优先级**——它和主线(FPC-03)完全解耦,什么时候做都行。 + +> **拍板结果**:(待填) +> **日期**:(待填) + +--- + +## ✅ 已拍板 + +(暂无。第一条拍板后移到这里,保留原文并补上结论与日期。) diff --git a/plan-doc/fecore-property-cleanup/progress.md b/plan-doc/fecore-property-cleanup/progress.md new file mode 100644 index 00000000000..1a064f44bbd --- /dev/null +++ b/plan-doc/fecore-property-cleanup/progress.md @@ -0,0 +1,90 @@ +# 📜 进度记录(append-only) + +> **只追加,不覆盖**。新条目写在**底部**(时间正序)。 +> 每条格式:日期 /(commit)/ 做了什么 / 结论 / 踩了什么坑。 +> 「下一步做什么」不写这里(在 [`HANDOFF.md`](./HANDOFF.md));「勾到哪了」不写这里(在 [`tasklist.md`](./tasklist.md))。 + +--- + +## 2026-07-28 — 任务空间建立 + 调研完成(FPC-00 / FPC-01a) + +**基线**:`3468d905eb3`,分支 `catalog-spi-review-21`。**代码零改动。** + +### 起因 + +用户提出独立调研任务:fe-core `datasource/property/` 下的 `common` 和 `metastore` 两个目录, +能不能删掉、或者迁进 `fe/fe-connector`?给出方案或给出不能的理由。 +背景原则:元数据服务(Glue 之类)归 `fe-connector-metastore-api`,存储(hdfs/s3 之类)归 `fe-filesystem`。 +用户明确要求「不要局限于当前逻辑,必要时可增删任意模块的接口」。 + +### 做法 + +Workflow 编排:8 路并行侦察(产出 172 条 finding)→ 3 路独立设计 +(minimal / architectural / risk 三种视角,**结论并不一致**)→ 6 项对抗验证(每项被推翻后再走第二轮独立复核) +→ 综合。之后由本 session 对**承重结论逐条亲自复核代码**(不只采信 agent)。 + +三份设计的裁决分歧本身是有信息量的: + +| 视角 | `common/` | `metastore/` | +|---|---|---| +| minimal | KEEP | DELETE | +| architectural | DELETE_AND_REDIRECT | DELETE | +| risk | KEEP | DELETE_AND_REDIRECT | + +⇒ `metastore/` **三路一致要删**;`common/` **2:1 主张留**,且主张删的那路(architectural) +自己也承认需要用户先拍两个板。最终采纳「留」。 + +### 结论 + +- **`metastore/`(4 文件 333 行)→ 整体删除**,连带孤儿 `ConnectionProperties.java`(140 行)。 + 运行期两道门都不可达;包里本来就零解析(唯一注册的 `TrinoConnectorPropertiesFactory` 连 + `initNormalizeAndCheckProps()` 都不调);持久化无坑;接班人 + (`fe-connector-metastore-api` 的 `MetaStoreProperties` + `MetaStoreProviders.bind` + + `Connector.deriveStorageProperties`)早已在生产路径上跑着 ⇒ **没有任何东西需要搬**。 +- **`common/`(2 文件 237 行)→ 留在 fe-core,只砍死的一半(~146 行)**。 + 它服务的是**内部存储**(冷存 StoragePolicy / 云上 StorageVault / TVF / backup / export), + 零个 fe-connector、零个 fe-filesystem 模块 import 它 —— **根本不在这次迁移的射程内**。 + +### 🔴 踩坑记录(最有价值的部分) + +**坑 1 —— 「重复造轮子」的误判,被对抗验证两轮推翻。** +我的初判是:`common/` 和 `fe-filesystem-s3-base` 的 +`S3CredentialsProviderType`/`S3CredentialsProviderFactory` 是同一套逻辑的两份副本, +应该「删除 + 把消费者指向现成实现」。**错。** 两条活的行为差异: +① 发给 hadoop 的凭证串会多出 `ProfileCredentialsProvider`; +② 模式串接受面放宽(空串 / `ENVIRONMENT` / `WEB_IDENTITY_TOKEN_FILE` 从抛异常变成接受, +而 `StorageAdapter.java:169-170` 注释明说这个严格是**故意的**)。 +**且全仓没有任何测试钉住那个串** ⇒ 换掉会绿着上线一个回归。 +**通用教训**:「两个类长得像 ⇒ 可以合并」是个高频误判;判定等价必须**逐字段比对输出** +(尤其是会发到 BE / 写进 hadoop conf 的**字符串**),而不是比对结构。 +顺带一提,仓库里其实有**四份**这套逻辑(第三份在 `fe-connector-iceberg`,第四份在 +`fe-connector-metastore-iceberg`),而且第三份的语义又和前两份都不同(未知模式回退 DEFAULT 而非抛)。 + +**坑 2 —— 对抗验证抓到「两份候选设计会挂在 checkstyle 上」。** +`fe/pom.xml:177-183` 把 checkstyle `check` 绑到**每个模块的 validate 阶段**, +`checkstyle.xml:167` 开着 `UnusedImports` 且 `severity=error` ⇒ +**漏删一个 import,不带 flag 的 `mvn test` 会在跑任何测试之前就中止**。 +三份设计里两份的 import 清单是错的(漏了 `CatalogProperty.java:25` 的 `Preconditions`; +另有一份把 `StorageAdapter` 的 import 错记到 `AwsCredentialsProviderFactory` 头上)。 +`tasklist.md` 里是**修正后**的精确清单。 + +**坑 3 —— 调研报告自己给的 HIGH 风险缓解方案,复核后是无效的。** +报告主张「把 `setPluginDerivedStorageDefaultsSupplier` 语句提前到造连接器之前」来关闭 +null-supplier 窗口。复核发现**修不干净**:提前之后 lambda 捕获的 `connector` 字段仍是旧值/null +(`connector = newConnector` 发生在 `createConnectorFromProperties()` 返回**之后**), +窗口内照样得到 `emptyMap`。 +更进一步,我逐个复核了四个连接器的构造函数(paimon `:151-166` / iceberg `:215+` / hive / hudi), +**没有任何一个在构造期碰 storage**(都只传惰性方法引用 `this::pluginAuthenticator`), +`validateProperties` 还显式传 `Collections.emptyMap()` ⇒ **这个窗口今天根本不可达**。 +所以它不是 blocker,而是「删完之后要 fail-loud 还是 fail-silent」的选择题 → 收敛成 **OD-1**。 +**通用教训**:**调研报告(哪怕是自己多轮对抗产出的)的「风险 + 缓解」也要复核**, +高危标注可能是理论可达性而非真实可达性,配的解法可能治标不治本。 + +### 产出 + +本任务空间 `plan-doc/fecore-property-cleanup/`: +`README.md` · `design.md` · `tasklist.md` · `open-decisions.md` · `HANDOFF.md` · `progress.md`。 + +### 卡点 + +**OD-1 待用户拍板**(null-supplier 分支 fail-loud vs fail-silent),阻塞 FPC-03。 diff --git a/plan-doc/fecore-property-cleanup/tasklist.md b/plan-doc/fecore-property-cleanup/tasklist.md new file mode 100644 index 00000000000..38d5c98acdf --- /dev/null +++ b/plan-doc/fecore-property-cleanup/tasklist.md @@ -0,0 +1,177 @@ +# ✅ Task List — 清理 fe-core `datasource/property/{common,metastore}` + +> **本任务的唯一进度清单**。完成一项即把 `[ ]` 勾成 `[x]`(随 commit 更新)。 +> **「怎么做」看 [`design.md`](./design.md),「下一步做什么」看 [`HANDOFF.md`](./HANDOFF.md), +> 「还没定的事」看 [`open-decisions.md`](./open-decisions.md)。** +> **⚠️ 行号信 HEAD 不信文档**(基线 = 2026-07-28 / `3468d905eb3`)。 +> 状态:⬜ 未开始 | 🚧 进行中 | ✅ 完成 | ⛔ blocked。编号永不复用。 + +--- + +## 🎯 总判据(唯一的「做完了没」标准) + +```bash +R=/mnt/disk1/yy/git/wt-catalog-spi + +# ① fe-core 不再有 metastore 属性代码(基线 5 文件 → 0) +ls $R/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/ 2>/dev/null | wc -l # → 0 +test -f $R/fe/fe-core/src/main/java/org/apache/doris/datasource/property/ConnectionProperties.java # → 不存在 + +# ② 全仓无残留引用(含 groovy / 注释 / 文档) +grep -rIn 'MetastoreProperties\|MetastorePropertiesFactory\|AbstractMetastorePropertiesFactory\|TrinoConnectorPropertiesFactory\|ConnectionProperties\|checkMetaStoreAndStorageProperties\|getMetastoreProperties' \ + $R/fe $R/regression-test $R/tools $R/gensrc --exclude-dir=target # → 空 + +# ③ common/ 只剩活代码 +grep -rn 'createV2\|createDefaultV2\|getAwsCredentialsProvider()' $R/fe --exclude-dir=target # → 空 + +# ④ 编译 + 门禁全绿(每步都要,不只最后一次) +mvn -f $R/fe/pom.xml -T 1C clean test-compile -Dcheckstyle.skip=true +mvn -f $R/fe/pom.xml -pl fe-core checkstyle:check +``` + +**⚠️ 三条纪律**(本仓库已知踩坑,见 `design.md` §5): +1. 删除类改动**不能只信增量编译** → 每步先 `rm -rf fe-core/target/{classes,test-classes}`。 +2. 全反应堆**必须含测试源**,**禁 `-Dmaven.test.skip=true`**;且必须 `-Dcheckstyle.skip=true` + (否则 checkstyle 扫 generated-sources 退化成平方级,构建卡死)。 +3. checkstyle 的 `UnusedImports` 是**阻塞门禁**,改为**只对改动模块**单独跑 `checkstyle:check`。 + +--- + +## 阶段 0 — 调研(✅ 已完成) + +- [x] **FPC-00** 事实基线:8 路并行侦察(172 条 finding)+ 3 路独立设计(minimal / architectural / risk) + + 6 项对抗验证(**2 项被推翻**)→ [`design.md`](./design.md) + - 🔴 **被推翻的初判必读**:`common/` **不是** `fe-filesystem-s3-base` 的可替换双胞胎 + (两条活的行为差异,`design.md` §3.3)。这是本轮最容易重犯的错。 +- [x] **FPC-01a** 承重结论逐条复核(本文作者亲验,非仅采信 agent): + `metastore/` 唯一 import 者 · 两道门皆不可达 · `ConnectionProperties` 在 `StorageAdapter:856` + 仅是 javadoc · `getAwsCredentialsProvider()` 零调用者 · fe-core pom 无 s3-base 依赖 · + **四个连接器构造函数均不碰 storage ⇒ null-supplier 窗口今天不可达** + +--- + +## 阶段 1 — 🔴 前置拍板(**不拍板不许开工 FPC-03**) + +- [ ] **FPC-01** ⬜ **OD-1 拍板**:删掉 metastore 后,`resolveDerivedStorageDefaults()` 的 null-supplier + 分支要 **fail-loud(`throw`)** 还是 **fail-silent(`return emptyMap()`)**? + 详见 [`open-decisions.md`](./open-decisions.md) **OD-1**(含推荐值与三个选项的代价)。 + - ⚠️ 这条**改变 FPC-03 的代码**,必须先定。 + - ⚠️ 调研报告原提的缓解方案「把 supplier 安装语句提前」**经复核修不干净** + (lambda 读到的 `connector` 字段仍是旧值/null),已在 OD-1 中列为**不推荐**。 + +--- + +## 阶段 2 — 删死代码(独立,可先做,也可整项丢弃) + +- [ ] **FPC-02** ⬜ 删 AWS provider 的死构造臂(**~146 行,零行为变更**) + - **文件**: + - `fe/fe-core/src/main/java/org/apache/doris/datasource/storage/StorageAdapter.java` + - `fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java` + - (仅注释)`fe/fe-connector/fe-connector-iceberg/.../AwsCredentialsProviderModes.java:36-37`、 + `IcebergConnector.java:1113-1114` + - **删**: + - `StorageAdapter`:`getAwsCredentialsProvider()`(`:383-416` 含 javadoc)、 + `staticAwsCredentialsProvider(...)`(`:418-429`)、`s3AwsCredentialsProvider(...)`(`:431-458`) + - `AwsCredentialsProviderFactory`:`createV2(mode,boolean)`(`:46-68`)、 + `createDefaultV2(boolean)`(`:80-99`)、**单参** `getV2ClassName(mode)`(`:136-162`) + - **import 精确修剪**(⚠️ 三份候选设计里有两份这里是错的): + - `StorageAdapter` 删 8 个:`AnonymousCredentialsProvider` `AwsBasicCredentials` + `AwsCredentialsProvider` `AwsSessionCredentials`(`:38-41`)、`StaticCredentialsProvider`(`:43`)、 + `Region`(`:44`)、`StsClient`(`:45`)、`StsAssumeRoleCredentialsProvider`(`:46`) + - `AwsCredentialsProviderFactory` 删 **2 个**:`:26` `AwsCredentialsProvider` **和** + `:27` `AwsCredentialsProviderChain` + (⚠️ 某份设计写的 "StaticCredentialsProvider 族" **在本文件里不存在**,它在 `StorageAdapter.java:43`) + - **✋ 必须保留**:`StorageAdapter.getAwsCredentialsProviderMode()`(`:379-381`)+ `s3CredentialsMode` 字段 + (被 `AzureGuessRoutingParityTest` 钉住,且喂 `:614` 这个**活的** BE 值); + `StorageAdapter` 的 `InstanceProfileCredentialsProvider` import(`:42`,`:731` 在用); + `AwsCredentialsProviderFactory.getV2ClassName(mode, boolean)`(`:101-134`)+ 两个 env 探针(`:70-78`) + - **验收**: + ```bash + R=/mnt/disk1/yy/git/wt-catalog-spi + grep -rIn 'getAwsCredentialsProvider()\|createV2\|createDefaultV2' $R/fe $R/regression-test --exclude-dir=target + rm -rf $R/fe/fe-core/target/classes $R/fe/fe-core/target/test-classes + mvn -f $R/fe/pom.xml -T 1C clean test-compile -Dcheckstyle.skip=true + mvn -f $R/fe/pom.xml -pl fe-core test -Dcheckstyle.skip=true -DfailIfNoSpecifiedTests=false \ + -Dtest='AzureGuessRoutingParityTest,S3ThriftAdapterParityTest,CloudObjectStoreAdapterParityTest,LocationPathTest,DefaultConnectorContextBackendStoragePropsTest,DefaultConnectorContextNormalizeUriTest' + mvn -f $R/fe/pom.xml -pl fe-core checkstyle:check # 阻塞项:证明 import 修剪精确 + ``` + - 🟢 **可整项丢弃**:不影响 FPC-03。落地前 grep 一次上游 master 是否有 + `getAwsCredentialsProvider()` 调用者(rebase 冲突风险,`design.md` §5) + +--- + +## 阶段 3 — 主删除(**依赖 FPC-01 拍板**) + +- [ ] **FPC-03** ⬜ 退役整个 `metastore/` 集群 + 孤儿 `ConnectionProperties` + (**删 5 文件 473 行 + 从 `CatalogProperty` 挖掉 ~45 行;零 pom / 零连接器 / 零 fe-filesystem 改动**) + - **`CatalogProperty.java` 改动**: + 1. `resolveDerivedStorageDefaults()`(`:264-272`)→ 只走 supplier; + null 分支按 **FPC-01 的拍板结果**写(`throw` 或 `return Collections.emptyMap()`) + 2. 更新其 javadoc(`:257-263` 里 `{@link}` 了 `MetastoreProperties`)和 `:274-278` 的 javadoc + 3. 删字段 `metastoreProperties`(`:94`)+ `resetAllCaches()` 里的 `this.metastoreProperties = null;`(`:189`) + 4. 删 `checkMetaStoreAndStorageProperties(Class)`(`:307-321`)和 `getMetastoreProperties()`(`:323-345`) + 5. **import 精确修剪**:`:20` `UserException`(只被 `:312`/`:336` 用)、`:21` `MetastoreProperties`、 + **`:25` `Preconditions`(只被 `:316-317` 用 —— ⚠️ 两份设计漏了这个)**、 + `:29` `ExceptionUtils`(只被 `:314`/`:339` 用)、`:30-31` `LogManager`/`Logger`, + 以及 **`:48` 的 `LOG` 字段**(唯一使用点是 `:337`)。 + **✋ 保留** `MapUtils`(`:250` 在用)和 `Collections`。 + - **`git rm`**: + - `property/metastore/MetastoreProperties.java` + - `property/metastore/MetastorePropertiesFactory.java` + - `property/metastore/AbstractMetastorePropertiesFactory.java` + - `property/metastore/TrinoConnectorPropertiesFactory.java` + - `property/ConnectionProperties.java`(删 metastore 后成孤儿,`design.md` §2.5) + - **注释清理**(无编译影响,但守 Rule 12「不留悬空引用」): + - `StorageAdapter.java:856` —— 那是 **javadoc 散文**,改写掉即可,**不是**编译依赖 + - `fe-connector-paimon/.../TcclPinningConnectorContext.java:49` + - `fe-core/src/test/.../CatalogPropertyPluginStorageDerivationTest.java:33-34,:53-54` + - **⚠️ `CatalogPropertyPluginStorageDerivationTest` 不许删也不许合并**:三个用例都装了 supplier + (`:55`/`:74`/`:83`),删除后**照常通过**;但它注释里写的变异(「把 + `resolveDerivedStorageDefaults` 改回走 `getMetastoreProperties()`」)**变得无法表达** ⇒ + 按 Rule 9 把变异描述**改钉到一个仍然存在的变异上**。它是插件派生路径的唯一守卫。 + - **验收**: + ```bash + R=/mnt/disk1/yy/git/wt-catalog-spi + grep -rIn 'MetastoreProperties\|MetastorePropertiesFactory\|AbstractMetastorePropertiesFactory\|TrinoConnectorPropertiesFactory\|ConnectionProperties\|checkMetaStoreAndStorageProperties\|getMetastoreProperties' \ + $R/fe $R/regression-test $R/tools $R/gensrc --exclude-dir=target + rm -rf $R/fe/fe-core/target/classes $R/fe/fe-core/target/test-classes + mvn -f $R/fe/pom.xml -T 1C clean test-compile -Dcheckstyle.skip=true + mvn -f $R/fe/pom.xml -pl fe-core test -Dcheckstyle.skip=true -DfailIfNoSpecifiedTests=false \ + -Dtest='CatalogPropertyPluginStorageDerivationTest,CatalogPropertyEffectiveRawStoragePropsTest,HmsGsonCompatReplayTest,IcebergGsonCompatReplayTest,PaimonGsonCompatReplayTest,PluginDrivenExternalCatalog*Test' + # 🔴 录制基线必须显式跑(全反应堆 test-compile 不跑 surefire —— 本分支已知盲区) + mvn -f $R/fe/pom.xml -pl fe-connector/fe-connector-api test -Dcheckstyle.skip=true + mvn -f $R/fe/pom.xml -pl fe-core checkstyle:check + ``` + **预期不需要刷 `connector-metadata-methods.txt`**(fe-connector-api 不依赖 fe-core, + 72 行基线只用 `connector.api.*`/`java.*` 类型)—— **一旦红了就停手,别顺手刷基线。** + +--- + +## 阶段 4 — 可选清扫(**另起提交,落地前重新 grep**) + +- [ ] **FPC-04** ⬜ 清扫 fe-core 已死的 storage 门 + - **仅当执行时重新 grep 确认零调用者**才做: + `ExternalCatalog.getHadoopProperties()`、`ExternalCatalog.getConfiguration()`(已标 `@Deprecated`) + + `buildConf()` 及其缓存字段、`CatalogProperty.getBackendStorageProperties()`、 + `CatalogProperty.getOrderedStorageAdapters()` + - **✋ 不要碰** `ExternalCatalog.buildHadoopConfiguration(Map)` —— 它的调用者**没有枚举过** + - **收益**:做完后 `PluginDrivenExternalCatalog.java:207-208` 成为 `initStorageAdapters()` 的 + **唯一入口(由构造保证,而非靠人工审计)** + - **验收**:逐符号零调用者 grep → **完整** `mvn -pl fe-core test -Dcheckstyle.skip=true --fail-at-end` + (⚠️ 它动的是每个 catalog 都继承的基类,**窄 `-Dtest` 列表不够**)→ `checkstyle:check` + - 🟢 刻意与 FPC-03 分开,好让 FPC-03 保持**可单独回滚** + +--- + +## 📋 单列后续(**不并入本任务**,各自开 ticket) + +- **SEP-1** `StorageAdapter.checkAzureOauth2OnlyForIcebergRest()`(`:822-843`)在 storage 路径读 + metastore 命名空间键(`type` / `iceberg.catalog.type`)—— 真的 ARCH-GOAL 违规,但带着上游 #66004 + 刻意的大小写敏感怪癖,需单独一刀 + e2e。 +- **SEP-2** architectural 视角的完整 S3–S5 轨道(把 `S3CredentialsProviderType` 上提 `fe-filesystem-api` + → 调和 `hadoopClassName` → 删 `common/`)。**架构上是对的终局**,但需用户对 + `Config.aws_credentials_provider_version` v1 分支(`Config.java:3740`)+「发哪条 DEFAULT 链」拍板, + 且会改线上串。见 `design.md` §7-2。 +- **SEP-3** `constants/BaseProperties.getCloudCredential(...)` 零调用者,唯一作用是当 `AIProperties` 的空父类。 +- **SEP-4** `fe-connector-metastore-api/pom.xml:64` 注释「This module is compiled into fe-core」是 + **过时的假话**(无任何 pom 声明它;它打进各插件 zip)——顺手改掉。 --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
