fix(frontend): clean account-owned metadata on DROP ACCOUNT - #27690
Conversation
9ee7617 to
c05f087
Compare
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
10312aa to
792893c
Compare
792893c to
317458b
Compare
aptend
left a comment
There was a problem hiding this comment.
Reviewed exact head 317458b and the full account-drop transaction, including system/tenant context switches, both transaction modes, semantic ownership keys, rollback/short-circuit behavior, cross-account branch ownership, restore interaction, and the distributed same-name recreation case. Build, vet, focused tests, full pkg/frontend tests, and 100 targeted race repetitions pass locally. No blocking issue found.
XuPeng-SH
left a comment
There was a problem hiding this comment.
Blocking correctness issue: the unconditional delete from mo_catalog.mo_branch_metadata where creator = <accountID> is unsafe for chained cross-account branches such as B -> A -> C. creator is the destination account, not exclusive lineage ownership. When A is dropped, A's branch metadata is marked deleted, but C is still live, so the existing reclaim algorithm intentionally keeps the A-edge protect snapshot. This new DELETE then removes A's metadata while its snapshot remains, breaking C's ancestor/LCA chain and leaving an orphan snapshot that cannot be reclaimed when C is later dropped. The existing cross-account source-drop test confirms that a protect snapshot must remain while a child is alive:
matrixone/pkg/vm/engine/test/branch_protect_snapshot_test.go
Lines 550 to 575 in 317458b
|
Addressed in The account-drop path now loads Added Verification after rebasing onto |
XuPeng-SH
left a comment
There was a problem hiding this comment.
Re-reviewed exact head 3b0b740 after my earlier DAG-lifecycle request changes.
The B -> A -> C blocker is fixed: creator is now only the root selector, ancestor reclaim is subtree-aware, live descendants retain the deleted ancestor edge, and the final child drop reclaims both normal edges and their protect snapshots.
One blocking lifecycle gap remains for ALTER generations.
branchReclaimableTableIDs deliberately skips every level='alter' / alter:* row, and BuildBranchMetadataDeleteSQL repeats that exclusion. However, doDropAccount calls only reclaimAccountOwnedBranchMetadataWithBH; it never invokes compactHistoricalAlterLineageWithBH. The compactor is currently triggered only by Data Branch DELETE and snapshot/PITR operations. Once the account is gone, none of those provides a bounded cleanup guarantee.
Deterministic counterexample:
- account A creates a normal branch row T2;
- copy-and-swap ALTER creates an ALTER lineage row T3 owned by A;
- DROP ACCOUNT marks both generations deleted;
- this new planner can delete T2 and
__mo_branch_2but explicitly retains T3 and__mo_branch_3; - no later operation is guaranteed to run the historical-lineage compactor, so the row keyed by the deleted account and its protection snapshot can remain indefinitely.
The added TestComputeAccountBranchReclaimPlan confirms the skip but does not close the lifecycle. This violates issue #27660's requirement that account-owned branch tombstones not accumulate indefinitely (or have a bounded documented cleanup process).
Please run the historical ALTER compaction safely in the account-drop transaction after account snapshot/PITR ownership has been removed, or otherwise include eligible ALTER generations in a bounded ownership-aware cleanup. Add an end-to-end case that creates a branch, produces an ALTER generation, drops the account, and proves both creator metadata and branch snapshots are gone when no external historical owner/live descendant remains. Also retain a control where an external snapshot/PITR or live descendant still requires the generation.
I also rechecked the normal-edge delete guards, system-account context, rollback propagation, feature-limit cleanup, full-DAG lock ordering, cycle bounds, and the exact-head green CI. No other blocker found.
|
Addressed in The lifecycle gap was valid. DROP ACCOUNT now invokes the shared ownership-aware historical ALTER compactor after removing the dropped account user snapshots and PITR rows. Deleted account-owned ALTER generations and their branch-protect snapshots are reclaimed in the same transaction when no external historical source or live descendant remains; a live external account snapshot retains them. Added |
XuPeng-SH
left a comment
There was a problem hiding this comment.
Re-reviewed exact head ca25d0d5b1be811e0724091b7963d0cbe747f1a1 against merge-base a73f76d109bd528ae2e53a4b8ab294422fb13e21, with special focus on the delta after my review of 3b0b74027bb6c3b4f8acff235c4715b82c95ccc3.
Both prior blockers are now closed.
- Normal cross-account lineage is DAG-aware:
creatorselects roots only; ancestor candidates are retained while any descendant is live, ALTER rows are excluded from this delete path, and the SQL repeatstable_deletedplus level guards. TheB -> A -> Ctest proves dropping A retains the ancestor edge/snapshots and dropping C later reclaims the complete normal chain. - The remaining ALTER lifecycle is now bounded.
doDropAccountinvokes the shared ownership-aware historical compactor in the same transaction after deleting the dropped account's user snapshots and PITR rows. No-source deleted ALTER generations and their deterministic protect snapshots are reclaimed; an external account snapshot retains the generation, and dropping that owner later releases it. This is the exact control requested in the previous review.
I rechecked transaction/context behavior: both caller transaction modes execute the scans/deletes as the system account; errors return through the existing rollback/short-circuit path; normal metadata and its snapshot are deleted as one transaction; the compactor retains live logical components, identity-mismatched edges, live rows missing identity snapshots, and components covered by user snapshot/PITR timestamps. DAG walks are cycle-bounded and memoized, so corrupt cycles do not hang and shared descendants do not cause quadratic subtree recomputation.
Performance cost is confined to the low-frequency synchronous DROP ACCOUNT path: currently it performs two full reads of global branch metadata under the same transaction lock plus the historical-source scans. That can increase account-drop latency for very large catalogs, but it avoids per-row queries, keeps memory O(lineage rows), and is required for atomic cross-account ownership decisions; it does not affect normal query/DML hot paths. No new goroutine, channel, file, or asynchronous cleanup ownership is introduced.
Exact-head CI is green, including Ubuntu UT, SCA, coverage, multi-CN proxy BVT, and multi-CN pessimistic BVT. The targeted three BVTs cover same-name account recreation, chained normal branches, ALTER cleanup, and external historical retention. I found no remaining blocking correctness, performance, lifecycle, or hang issue.
Merge Queue Status
This pull request spent 3 minutes 28 seconds in the queue, with no time running CI. ReasonThe pull request #27690 has been manually updated Requeued — the merge queue status continues in this comment ↓. |
XuPeng-SH
left a comment
There was a problem hiding this comment.
Incremental deep re-review completed at exact head 24a846528dfa3dce2fee3fe8323eee22289a1eb2 on current main 22d56a30b102b58050a31ff04e4f5187541c0f7b.
The rebased PR aggregate patch-id is identical to the fully reviewed head ca25d0d5b1be811e0724091b7963d0cbe747f1a1 (b6340f717ac73b616c90ac75b88eaa1d4926b4c1). There are no files changed by both the PR and the intervening main range, and git diff --check is clean. The prior DAG ownership, ALTER-generation compaction, external historical owner, correctness, catalog-scan performance, and cleanup unhappy-path conclusions therefore remain valid on this exact head. Per reviewer direction, I am not waiting for rerun CI.
Merge Queue Status
This pull request spent 1 hour 19 minutes 47 seconds in the queue, including 47 minutes 45 seconds running CI. Required conditions to merge
|
What type of PR is this?
Which issue(s) this PR fixes:
Related to #27660
What this PR does / why we need it:
Root cause
mo_catalog.mo_branch_metadataandmo_catalog.mo_feature_limitare physically global, predefined catalog tables. GenericDROP ACCOUNTcleanup only deletes cluster-table rows through their physicalaccount_idownership column. The affected rows are owned semantically bymo_branch_metadata.creatorandmo_feature_limit.account_id, so the original cleanup missed them and left permanent orphan metadata.For Branch metadata,
creatoridentifies the destination account that created an edge; it is not exclusive ownership of the edge's lineage. An unconditional delete bycreatorcan remove an ancestor edge and its protect snapshot while a live descendant account still depends on that edge.Changes
creatoronly to select the dropped account's roots, walks their ancestors, and reclaims a normal edge only when its complete descendant subtree is deleted.FOR UPDATE, retain deleted ancestor edges needed by live cross-account descendants, and reclaim their protect snapshots and metadata together when the final descendant account is dropped.table_deletedand normal-lineage predicates. DROP ACCOUNT invokes the existing ownership-aware historical-lineage compactor after removing the dropped account user snapshots and PITR rows, so ALTER generations are reclaimed only when no external historical source or live descendant needs them.doDropAccountunder the system-account context and the existing account-drop transaction, propagating errors through the existing rollback/finish path.B -> A -> CBVT. The chain uses ordinary publication/subscription setup followed by real Data Branch operations, drops A while C is live, verifies A's deleted edge and both protect snapshots remain, then drops C and verifies the complete chain and quotas are gone.Issue-to-test proof
drop_account_branch_metadata.sql/.resultproves the reported lifecycle: a real Branch operation creates one lineage row and oneBRANCHquota row, both semantic rows are gone afterDROP ACCOUNT, same-name recreation receives a new account ID, old IDs remain isolated, and the recreated account is cleaned too.drop_account_branch_metadata_chain.sql/.resultproves the review boundary: it creates and asserts aB -> A -> Clineage, checks that dropping A retains the deleted A edge and both snapshots while C is live, then checks that dropping C removes both chain metadata rows, both snapshots, all rows for both creators, and C's quota.TestComputeAccountBranchReclaimPlanproves that live descendants retain an ancestor edge, the final child drop reclaims the retained ancestor, and ALTER rows are excluded.Test_doDropAccount_InTransactionand the restore-path coverage prove both transaction modes execute the semantic scan/cleanup under the system account.Test_doDropAccount_AccountOwnedMetadataCleanupErrorproves a Branch metadata scan error is returned, the newly opened transaction is rolled back, and feature-limit cleanup is not attempted.Tests
.agents/skills/mo-dev/scripts/mo-cgo-test -count=1 ./pkg/frontend.agents/skills/mo-dev/scripts/mo-cgo-test -count=1 ./pkg/frontend/databranchutils.agents/skills/mo-dev/scripts/mo-cgo-test -count=1 -run 'TestBranchProtectSnapshot_(CrossAccount|CrossAccount_DropSourceFirst)' ./pkg/vm/engine/testgo vet ./pkg/frontend ./pkg/frontend/databranchutils ./pkg/vm/engine/testwith the repository CGo include/library pathsgolangci-lint run -c .golangci.yml ./pkg/frontend/databranchutilswith the repository CGo include/library paths (0 issues)make build(aftermake thirdpartiesandmake cgo)method=runfortest/distributed/cases/tenant/drop_account_branch_metadata.sql: 28/28 statements passed, 100% success ratemethod=runfortest/distributed/cases/tenant/drop_account_branch_metadata_chain.sql: 43/43 statements passed, 100% success ratemethod=runfortest/distributed/cases/tenant/drop_account_branch_metadata_alter.sql: 45/45 statements passed, 100% success rateResidual risks
Account drop now performs one full locked read of the global Branch metadata table and up to four semantic cleanup deletes in the existing synchronous transaction. The read is required to evaluate cross-account DAG dependencies atomically; it may add latency for catalogs with very large Branch metadata history. No asynchronous cleanup or fallback path is introduced. Local BVT execution used a direct CN endpoint because the standard multi-CN Docker image was unavailable in this environment; the committed cases remain in the distributed BVT suite and use separate tenant sessions.
Latest implementation update
DROP ACCOUNT now invokes the shared ownership-aware historical ALTER compactor after the dropped account user snapshots and PITR rows are removed. Deleted account-owned ALTER generations and their branch-protect snapshots are reclaimed in the same transaction when no external historical source or live descendant remains, while a live external historical source retains them.
Added
drop_account_branch_metadata_alter.sql/.result, covering a real cross-account copy-and-swap ALTER, no-external-owner cleanup, and an external account-snapshot control that retains the lineage across account drop and releases it when the owner snapshot is dropped.After rebasing onto
origin/mainat1d3483ac97with current PR headea9635da89, verification passed: rebase-focused DROP ACCOUNT frontend tests and cross-account Branch engine tests passed; prior fullpkg/frontend/..., CGo-configuredgo vet ./pkg/frontend/...,make build, and mo-tester 116/116 statements remain green.Freshness rebase update
After origin/main advanced to 22d56a3, the PR branch was rebased to 24a8465. Rebase-focused DROP ACCOUNT frontend tests and cross-account Branch engine tests passed again.
Freshness rebase update
After origin/main advanced to c1fe7b4, the PR branch was rebased to 6f550e5. Rebase-focused DROP ACCOUNT frontend tests and cross-account Branch engine tests passed again.
Freshness rebase update
After origin/main advanced to 43d361f, the PR branch was rebased to 225ae83. Rebase-focused DROP ACCOUNT frontend tests and cross-account Branch engine tests passed again.