From b867979a5dc109eb6a6f9e891e3dab3ab11a9a32 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 10 Dec 2024 13:40:57 +0100 Subject: [PATCH 1/6] Return different status for likely corupt indexes --- .../Factories/IndexPresentationFactory.cs | 94 +++++++++++++++++-- src/Umbraco.Cms.Api.Management/OpenApi.json | 21 +++-- .../ViewModels/Indexer/HealthStatus.cs | 1 + .../Examine/ExamineIndexRebuilder.cs | 16 +++- 4 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactory.cs index 0fae77078b..116e3f9743 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactory.cs @@ -1,6 +1,9 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Api.Management.ViewModels.Indexer; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.Services; @@ -11,16 +14,34 @@ public class IndexPresentationFactory : IIndexPresentationFactory private readonly IIndexDiagnosticsFactory _indexDiagnosticsFactory; private readonly IIndexRebuilder _indexRebuilder; private readonly IIndexingRebuilderService _indexingRebuilderService; + private readonly ILogger _logger; - public IndexPresentationFactory(IIndexDiagnosticsFactory indexDiagnosticsFactory, IIndexRebuilder indexRebuilder, IIndexingRebuilderService indexingRebuilderService) + public IndexPresentationFactory( + IIndexDiagnosticsFactory indexDiagnosticsFactory, + IIndexRebuilder indexRebuilder, + IIndexingRebuilderService indexingRebuilderService, + ILogger logger) { _indexDiagnosticsFactory = indexDiagnosticsFactory; _indexRebuilder = indexRebuilder; _indexingRebuilderService = indexingRebuilderService; + _logger = logger; + } + + [Obsolete("Use the non obsolete method instead. Scheduled for removal in v17")] + public IndexPresentationFactory(IIndexDiagnosticsFactory indexDiagnosticsFactory, IIndexRebuilder indexRebuilder, IIndexingRebuilderService indexingRebuilderService) + :this( + indexDiagnosticsFactory, + indexRebuilder, + indexingRebuilderService, + StaticServiceProvider.Instance.GetRequiredService>()) + { } public IndexResponseModel Create(IIndex index) { + var isCorrupt = !TryGetSearcherName(index, out var searcherName); + if (_indexingRebuilderService.IsRebuilding(index.Name)) { return new IndexResponseModel @@ -28,9 +49,9 @@ public class IndexPresentationFactory : IIndexPresentationFactory Name = index.Name, HealthStatus = new HealthStatusResponseModel { - Status = HealthStatus.Rebuilding, + Status = isCorrupt ? HealthStatus.Corrupt : HealthStatus.Rebuilding, }, - SearcherName = index.Searcher.Name, + SearcherName = searcherName, DocumentCount = 0, FieldCount = 0, }; @@ -55,21 +76,78 @@ public class IndexPresentationFactory : IIndexPresentationFactory } } + if (TryGetDocumentCount(indexDiag, index, out var documentCount) is false) + { + isCorrupt = true; + } + + if (TryGetFieldNameCount(indexDiag, index, out var fieldNameCount) is false) + { + isCorrupt = true; + } + var indexerModel = new IndexResponseModel { Name = index.Name, HealthStatus = new HealthStatusResponseModel { - Status = isHealthyAttempt.Success ? HealthStatus.Healthy : HealthStatus.Unhealthy, + Status = isCorrupt + ? HealthStatus.Corrupt + : isHealthyAttempt.Success ? HealthStatus.Healthy : HealthStatus.Unhealthy, Message = isHealthyAttempt.Result, }, - CanRebuild = _indexRebuilder.CanRebuild(index.Name), - SearcherName = index.Searcher.Name, - DocumentCount = indexDiag.GetDocumentCount(), - FieldCount = indexDiag.GetFieldNames().Count(), + CanRebuild = isCorrupt is false && _indexRebuilder.CanRebuild(index.Name), + SearcherName = searcherName, + DocumentCount = documentCount, + FieldCount = fieldNameCount, ProviderProperties = properties, }; return indexerModel; } + + private bool TryGetSearcherName(IIndex index, out string name) + { + try + { + name = index.Searcher.Name; + return true; + } + catch (Exception e) + { + _logger.LogError(e, "An error occured trying to get the searcher name of index {IndexName}", index.Name); + name = "Could not determine searcher name because of error."; + return false; + } + } + + private bool TryGetDocumentCount(IIndexDiagnostics indexDiag, IIndex index, out long documentCount) + { + try + { + documentCount = indexDiag.GetDocumentCount(); + return true; + } + catch (Exception e) + { + _logger.LogError(e, "An error occured trying to get the document count of index {IndexName}", index.Name); + documentCount = 0; + return false; + } + } + + private bool TryGetFieldNameCount(IIndexDiagnostics indexDiag, IIndex index, out int fieldNameCount) + { + try + { + fieldNameCount = indexDiag.GetFieldNames().Count(); + return true; + } + catch (Exception e) + { + _logger.LogError(e, "An error occured trying to get the field name count of index {IndexName}", index.Name); + fieldNameCount = 0; + return false; + } + } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 261279dc64..a6f3db0530 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -38339,6 +38339,7 @@ "enum": [ "Healthy", "Unhealthy", + "Corrupt", "Rebuilding" ], "type": "string" @@ -40758,7 +40759,8 @@ "format": "uuid" }, "packagePath": { - "type": "string" + "type": "string", + "readOnly": true } }, "additionalProperties": false @@ -42742,22 +42744,26 @@ { "$ref": "#/components/schemas/RelationReferenceModel" } - ] + ], + "readOnly": true }, "child": { "oneOf": [ { "$ref": "#/components/schemas/RelationReferenceModel" } - ] + ], + "readOnly": true }, "createDate": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true }, "comment": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true } }, "additionalProperties": false @@ -44939,7 +44945,8 @@ } }, "packagePath": { - "type": "string" + "type": "string", + "readOnly": true } }, "additionalProperties": false @@ -46093,4 +46100,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs index a71601e6e8..63fde8c4eb 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs @@ -4,5 +4,6 @@ public enum HealthStatus { Healthy, Unhealthy, + Corrupt, Rebuilding } diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs index 5fe98dca0b..03ab284ea4 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -187,8 +187,7 @@ public class ExamineIndexRebuilder : IIndexRebuilder { // If an index exists but it has zero docs we'll consider it empty and rebuild IIndex[] indexes = (onlyEmptyIndexes - ? _examineManager.Indexes.Where(x => - !x.IndexExists() || (x is IIndexStats stats && stats.GetDocumentCount() == 0)) + ? _examineManager.Indexes.Where(x => ShouldRebuild(x)) : _examineManager.Indexes).ToArray(); if (indexes.Length == 0) @@ -228,4 +227,17 @@ public class ExamineIndexRebuilder : IIndexRebuilder } } } + + private bool ShouldRebuild(IIndex index) + { + try + { + return !index.IndexExists() || (index is IIndexStats stats && stats.GetDocumentCount() == 0); + } + catch (Exception e) + { + _logger.LogError(e, "An error occured trying to get determine index shouldRebuild status for index {IndexName}. The index will be considered for rebuilding", index.Name); + return false; + } + } } From 73f7f4c0ba142a898b195a8aa63b3c76da663105 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 10 Dec 2024 13:42:39 +0100 Subject: [PATCH 2/6] Handle examine corrupt index status --- src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts | 2 ++ .../src/external/backend-api/src/types.gen.ts | 1 + .../views/section-view-examine-indexers.ts | 11 ++++++++++- .../views/section-view-examine-overview.ts | 3 ++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 3cd822caf0..0f54f27c9f 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -653,6 +653,8 @@ export default { 'The process is taking longer than expected, check the Umbraco log to see if there have been any errors during this operation', indexCannotRebuild: 'This index cannot be rebuilt because it has no assigned', iIndexPopulator: 'IIndexPopulator', + corruptStatus: 'Possible corrupt index detected', + corruptErrorDescription: 'Error received when evaluating the index:' }, placeholders: { username: 'Enter your username', diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts index 05a782015e..476aa305f9 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts @@ -1027,6 +1027,7 @@ export type HealthCheckWithResultPresentationModel = { export enum HealthStatusModel { HEALTHY = 'Healthy', UNHEALTHY = 'Unhealthy', + CORRUPT = 'Corrupt', REBUILDING = 'Rebuilding' } diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts index 41ad3d0126..2254f8a64e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts @@ -93,8 +93,12 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { switch (healthStatus.status) { case HealthStatusModel.HEALTHY: return html`${msg}`; + case HealthStatusModel.CORRUPT: + return html`
+ Possible corrupt index detected +

Error received when evaluating the index:

${msg}
`; case HealthStatusModel.UNHEALTHY: - return html`${msg}`; + return html`${msg}`; case HealthStatusModel.REBUILDING: return html`${msg}`; default: @@ -174,9 +178,14 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { css` #health-status { display: flex; + align-items: start; gap: var(--uui-size-6); } + #health-status umb-icon { + margin-top: var(--uui-size-1); + } + :host { display: block; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts index 0d0424c58c..a375374906 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts @@ -44,8 +44,9 @@ export class UmbDashboardExamineOverviewElement extends UmbLitElement { switch (status) { case HealthStatusModel.HEALTHY: return html``; + case HealthStatusModel.CORRUPT: case HealthStatusModel.UNHEALTHY: - return html``; + return html``; case HealthStatusModel.REBUILDING: return html``; default: From 7aa0ff09850996927c4ecdcd07370e1cde2f1ee5 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 10 Dec 2024 13:43:54 +0100 Subject: [PATCH 3/6] rebuilder bugfix --- src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs index 03ab284ea4..5191d400e6 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -236,7 +236,7 @@ public class ExamineIndexRebuilder : IIndexRebuilder } catch (Exception e) { - _logger.LogError(e, "An error occured trying to get determine index shouldRebuild status for index {IndexName}. The index will be considered for rebuilding", index.Name); + _logger.LogError(e, "An error occured trying to get determine index shouldRebuild status for index {IndexName}. The index will NOT be considered for rebuilding", index.Name); return false; } } From bb2fe14248aaf956e74bd399aa2278c41b0f7faf Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Wed, 11 Dec 2024 10:28:12 +0100 Subject: [PATCH 4/6] Added a link with more information regarding corrupt indexes --- .../views/section-view-examine-indexers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts index 2254f8a64e..0c16cd657d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts @@ -95,7 +95,7 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { return html`${msg}`; case HealthStatusModel.CORRUPT: return html`
- Possible corrupt index detected + Possible corrupt index detected

Error received when evaluating the index:

${msg}
`; case HealthStatusModel.UNHEALTHY: return html`${msg}`; From 2dbdaa1056b4c273f544b59bb922eecdc5518c6e Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Thu, 12 Dec 2024 10:03:04 +0100 Subject: [PATCH 5/6] Fix breaking change --- .../ViewModels/Indexer/HealthStatus.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs index 63fde8c4eb..cfb2ebb83f 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Indexer/HealthStatus.cs @@ -4,6 +4,6 @@ public enum HealthStatus { Healthy, Unhealthy, - Corrupt, - Rebuilding + Rebuilding, + Corrupt } From f8333502186f31fec2ba4db78b7f769194d9c74f Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Thu, 12 Dec 2024 11:06:43 +0100 Subject: [PATCH 6/6] Fix broken logger in tests --- .../Factories/IndexPresentationFactoryTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactoryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactoryTests.cs index e9c88b28df..87c5ae7b5d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactoryTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Factories/IndexPresentationFactoryTests.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using Examine; +using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Api.Management.Factories; @@ -51,7 +52,8 @@ public class IndexPresentationFactoryTests var factory = new IndexPresentationFactory( indexDiagnosticsFactoryMock.Object, indexRebuilderMock.Object, - indexRebuilderServiceMock.Object); + indexRebuilderServiceMock.Object, + Mock.Of>()); // act