Skip to content

[#12775] feat(core): Add OCC for tag metadata - #12781

Open
yuqi1129 wants to merge 4 commits into
apache:mainfrom
yuqi1129:feat/12775-occ-tag
Open

[#12775] feat(core): Add OCC for tag metadata#12781
yuqi1129 wants to merge 4 commits into
apache:mainfrom
yuqi1129:feat/12775-occ-tag

Conversation

@yuqi1129

@yuqi1129 yuqi1129 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

  • Apply stable-ID and expected-version CAS to tag alter and delete operations.
  • Replace database-specific tag overwrite upserts with locked, monotonic root updates that preserve the stored tag ID.
  • Lock parent metalakes during creation and tag roots during assignment changes.
  • Keep tag deletion and all dependent relationship cleanup, including policy-on-tag rows, atomic.
  • Add real two-transaction interleaving coverage for create/delete, overwrite/rename, and assignment/rename races.

Why are the changes needed?

Concurrent tag writes could otherwise cause lost updates, stale deletions, ID/version resets, or partial relationship cleanup.

Fix: #12775

Does this PR introduce any user-facing change?

No API or configuration change. Concurrent stale writes now fail deterministically; a tag deleted or renamed by a competing transaction preserves the existing idempotent delete result.

How was this patch tested?

  • ./gradlew :core:spotlessApply :core:compileTestJava :core:javadoc
  • ./gradlew :core:test --tests org.apache.gravitino.storage.relational.service.TestTagMetaService --tests org.apache.gravitino.storage.relational.service.TestPolicyTagRelService --tests org.apache.gravitino.tag.TestTagManager -PskipDockerTests=false
  • TestTagMetaService ran against H2, MySQL, and PostgreSQL.

Copilot AI lite review requested due to automatic review settings September 1, 2026 08:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Adds optimistic concurrency control (OCC) to tag metadata mutations to prevent lost updates and stale deletes, and ensures tag versions remain monotonic during overwrites.

Changes:

  • Introduce version-checked (stable-ID + expected-version) update/delete paths for tags and dependent relationship cleanup.
  • Ensure tag overwrite increments the stored OCC sequence (monotonic current/last versions).
  • Add row-level locking for metalake/tag roots during tag creation and tag-assignment changes, plus new OCC-focused tests.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
core/src/test/java/org/apache/gravitino/storage/relational/service/TestTagMetaService.java Adds tests covering monotonic versions and optimistic-lock failure behavior for tag alter/delete.
core/src/main/java/org/apache/gravitino/storage/relational/utils/POConverters.java Updates tag PO version bump logic to increment from current version.
core/src/main/java/org/apache/gravitino/storage/relational/service/TagMetaService.java Applies OCC + locking for create/update/delete and tag-assignment flows; adds relationship cleanup by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/TagMetadataObjectRelPostgreSQLProvider.java Adds Postgres-specific SQL for soft-deleting tag-object relations by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/TagMetaPostgreSQLProvider.java Switches tag soft-delete to (tag_id, current_version) and enforces monotonic overwrite versioning in Postgres.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TagMetadataObjectRelBaseSQLProvider.java Adds base SQL for soft-deleting tag-object relations by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/TagMetaBaseSQLProvider.java Updates OCC semantics for update/delete SQL and includes allowed_values in select projections.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/PolicyTagRelBaseSQLProvider.java Adds SQL to soft-delete policy-tag relations by tag ID during tag deletion.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/TagMetadataObjectRelSQLProviderFactory.java Exposes provider factory method for deleting tag-object relations by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/TagMetadataObjectRelMapper.java Adds mapper API to soft-delete tag-object relations by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/TagMetaSQLProviderFactory.java Renames/rewires soft-delete SQL to version-checked delete by (tag_id, current_version).
core/src/main/java/org/apache/gravitino/storage/relational/mapper/TagMetaMapper.java Adds mapper API for version-checked soft delete by tag ID + observed version.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/PolicyTagRelSQLProviderFactory.java Exposes provider factory method for deleting policy-tag relations by tag ID.
core/src/main/java/org/apache/gravitino/storage/relational/mapper/PolicyTagRelMapper.java Adds mapper API to soft-delete policy-tag relations by tag ID.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Code Coverage Report

Overall Project 68.89% +0.42% 🟢
Files changed 90.03% 🟢

Module Coverage
aliyun 19.74% 🔴
api 51.62% 🟢
authorization-common 85.96% 🟢
authorization-ranger 4.38% 🔴
aws 53.54% 🟢
azure 32.1% 🔴
catalog-common 19.1% 🔴
catalog-fileset 80.3% 🟢
catalog-glue 69.24% 🟢
catalog-hive 82.96% 🟢
catalog-jdbc-common 45.69% 🟢
catalog-jdbc-doris 82.69% 🟢
catalog-jdbc-mysql 79.33% 🟢
catalog-jdbc-postgresql 83.39% 🟢
catalog-jdbc-starrocks 79.16% 🟢
catalog-kafka 76.99% 🟢
catalog-lakehouse-generic 60.55% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 85.86% 🟢
catalog-lakehouse-paimon 84.26% 🟢
catalog-model 77.99% 🟢
cli 44.51% 🟢
client-java 77.37% 🟢
common 56.34% 🟢
core 83.88% +0.43% 🟢
filesystem-hadoop3 76.45% 🟢
flink 0.0% 🔴
flink-common 52.1% 🟢
flink-runtime 0.0% 🔴
gcp 32.2% 🔴
hadoop-auth 68.0% 🟢
hadoop-common 17.84% 🔴
hive-metastore-common 53.4% 🟢
iceberg-aliyun-bundle 0.0% 🔴
iceberg-common 64.75% 🟢
iceberg-rest-server 75.96% 🟢
idp-basic 86.42% 🟢
integration-test-common 0.0% 🔴
jobs 62.92% 🟢
lance-common 32.52% 🔴
lance-rest-server 65.69% 🟢
lineage 53.02% 🟢
optimizer 83.24% 🟢
optimizer-api 21.95% 🔴
server 88.47% 🟢
server-common 80.67% 🟢
spark 28.57% 🔴
spark-common 48.92% 🟢
tencent 81.78% 🟢
trino-connector 51.26% 🟢
Files
Module File Coverage
core PolicyTagRelBaseSQLProvider.java 100.0% 🟢
TagMetaPostgreSQLProvider.java 100.0% 🟢
TagMetaService.java 97.56% 🟢
PolicyTagRelService.java 94.51% 🟢
PolicyTagRelSQLProviderFactory.java 94.44% 🟢
TagMetaBaseSQLProvider.java 94.12% 🟢
TagMetadataObjectRelBaseSQLProvider.java 93.75% 🟢
TagMetadataObjectRelSQLProviderFactory.java 93.55% 🟢
TagMetaSQLProviderFactory.java 92.59% 🟢
TagMetadataObjectRelPostgreSQLProvider.java 91.67% 🟢
POConverters.java 88.18% 🟢
TagManager.java 83.84% 🟢
PolicyTagRelMapper.java 0.0% 🔴
TagMetaMapper.java 0.0% 🔴
TagMetadataObjectRelMapper.java 0.0% 🔴

@jerryshao jerryshao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 10 findings below, most severe first. This PR is notably cleaner than the companion Model-OCC PR (#12649): I specifically checked for the same TOCTOU alias-race bug class found there and it's absent here — this PR correctly uses the shared OccWriteSupport helper (from #12639) for its single-entity locking paths. The remaining findings are an undocumented cascade-delete safety gap, several missing tests for new failure branches, one real reuse gap (a hand-rolled duplicate of OccWriteSupport), a CLAUDE.md convention violation, and an observability regression.

- lockTagsForAssignment goes through OccWriteSupport.lockParentForChildWrite
  instead of repeating the null-and-identity check inline, so the three tag
  OCC paths classify a vanished row the same way.
- Documented why the assignment change runs in one transaction, why the
  metalake row is locked in share mode without comparing its version, and
  why the tag rows are locked in ID order.
- lockMetalakeForTagCreate is private; the deleteTag overload that takes an
  observed row is marked @VisibleForTesting, which is the only reason it is
  not private.
- Added coverage for parent-metalake fencing on create and for allowed
  values surviving an overwrite and an alter, which the widened select
  lists now carry.

Claude-Session: https://claude.ai/code/session_01H8XCY74Bmjbcr618EKxNbA
- Tag rows are locked by one statement (listTagPOsByTagIdsForUpdate, ordered
  by tag ID) instead of one SELECT ... FOR UPDATE per tag, so an assignment
  change touching N tags costs one round trip while keeping the ID lock
  order that avoids deadlocks.
- PolicyTagRelService.lockTag routes through the same helper, so the three
  copies of "lock a tag row and check its identity" are now one.
- The package-private deleteTag overload moved below the public methods, as
  the member ordering in CLAUDE.md asks.
- Documented that the version-checked delete has to stay first in deleteTag
  and why the cascade cleanups that follow are not row-count checked.
- TagManager logs a concurrent alter or delete with the tag and metalake
  before rethrowing, which the old catch (IOException) used to do.
- Added coverage for the cascade cleanup a tag delete performs (assignments,
  policy-tag relations, policies applied to the tag, its owner relation and
  the securable objects that name it) and for a delete of an already deleted
  tag being reported as missing rather than as a conflict.

Claude-Session: https://claude.ai/code/session_01H8XCY74Bmjbcr618EKxNbA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Subtask] Implement version-CAS OCC for tag

3 participants