Skip to content

Commit 18ef102

Browse files
fix(index): clean up indicies DB rows on all physical delete paths — Fixes #35306
Three code paths physically deleted indices without removing the corresponding row from the `indicies` table, leaving stale DB entries that could cause startup failures or silent inconsistencies: - `ContentletIndexAPIImpl.delete()`: now calls `versionedIndicesAPI.removeByIndexName()` after a successful physical delete (covers the REST endpoint `DELETE /api/v1/esindex/{name}`). - `IndexAjaxAction.deleteIndex()`: was bypassing `ContentletIndexAPI` and going straight to `ESIndexAPI.delete()`. Routed through `ContentletIndexAPI.delete()` so DB cleanup is included. - `DeleteInactiveLiveWorkingIndicesJob`: `ESIndexAPI .deleteInactiveLiveWorkingIndices()` now returns the list of physically deleted index names (`List<String>` instead of `void`). The job iterates that list and calls `VersionedIndicesAPI.removeByIndexName()` for each entry. `IndexAPI`, `IndexAPIImpl`, and `OSIndexAPIImpl` updated accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fc12f6b commit 18ef102

12 files changed

Lines changed: 99 additions & 9 deletions

File tree

dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,17 @@ private void logSwitchover(final IndiciesInfo oldInfo, final String luckyServer)
12851285

12861286
}
12871287

