fix(unique-fields): remove @CloseDBIfOpened to restore atomicity with contentlet save#35567
Conversation
… contentlet save @CloseDBIfOpened on innerValidate() forced the INSERT into unique_fields to run on a separate JDBC connection with its own committed transaction. If the outer contentlet transaction was later rolled back, the unique_fields row was already committed and became a permanent orphan — causing duplicate-key failures on the next startup or save attempt with no corresponding contentlet. Removing @CloseDBIfOpened lets @WrapInTransaction join the caller's existing transaction. The unique_fields INSERT and the contentlet save are now atomic: a rollback of one rolls back the other. Fixes #35566 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @fabrizzio-dotCMS's task in 56s —— View job Review
The fix is correct. No test for the rollback scenario. The entire justification for this change is that a rolled-back outer transaction should also roll back the
Summary: The code change is correct and the analysis in the PR description is accurate. The main gap is the missing integration test for rollback atomicity — the entire premise of the fix — which leaves the behavior unverified by CI. |
Problem
Closes #35566
DBUniqueFieldValidationStrategy.innerValidate()was annotated with both@WrapInTransactionand@CloseDBIfOpened. The combination has a critical side-effect:@CloseDBIfOpenedcloses any existing JDBC connection and opens a fresh one, isolated from the caller's transaction.@WrapInTransactionthen starts and commits a new transaction on that fresh connection.This means the
INSERT INTO unique_fieldscommits independently of the outer contentlet save transaction. If the outer transaction is later rolled back (e.g., a failure mid-save, or a startup migration task that rolls back), theunique_fieldsrow persists as a permanent orphan — a hash pointing to a contentlet that no longer exists.On the next contentlet save or migration run,
findVariable()returns empty (no contentlet exists), butpublish()/save()hits a duplicate-key violation onunique_fields_pkey, incorrectly blocking a valid operation.Fix
Remove
@CloseDBIfOpenedfrominnerValidate()andinnerValidateInPreview().With only
@WrapInTransaction, the interceptor joins the caller's existing transaction if one is active. Theunique_fieldsINSERT and the contentlet save are now atomic — a rollback of the outer transaction rolls back both.When no outer transaction is present,
@WrapInTransactionstarts its own, preserving the existing behavior.Impact
unique_fieldsrows from being created on any contentlet save rollback.Files changed
dotCMS/src/main/java/com/dotcms/contenttype/business/uniquefields/extratable/DBUniqueFieldValidationStrategy.java@CloseDBIfOpenedfrominnerValidate()andinnerValidateInPreview()import com.dotcms.business.CloseDBIfOpenedTest plan
unique_fieldsrow is insertedunique_fieldsrow is also absent after rollbackDBUniqueFieldValidationStrategyintegration tests — no regressions🤖 Generated with Claude Code