diff --git a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImpl.java b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImpl.java index 6af530c8607..4f9bda5227f 100644 --- a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImpl.java @@ -1285,8 +1285,17 @@ private void logSwitchover(final IndiciesInfo oldInfo, final String luckyServer) } - public boolean delete(String indexName) { - return indexAPI.delete(indexName); + public boolean delete(final String indexName) { + final boolean deleted = indexAPI.delete(indexName); + if (deleted) { + try { + versionedIndicesAPI.removeByIndexName(indexName); + } catch (DotDataException e) { + Logger.error(this, "Index [" + indexName + "] was physically deleted but the " + + "indices DB row could not be removed: " + e.getMessage(), e); + } + } + return deleted; } public boolean optimize(List indexNames) { diff --git a/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactory.java b/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactory.java index 374b9f7f0de..efbc87764ba 100644 --- a/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactory.java +++ b/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactory.java @@ -127,4 +127,14 @@ public interface IndicesFactory { void insertIndexIfPresent(String indexName, String indexType, String version) throws DotDataException; + /** + * Removes the {@code indicies} table row for the given index name. + * Handles both plain and {@code os::}-prefixed forms of the name so that + * callers do not need to know how the name is stored in the database. + * + * @param indexName the physical index name (with or without vendor tag) + * @throws DotDataException if the name is blank or a SQL error occurs + */ + void removeByIndexName(String indexName) throws DotDataException; + } diff --git a/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactoryImpl.java b/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactoryImpl.java index 095e9ee932d..fd4609ca8e8 100644 --- a/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactoryImpl.java +++ b/dotCMS/src/main/java/com/dotcms/content/index/IndicesFactoryImpl.java @@ -43,6 +43,8 @@ public class IndicesFactoryImpl implements IndicesFactory { "SELECT COUNT(*) as count FROM indicies WHERE index_version = ? AND index_version IS NOT NULL"; private static final String COUNT_INDICES_BY_VERSION_SQL = "SELECT COUNT(*) as count FROM indicies WHERE index_version = ? AND index_version IS NOT NULL"; + private static final String DELETE_INDEX_BY_NAME_SQL = + "DELETE FROM indicies WHERE index_name = ? OR index_name = ?"; @Override public Optional loadIndices(String version) throws DotDataException { @@ -360,6 +362,24 @@ public void insertIndexIfPresent(String indexName, String indexType, String vers } } + @Override + public void removeByIndexName(final String indexName) throws DotDataException { + if (!UtilMethods.isSet(indexName)) { + throw new DotDataException("Index name cannot be null or empty"); + } + // Always work from the bare name so we can reliably compute both DB forms + final String bareName = IndexTag.strip(indexName); + final String taggedName = IndexTag.OS.tag(bareName); + try { + final DotConnect dotConnect = new DotConnect(); + final int deleted = dotConnect.executeUpdate(DELETE_INDEX_BY_NAME_SQL, bareName, taggedName); + Logger.info(this, "Removed " + deleted + " row(s) from indicies for index: " + bareName); + } catch (Exception e) { + Logger.error(this, "Failed to remove indicies row for index: " + bareName, e); + throw new DotDataException("Failed to remove indicies row for index: " + bareName, e); + } + } + /** * Builds a VersionedIndicesInfo from legacy non-versioned database results. * Expects results to have columns: index_name, index_type (no index_version) diff --git a/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPI.java b/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPI.java index ef80f3981ed..1fdd0d15294 100644 --- a/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPI.java +++ b/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPI.java @@ -104,6 +104,16 @@ public interface VersionedIndicesAPI { */ Optional loadDefaultVersionedIndices() throws DotDataException; + /** + * Removes the {@code indicies} table row for the given index name. + * Handles both plain and {@code os::}-prefixed forms of the name so that + * callers do not need to know how the name is stored in the database. + * + * @param indexName the physical index name (with or without vendor tag) + * @throws DotDataException if the name is blank or a SQL error occurs + */ + void removeByIndexName(String indexName) throws DotDataException; + /** * Clears all cached indices data. * This should be called when indices are modified outside of this API diff --git a/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPIImpl.java b/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPIImpl.java index bf80c606edc..ea1c14749fd 100644 --- a/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/content/index/VersionedIndicesAPIImpl.java @@ -226,6 +226,23 @@ public Optional loadDefaultVersionedIndices() throws DotDataEx return loadIndices(VersionedIndices.OPENSEARCH_3X); } + /** + * {@inheritDoc} + */ + @WrapInTransaction + @Override + public void removeByIndexName(final String indexName) throws DotDataException { + Logger.debug(this, "Removing indices row for index: " + indexName); + indicesFactory.removeByIndexName(indexName); + // Flush all index-related caches so no stale names survive the deletion: + // 1. VersionedIndicesCache — our own versioned-index cache + cache.clearCache(); + // 2. IndiciesCache — legacy (ES, non-versioned) index cache used by IndiciesFactory + CacheLocator.getIndiciesCache().clearCache(); + // 3. ESQueryCache — cached search queries that may reference the deleted index name + CacheLocator.getESQueryCache().clearCache(); + } + /** * {@inheritDoc} */ diff --git a/dotcms-integration/src/test/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImplTest.java b/dotcms-integration/src/test/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImplTest.java index b85eff8cbb9..ed1908fac0a 100644 --- a/dotcms-integration/src/test/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImplTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/content/elasticsearch/business/ContentletIndexAPIImplTest.java @@ -374,6 +374,15 @@ public void createContentIndexAndDelete() throws Exception { //Verify we just added two more indices assertEquals(oldIndices, newIndices); + + // Verify the indicies table has no orphan rows for the deleted indices + final DotConnect dc = new DotConnect(); + dc.setSQL("SELECT COUNT(*) AS cnt FROM indicies WHERE index_name = ? OR index_name = ?"); + dc.addParam(workingIndex); + dc.addParam(liveIndex); + final List> rows = dc.loadResults(); + assertEquals("indicies table must have no orphan rows after delete", + 0L, Long.parseLong(rows.get(0).get("cnt").toString())); } /**