1288-
public boolean delete(String indexName) {
1289-
return indexAPI.delete(indexName);
1288+
public boolean delete(final String indexName) {
1289+
final boolean deleted = indexAPI.delete(indexName);
1290+
if (deleted) {
1291+
try {
1292+
versionedIndicesAPI.removeByIndexName(indexName);
1293+
} catch (DotDataException e) {
1294+
Logger.error(this, "Index [" + indexName + "] was physically deleted but the " +
1295+
"indices DB row could not be removed: " + e.getMessage(), e);
1296+
}
1297+
}
1298+
return deleted;
12901299
}
12911300

12921301
public boolean optimize(List<String> indexNames) {

dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESIndexAPI.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ public boolean deleteMultiple(String...indexNames) {
276276
* @param inactiveLiveWorkingSetsToKeep indicates how many live/working sets to keep
277277
*/
278278

279-
public void deleteInactiveLiveWorkingIndices(final int inactiveLiveWorkingSetsToKeep) {
279+
@Override
280+
public List<String> deleteInactiveLiveWorkingIndices(final int inactiveLiveWorkingSetsToKeep) {
280281
// get list of indices ordered by created desc and in sets of live/working
281282
List<String> indices = getLiveWorkingIndicesSortedByCreationDateDesc();
282283

@@ -309,6 +310,7 @@ public void deleteInactiveLiveWorkingIndices(final int inactiveLiveWorkingSetsTo
309310
+ String.join(",", indicesToRemove));
310311
}
311312

313+
return List.copyOf(indicesToRemove);
312314
}
313315

314316
private void deleteLiveWorkingSetFromList(List<String> indicesToRemove, String index, String indexTimestamp) {

dotCMS/src/main/java/com/dotcms/content/index/IndexAPI.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public String getStatus() {
104104
*
105105
* @param inactiveLiveWorkingSetsToKeep number of live/working sets to keep
106106
*/
107-
void deleteInactiveLiveWorkingIndices(int inactiveLiveWorkingSetsToKeep);
107+
List<String> deleteInactiveLiveWorkingIndices(int inactiveLiveWorkingSetsToKeep);
108108

109109
/**
110110
* Returns a set of all managed indices.

dotCMS/src/main/java/com/dotcms/content/index/IndexAPIImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,8 @@ public boolean deleteMultiple(final String... indexNames) {
341341
}
342342

343343
@Override
344-
public void deleteInactiveLiveWorkingIndices(final int inactiveLiveWorkingSetsToKeep) {
345-
router.write(impl -> impl.deleteInactiveLiveWorkingIndices(inactiveLiveWorkingSetsToKeep));
344+
public List<String> deleteInactiveLiveWorkingIndices(final int inactiveLiveWorkingSetsToKeep) {
345+
return router.writeReturning(impl -> impl.deleteInactiveLiveWorkingIndices(inactiveLiveWorkingSetsToKeep));
346346
}
347347

348348
@Override

dotCMS/src/main/java/com/dotcms/content/index/IndicesFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,14 @@ public interface IndicesFactory {
127127
void insertIndexIfPresent(String indexName, String indexType,
128128
String version) throws DotDataException;
129129

130+
/**
131+
* Removes the {@code indicies} table row for the given index name.
132+
* Handles both plain and {@code os::}-prefixed forms of the name so that
133+
* callers do not need to know how the name is stored in the database.
134+
*
135+
* @param indexName the physical index name (with or without vendor tag)
136+
* @throws DotDataException if the name is blank or a SQL error occurs
137+
*/
138+
void removeByIndexName(String indexName) throws DotDataException;
139+
130140
}

dotCMS/src/main/java/com/dotcms/content/index/IndicesFactoryImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class IndicesFactoryImpl implements IndicesFactory {
4343
"SELECT COUNT(*) as count FROM indicies WHERE index_version = ? AND index_version IS NOT NULL";
4444
private static final String COUNT_INDICES_BY_VERSION_SQL =
4545
"SELECT COUNT(*) as count FROM indicies WHERE index_version = ? AND index_version IS NOT NULL";
46+
private static final String DELETE_INDEX_BY_NAME_SQL =
47+
"DELETE FROM indicies WHERE index_name = ? OR index_name = ?";
4648

4749
@Override
4850
public Optional<VersionedIndices> loadIndices(String version) throws DotDataException {
@@ -360,6 +362,24 @@ public void insertIndexIfPresent(String indexName, String indexType, String vers
360362
}
361363
}
362364

365+
@Override
366+
public void removeByIndexName(final String indexName) throws DotDataException {
367+
if (!UtilMethods.isSet(indexName)) {
368+
throw new DotDataException("Index name cannot be null or empty");
369+
}
370+
// Always work from the bare name so we can reliably compute both DB forms
371+
final String bareName = IndexTag.strip(indexName);
372+
final String taggedName = IndexTag.OS.tag(bareName);
373+
try {
374+
final DotConnect dotConnect = new DotConnect();
375+
final int deleted = dotConnect.executeUpdate(DELETE_INDEX_BY_NAME_SQL, bareName, taggedName);
376+
Logger.info(this, "Removed " + deleted + " row(s) from indicies for index: " + bareName);
377+
} catch (Exception e) {
378+
Logger.error(this, "Failed to remove indicies row for index: " + bareName, e);
379+
throw new DotDataException("Failed to remove indicies row for index: " + bareName, e);
380+
}
381+
}
382+
363383
/**
364384
* Builds a VersionedIndicesInfo from legacy non-versioned database results.
365385
* Expects results to have columns: index_name, index_type (no index_version)

dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPI.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ public interface VersionedIndicesAPI {
104104
*/
105105
Optional<VersionedIndices> loadDefaultVersionedIndices() throws DotDataException;
106106

107+
/**
108+
* Removes the {@code indicies} table row for the given index name.
109+
* Handles both plain and {@code os::}-prefixed forms of the name so that
110+
* callers do not need to know how the name is stored in the database.
111+
*
112+
* @param indexName the physical index name (with or without vendor tag)
113+
* @throws DotDataException if the name is blank or a SQL error occurs
114+
*/
115+
void removeByIndexName(String indexName) throws DotDataException;
116+
107117
/**
108118
* Clears all cached indices data.
109119
* This should be called when indices are modified outside of this API

dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPIImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,23 @@ public Optional<VersionedIndices> loadDefaultVersionedIndices() throws DotDataEx
226226
return loadIndices(VersionedIndices.OPENSEARCH_3X);
227227
}
228228

229+
/**
230+
* {@inheritDoc}
231+
*/
232+
@WrapInTransaction
233+
@Override
234+
public void removeByIndexName(final String indexName) throws DotDataException {
235+
Logger.debug(this, "Removing indices row for index: " + indexName);
236+
indicesFactory.removeByIndexName(indexName);
237+
// Flush all index-related caches so no stale names survive the deletion:
238+
// 1. VersionedIndicesCache — our own versioned-index cache
239+
cache.clearCache();
240+
// 2. IndiciesCache — legacy (ES, non-versioned) index cache used by IndiciesFactory
241+
CacheLocator.getIndiciesCache().clearCache();
242+
// 3. ESQueryCache — cached search queries that may reference the deleted index name
243+
CacheLocator.getESQueryCache().clearCache();
244+
}
245+
229246
/**
230247
* {@inheritDoc}
231248
*/

dotCMS/src/main/java/com/dotcms/content/index/opensearch/OSIndexAPIImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,10 @@ public void updateReplicas(String indexName, int replicas) throws DotDataExcepti
441441
}
442442

443443
@Override
444-
public void deleteInactiveLiveWorkingIndices(int inactiveLiveWorkingSetsToKeep) {
444+
public List<String> deleteInactiveLiveWorkingIndices(int inactiveLiveWorkingSetsToKeep) {
445445
Logger.info(this.getClass(),
446446
"deleteInactiveLiveWorkingIndices not yet implemented for OpenSearch");
447+
return List.of();
447448
}
448449

449450
// =========================================================================

dotCMS/src/main/java/com/dotmarketing/portlets/cmsmaintenance/ajax/IndexAjaxAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public void deleteIndex(HttpServletRequest request, HttpServletResponse response
143143
Map<String, String> map = getURIParams();
144144
String indexName = indexHelper.getIndexNameOrAlias(map,"indexName","indexAlias",this.indexAPI);
145145
if(UtilMethods.isSet(indexName))
146-
APILocator.getESIndexAPI().delete(indexName);
146+
APILocator.getContentletIndexAPI().delete(indexName);
147147
}
148148

149149
public void activateIndex(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, DotDataException {

0 commit comments

Comments
 (0)