fix(search): guard active-index deletion and gate ES/OS delete cascade (#35640)#36399
Open
fabrizzio-dotCMS wants to merge 2 commits into
Open
fix(search): guard active-index deletion and gate ES/OS delete cascade (#35640)#36399fabrizzio-dotCMS wants to merge 2 commits into
fabrizzio-dotCMS wants to merge 2 commits into
Conversation
5 tasks
|
Tick the box to add this pull request to the merge queue (same as
|
Contributor
|
Claude finished @fabrizzio-dotCMS's task in 3m 4s —— View job I'll analyze this and get back to you. |
Contributor
🤖 dotBot Review (Bedrock)Reviewed 4 file(s); 3 candidate(s) → 2 confirmed, 0 uncertain (unverified, kept for review). Confirmed findings
us.deepseek.r1-v1:0 · Run: #28604656874 · tokens: in: 30329 · out: 10353 · total: 40682 · calls: 11 · est. ~$0.097 |
#35640) Index delete had two defects surfaced by QA (epic #35476, TC-016..020): TC-018 — deleting the currently active index was possible via the REST/AJAX endpoint. The maintenance UI only hides the Delete option for active indices (a client-side guard), so a direct DELETE /api/v1/esindex/{name} bypassed it and could leave the site with zero indices. Add a server-side guard in ContentletIndexAPIImpl.delete(): reject deletion of any index reported active or building by the phase-aware getCurrentIndex()/getNewIndex() (the same sources the UI uses), via DotStateException. ESIndexResource maps it to HTTP 400. The guard is fail-closed and can be bypassed with FEATURE_FLAG_ALLOW_ACTIVE_INDEX_DELETE. Guard reuses the phase-aware getters rather than VersionedIndices directly because VersionedIndices is empty in Phase 0 (rows are only written once migration starts), which would leave the most common prod state unprotected. TC-016 — delete cascades to the .os twin (phase-dispatched, since #35820). Make this an explicit, documented default gated by FEATURE_FLAG_INDEX_DELETE_CASCADE (default true). When off, delete is tag-dispatched (name ending in .os -> OS, otherwise ES) so only the named engine's index is removed and the twin is left intact — the ticket's documented no-cascade behavior. TC-016/017 cleanups in ESIndexResource: normalize the incoming name with removeClusterIdFromName so the endpoint accepts both the short name and the full physical name (with cluster prefix) instead of 404-ing on the latter; return a readable 404 body instead of an empty response; rename the inverted-sense indexExists helper to indexDoesNotExist. Tests: ContentletIndexAPIImplTest#delete_activeIndex_isRejected_unlessFeatureFlagOverrides and ContentletIndexAPIImplMigrationIntegrationTest#test_delete_phase1_cascadeOff_removesOnlyNamedEngine. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…5640) Apply the same removeClusterIdFromName normalization to the modIndex (activate/deactivate/clear/open/close) endpoint that deleteIndex already uses, so both accept the short name and the full physical name (with cluster prefix) instead of 404-ing on the latter. Addresses the AI review consistency finding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
73fa8a3 to
3311769
Compare
| // latter (issue #35640, TC-016). | ||
| final String resolvedName = indexAPI.removeClusterIdFromName(indexName); | ||
|
|
||
| if(indexDoesNotExist(resolvedName) ){ |
Contributor
There was a problem hiding this comment.
🟠 [High] 404 response in modIndex lacks error message body
The modIndex method throws NotFoundInDbException which returns an empty 404 response body, while deleteIndex uses ResponseUtil.mapErrorResponse() to return structured JSON errors. This inconsistency breaks client error handling expectations.
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.
Problem
QA on #35640 (epic #35476, TC-016–020) surfaced defects in the index delete path (
DELETE /api/v1/esindex/{name}→ContentletIndexAPIImpl.delete()):.ostwin — undocumented and not configurable.indexExistsreturnedtruewhen the index did not exist).Changes
TC-018 — active-index guard (
ContentletIndexAPIImpl.delete)getCurrentIndex()∪getNewIndex()— the same sources the maintenance UI uses to flag active/building — viaDotStateException, mapped to HTTP 400 inESIndexResource.FEATURE_FLAG_ALLOW_ACTIVE_INDEX_DELETE(defaultfalse).VersionedIndicesdirectly, becauseVersionedIndicesis empty in Phase 0 (rows are only written once migration starts), which would leave the most common prod state unprotected.TC-016 — cascade feature flag (
ContentletIndexAPIImpl.delete)FEATURE_FLAG_INDEX_DELETE_CASCADE(defaulttrue).router.writeProviders()— deletes the index and its twin (current [DEFECT] Confusing empty OS index appears in indices portlet on Phase 1 migration bootstrap #35820 behavior, no orphan)..os→ OpenSearch, otherwise Elasticsearch) — deletes only the named engine's index, leaving the twin intact (the documented no-cascade behavior).TC-016/017 — cleanups (
ESIndexResource)removeClusterIdFromNamein bothdeleteIndexandmodIndex, so the endpoints accept both the short name and the full physical name (no more false 404).indexExists→indexDoesNotExist(matches its actual meaning).Testing
ContentletIndexAPIImplTest#delete_activeIndex_isRejected_unlessFeatureFlagOverrides— active index survives a rejected delete; FF bypass then allows it.ContentletIndexAPIImplMigrationIntegrationTest#test_delete_phase1_cascadeOff_removesOnlyNamedEngine— Phase 1, cascade OFF: bare name deletes ES only,.ostwin survives (sibling to the existing cascade-ON test).openapi.yamldiff.Out of scope (follow-ups)
🤖 Generated with Claude Code