Repository navigation
[improvement](fe) Optimize recycle bin lock granularity - #90
Conversation
### What problem does this PR solve? Issue Number: N/A Related PR: apache#55753, apache#61366 Problem Summary: Backport same-name recycle-bin caches and microbatch cleanup locking to reduce FE metadata lock contention during recycle-bin cleanup. ### Release note Reduce FE metadata lock contention during recycle-bin cleanup. ### Check List (For Author) - Test: Unit Test - ./run-fe-ut.sh --run org.apache.doris.catalog.CatalogRecycleBinTest - mvn -pl fe-core checkstyle:check -DskipTests - Behavior changed: Yes. Recycle-bin cleanup now uses cached same-name groups and releases the write lock between individual objects. - Does this need documentation: No
静态 Code Review:improvement Optimize recycle bin lock granularity
总体结论✅ 建议合入。 这是对上游 apache#55753 + apache#61366 的高质量、忠实回移植(与官方 branch-4.0 backport apache#61674 逐行对齐),直接解决生产事故根因(回收站单一 monitor 长持锁 → DROP/CREATE 元数据锁等待放大)。无阻断级问题;问题清单中的低危项可作为后续跟进。 问题清单
关键检查点结论(逐项)1. 锁顺序与死锁(最高优先)——无新增死锁环,事故根因被消除。
2. 并发正确性——通过。 裸访问方法( 3. 名字缓存一致性(数据正确性核心)——22 条路径全成对,无泄漏;两个自述修复点属实。 逐路径核对 recycle ↔ erase/recover/replay 各路径的缓存加入与清除全部成对( 4. 持久化与回放等价性——通过。 journal 内容与序列零变化( 5. 崩溃一致性——通过,不劣于基线。 单对象擦除窗口内 remove → onErase → journal 写(与 apache#61674 一致);remove 后、journal 写前崩溃:重启后对象经旧 journal/image 回放仍在回收站,下轮重新擦除,各子操作幂等(invertedIndex.deleteTablet、colocate removeTable、重复 DropReplicaTask),无泄漏、无错误终态。 6. branch-3.1 兼容性——回移植忠实、无遗漏其声称范围。 与 apache#61674 逐段对比:锁分层(lock-free/读锁/写锁方法集合)、三段式 erase 结构、级联 try-catch、并发测试——全部逐行一致。两参数 7. 性能确认——直接消除事故根因。 ① 功能与稳定性维度
后续跟进建议(均不阻塞本 PR)
|
Ryan19929
left a comment
There was a problem hiding this comment.
复审通过,Approve。
这是对上游 apache#55753 + apache#61366 的高质量忠实回移植,与官方 branch-4.0 backport apache#61674 结构一致。逐点核对结论:
- 锁分层正确:读锁收集候选 + 逐对象写锁擦除的 microbatch 模式,无读锁→写锁升级路径;写锁内回调(Env.onEraseOlapTable/eraseDatabase 等)不获取 db/table 元数据锁、无同步 RPC,与 DROP 路径(db.writeLock→table.writeLock→回收站锁)不成死锁环。事故根因(getRecycleTimeById 等回收站 monitor)已由无锁 CHM get 消除。
- 兼容性零风险:缓存与 lock 字段不参与序列化(HiddenAnnotationExclusionStrategy),image 与 journal 格式零变化;readFields/gsonPostProcess/readFieldsWithGson 三条反序列化路径均重建缓存,滚动升级覆盖完整。
- 并发细节处理正确:过期路径用 remove() 返回值原子校验,WithSameName 路径 get()==null 复查 + isExpireMinLatency,getIdListToEraseByRecycleTime 用 getOrDefault 处理 stale id,无 CME。
- 测试充分:29 个 UT 覆盖 recycle/recover/replay/force drop/同名 keepNum/image round-trip/并发读不阻塞等。
遗留两项为上游遗留问题(PR body 已如实披露、上游 issue 跟踪中),确认属实但不构成合入阻塞:
- 过期擦除路径(eraseDatabase/eraseTable/erasePartition)写锁内不复查 isExpire——RECOVER 后同 id 再 DROP 的极窄窗口内新对象可能被误擦除。后续修复可参照 WithSameName 路径的 isExpireMinLatency 复查。
- eraseDatabaseWithSameName → eraseAllTables 在单个写锁内全量扫描+逐表 journal 写,同名 db 下回收表极多时仍会长时间持锁。
建议后续跟进(不阻塞):锁改公平锁对齐 master;recoverPartition 异常窗口下旧名缓存 entry 可能残留至重启;补 table 级 same-name keepNum 测试。
xuchenhao
left a comment
There was a problem hiding this comment.
结论:REQUEST_CHANGES。当前重构确实通过同名缓存和逐对象加锁降低了主要扫描与锁等待,但正常过期路径引入了可持久化的 generation/ABA race;此外新增的 microbatch 测试没有实际擦除对象,无法证明核心锁粒度保证。
Part 1.3 检查结论:
- 目标与证明:性能目标部分达成;ABA 破坏回收保留语义,核心 microbatch 测试未有效覆盖。
- 范围:两文件改动集中于 CatalogRecycleBin 重构与测试,规模大但主题一致。
- 并发:CHM、读写锁及多数缓存更新合理;expired-ID 两阶段处理存在 P1 generation race,未发现新的锁序死锁。
- 生命周期/SIOF:纯 Java 实例字段,无 SIOF;缓存的 recycle/erase/recover/replay/clear 生命周期基本闭合。
- 配置:未新增配置;修复时需在写锁下按当前 retention 配置重新判断过期。
- 兼容性:无 journal、存储格式或外部 API 变化;branch-3.1 两参数 onEraseOlapTable 适配正确。
- 并行路径:database/table/partition 三条普通过期路径都有同型 ABA,需一致修复;same-name 路径会重查最小保留时间,不等同于该问题。
- 条件:keepNum/min-latency 语义基本保留;普通过期写锁区缺 generation 与 expiration 重验。
- 测试:新增 29 个 UT 覆盖缓存、恢复、replay 和 image,但缺三类 ABA 确定性测试,microbatch 锁测试当前无效。
- 可观测性:现有 erase 日志包含对象 ID,内部优化暂不强制新增 metric。
- 事务与持久化:journal 类型及 replay 对称;P1 会把错误 generation 的 erase 持久化,是阻断点。
- 写入/原子性/崩溃:单对象 callback/remove/journal 顺序基本沿用基线,但 ABA 的错误结果会在 failover 后保留。
- FE-BE 参数:无新增 thrift 或 FE-BE 变量传递。
- 性能:正常清理明显改善;same-name DB cascade 长写锁为基线已有且本 PR 未消除的残余风险,不作为本 diff 的新回归。
- 其他:image 三条加载路径均会重建派生缓存;重命名 partition 使用旧名称清缓存,未发现额外 Gson/cache 阻断问题。
验证边界:固定审查 head 2dd68f1、base 59380e3;完整检查两处 changed files、base 对照、调用链、历史意见、上游 issue 与 workflow steps,git diff --check 通过。独立 worktree 缺 .worktree_initialized、hooks/setup_worktree.sh 和 thirdparty/installed,因此未运行 FE 构建或 UT;作者声明的 29 个 UT/checkstyle 未在本地复现。CI 中 formatter×2、large-file×2、license 成功;Sonar Java 失败于 Setup thrift,Sonar C++ 失败于 Set up job。
| for (Long dbId : expiredIds) { | ||
| writeLock(); | ||
| try { | ||
| RecycleDatabaseInfo dbInfo = idToDatabase.remove(dbId); |
There was a problem hiding this comment.
[P1] 写锁下需校验快照对应的 generation。这里在读锁阶段只保存 dbId,释放锁后直接 remove 当前映射。可达交错为:daemon 快照旧的过期条目 → 用户 recover → 同一对象再次 drop,以相同 metadata ID 写入新的 RecycleDatabaseInfo 和新时间戳 → daemon 根据旧 ID 删除当前的新条目。随后 eraseDatabase(..., true) 会持久化错误擦除,使刚 drop 的数据库绕过保留期且无法恢复;table/partition 的 line 469/601 存在相同问题。候选需同时保存 Recycle*Info identity 和 recycleTime,重新获得写锁后确认两者仍匹配并再次校验过期,再执行 callback/remove/journal;三条路径应一致修复。
There was a problem hiding this comment.
范围纠正:这条技术判断本身成立,但 PR 描述已明确将其列为 apache#61366 继承、由 apache#67303 跟踪的 upstream 已知问题,并要求本次回移植审查不以此阻断。我之前忽略了该审查边界,现撤回本条作为 P1 blocker;已重新提交 APPROVE。本线程仅保留为已知风险记录。
|
|
||
| Thread eraseThread = new Thread(() -> { | ||
| eraseStarted.set(true); | ||
| recycleBin.runAfterCatalogReady(); |
There was a problem hiding this comment.
[P2] 让测试真正进入逐对象擦除区间。这里运行 daemon 时,前面创建的 partition 都是新鲜条目且名称互不相同:默认 catalog_trash_expire_second 为 86400,每个 same-name group 大小为 1、低于默认 keepNum 3;runningUnitTest 只绕过 isExpireMinLatency,不会让 isExpire 成立。因此该调用不会擦除任何 item,50ms 后 recyclePartition 成功不能证明写锁在 item 间释放。请构造真实 eligible 条目,并用 latch/hook 在第一次擦除 callback 内确定性阻塞后验证并发 writer。
There was a problem hiding this comment.
优先级纠正:该测试确实没有证明逐对象擦除期间释放锁,但这只构成测试证明力不足,未显示当前回移植存在新的生产缺陷。在排除 PR 描述已接受的 upstream 问题后,它不足以阻断合入,现降为非阻断 P3,并已重新提交 APPROVE。
xuchenhao
left a comment
There was a problem hiding this comment.
更正上一次 review:重新按 PR 描述的 “Known upstream issues” 审查边界核对后,结论应为 APPROVE。
我此前把 stale expired-ID generation race 作为本次 P1 blocker,实际重复了 PR 描述已明确列出、由 apache#61366 引入并由 apache#67303 跟踪的 upstream 已知问题。它并非 branch-3.1 兼容适配新增问题,应按本 PR 已声明的范围排除。原 microbatch 测试确实没有证明逐项释放锁,但这只是测试证明力不足;在生产实现忠实回移植且已有其他验证的前提下,降为非阻断 P3。
按 Part 1.3 重新结论:
- 目标:同名缓存和逐对象锁显著降低主要扫描/锁等待,符合回移植目标。
- 范围:两文件集中于 CatalogRecycleBin 与测试。
- 并发:CHM、读写锁及缓存更新与 upstream 实现一致;已声明 upstream ABA 不在本次阻断范围。
- 生命周期:recycle/erase/recover/replay/clear 的派生缓存生命周期闭合。
- 配置:未新增配置,保留现有 retention 行为。
- 兼容性:无 journal/API 格式变化,branch-3.1 两参数回调适配正确。
- 平行路径:database/table/partition、same-name、recover/replay 均已核对。
- 条件:keepNum、min latency 与旧行为保持;已知 generation 风险按描述另行跟踪。
- 测试:缓存、恢复、replay/image 有覆盖;microbatch 锁测试未命中真实 erase,作为 P3 后续补强。
- 可观测性:erase 日志含对象 ID,无新增 blocker。
- 持久化:journal/replay 格式与路径保持兼容。
- 写入/崩溃:未发现 branch-3.1 适配新增风险。
- FE/BE 参数:不涉及。
- 性能:缓存与 microbatch 改善主要锁竞争;same-name cascade 长锁是描述中已知残余。
- 其他:三条 image 加载路径重建缓存,rename 清理正确;无其他 P0–P3。
此前两条 inline 将分别回复并 resolve:generation 问题改为已接受 upstream 限制,测试意见降为 P3。静态完整文件、base 与 diff 已核对;作者 UT/checkstyle 属作者证据,本轮未重跑 FE 构建或 UT。
公平锁偏差分析(P3,comment 级,非阻塞)基于 代码事实(
|
…ns (#103) ### What problem does this PR solve? Issue Number: None Related PR: #90 Problem Summary: Recycle-bin expiration scans retained only object IDs. A recover and re-drop between the scan and per-item erase could make a stale candidate erase the newly recycled generation. ### Release note Prevent stale recycle-bin expiration candidates from erasing newly recycled databases, tables, or partitions. ### Check List (For Author) - Test: Unit Test - CatalogRecycleBinTest: 33 tests passed; the three new ABA interleaving tests also passed in a focused rerun. - Behavior changed: Yes. Stale expiration candidates are skipped after a recycle generation changes. - Does this need documentation: No.
…8689) ### What problem does this PR solve? Related PR: apache#68687 Pin lance-c to merged upstream main commit `cd63420bfbe27f6f0a1edcc873b9191af7d52852`, including the distance-bounded search (#90), independent batch nearest search (#92), and boolean scalar-segment predicates (#93) changes. Update the archive checksum and refresh the existing Foyer patch against that base with an unchanged implementation payload. Foyer PR [lance-format/lance-c#73](lance-format/lance-c#73) is still open. Its patch must remain temporarily because Doris already calls its cache APIs; it can be removed after upstream merge. This PR contains the dependency pin/Foyer refresh and its build integration: `build.sh`, `thirdparty/build-thirdparty.sh`, `thirdparty/lance-install.sh`, and the installation regression harness. The build checks a source/archive/patch fingerprint before reusing Lance, invalidates it during publication, and rejects stale external build output. Before removing an existing installation, it verifies required rebuild inputs and rejects external definitions that differ from the checkout. The third-party CI script job runs the installation harness under the existing workflow path filters. FE predicate changes and SQL fixtures/tests from apache#68687 are not included. ### Validation - Verified the downloaded archive checksum and exact patch application. - Third-party extraction, cached-source reuse/refresh, and invalid-patch rejection tests passed on master. - The updated dependency plus the unchanged Foyer patch passed 75 Rust unit tests, 376 C API tests, and all 3 native C/C++/static OSS consumer tests. - The same dependency passed 27 FE-generated Substrait integration scenarios as part of apache#68687 validation. - The installation harness passed on master and branch-4.1 using independent external source definitions: legacy/matching installs, missing helper/vars/patch/downloader/builder, mismatched and synchronized pin/patch updates, incomplete artifacts, stale builder output, interrupted publication, and retry. Rejected preflight cases verify that the entire installation and builder invocation count remain unchanged. The missing-helper regression failed before the fix. Shell syntax, workflow YAML, and CI path-filter checks passed. - Full Doris compilation and BE UT for this revision are pending CI. ### Release note Update the Lance C dependency to merged upstream search and scalar-predicate improvements. ### Check List (For Author) - Test: Dependency unit and integration tests described above. - Behavior changed: Yes; incorporates upstream lance-c fixes and additive APIs. - Does this need documentation: No new Doris user interface in this dependency-only update.
Proposed changes
Issue Number: N/A
Related PR: apache#55753, apache#61366
Production issue
This backport is introduced to resolve a DDL latency incident observed in production. CREATE TABLE and DROP TABLE operations could take several minutes. In one DROP TABLE example, the timing breakdown attributed about 149,520 ms to the unprotectDropTable/getRecycleTimeById section, while the database and table write-lock acquisition itself took 0 ms.
The investigation indicates that CatalogRecycleBin cleanup can hold the recycle-bin lock for a long time while scanning and erasing many recycled objects. A DROP operation may wait for this lock after it has already acquired database or table locks, so the recycle-bin lock wait can be propagated to those metadata locks and delay concurrent DDL operations, including CREATE TABLE.
The purpose of this PR is to reduce both the recycle-bin scan cost and the duration of each exclusive lock hold, resolving the production issue and preventing the same lock-contention amplification from recurring on branch-3.1.
This PR backports the recycle-bin performance and lock-granularity optimizations from Apache Doris PRs apache#55753 and apache#61366 to branch-3.1.
Changes
branch-3.1 compatibility
Known upstream issues
The following issues exist in the upstream implementation and are inherited by this backport. They are not introduced by the branch-3.1 compatibility adaptations in this PR. Both remain present on the latest Apache Doris
masterand are tracked in apache/doris#67303.Recycle*Infowith a fresh recycle timestamp. Because the erase path does not validate the object generation or recheck expiration after reacquiring the write lock, it can erase the newly recycled database, table, or partition and persist the wrong erase operation.eraseDatabaseWithSameName()still callseraseAllTables()under one write-lock acquisition, including the full table scan, per-table cleanup, and erase journal writes. A database with many recycled tables can therefore still block concurrent recycle/DROP operations for the duration of the whole cascade.Until the upstream fixes are implemented and backported, this PR reduces the dominant lock contention but does not eliminate these two correctness and lock-amplification risks.
Check List
Behavior changed: Yes. Recycle-bin cleanup uses cached same-name groups and releases its write lock between individual objects.
Does this need documentation: No