[#12739] improvement(core): add OCC for user writes - #12744
Merged
Conversation
Every user write now carries the version it read. updateUserMeta, updateUserMetaByExternalId and softDeleteUserMetaByUserId compare current_version in the WHERE clause and report how many rows they matched, so a writer working from a stale snapshot loses instead of silently overwriting a newer row. The predicate is the primary key plus the version rather than the whole row. Comparing user_name, metalake_id, audit_info and last_version made the statement look safe while keying on values that move together, and it could not tell a lost update apart from a row renamed away. updateUserPOWithVersion now advances the version instead of reusing the last one, which is what makes the compare-and-set mean anything. The overwrite insert advances it as well rather than writing the value from the incoming PO, so a writer holding a pre-overwrite snapshot cannot pass a later compare-and-set (an ABA conflict). Creating a user takes a shared lock on the parent metalake row for the rest of the transaction, so a create cannot slip in beside a drop of the metalake it belongs to. The metalake's version is deliberately not compared, matching CatalogMetaService: holding the row is what makes the create safe, and an unrelated metalake edit committing in between would otherwise reject the create for no reason. A failed compare-and-set is classified by re-reading the row under a lock. A row that is gone, renamed, or moved to another metalake is reported as NoSuchEntityException; anything else is a concurrent modification. What counts as "renamed away" depends on how the caller addressed the user, which is what UserLookup records. Two behavior changes worth calling out: - updateUser no longer returns early when the role set is unchanged. The compare-and-set has to run so a stale caller is told, and a metadata-only change such as the audit info still has to be written. - deleteUser now fails when the row moved under it. deleteUserById still returns false when the row is already gone, since a delete with nothing left to delete is a no-op rather than an error.
yuqi1129
force-pushed
the
feat/12739-occ-user
branch
from
August 31, 2026 09:43
664b711 to
1e1944b
Compare
jerryshao
approved these changes
Aug 31, 2026
Code Coverage Report
Files |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes were proposed in this pull request?
Add version-CAS optimistic concurrency control and atomic service operations for managed users.
Why are the changes needed?
Concurrent user mutations could otherwise overwrite each other or leave partially updated relationship state.
Fix: #12739
Does this PR introduce any user-facing change?
Concurrent managed-user writes now report the existing optimistic-lock conflict response (HTTP 409). No API or configuration keys are changed.
How was this patch tested?
./gradlew :core:test --tests TestUserMetaService --tests TestAuthMappers --tests TestPOConverters -PskipITs -PskipDockerTests=true./gradlew :core:spotlessCheck :core:compileTestJavacurrentVersionandlastVersiondiffer.