diff --git a/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs b/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs index ff3bebd989..282d6c57fb 100644 --- a/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs @@ -9,10 +9,16 @@ namespace Umbraco.Cms.Core.Configuration.Models; public class IndexingSettings { private const bool StaticExplicitlyIndexEachNestedProperty = false; + private const int StaticBatchSize = 10000; /// /// Gets or sets a value for whether each nested property should have it's own indexed value. Requires a rebuild of indexes when changed. /// [DefaultValue(StaticExplicitlyIndexEachNestedProperty)] public bool ExplicitlyIndexEachNestedProperty { get; set; } = StaticExplicitlyIndexEachNestedProperty; + + /// + /// Gets or sets a value for how many items to index at a time. + /// + public int BatchSize { get; set; } = StaticBatchSize; } diff --git a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index eb9e9f135f..e912728fee 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -1,5 +1,9 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Services; @@ -20,11 +24,23 @@ public class ContentIndexPopulator : IndexPopulator private readonly bool _publishedValuesOnly; private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; + private IndexingSettings _indexingSettings; + /// /// This is a static query, it's parameters don't change so store statically /// private IQuery? _publishedQuery; + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] + public ContentIndexPopulator( + ILogger logger, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IContentValueSetBuilder contentValueSetBuilder) + : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder, StaticServiceProvider.Instance.GetRequiredService>()) + { + } + /// /// Default constructor to lookup all content data /// @@ -32,8 +48,21 @@ public class ContentIndexPopulator : IndexPopulator ILogger logger, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, - IContentValueSetBuilder contentValueSetBuilder) - : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) + IContentValueSetBuilder contentValueSetBuilder, + IOptionsMonitor indexingSettings) + : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder, indexingSettings) + { + } + + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] + public ContentIndexPopulator( + ILogger logger, + bool publishedValuesOnly, + int? parentId, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IValueSetBuilder contentValueSetBuilder) + : this(logger, publishedValuesOnly, parentId, contentService, umbracoDatabaseFactory, contentValueSetBuilder, StaticServiceProvider.Instance.GetRequiredService>()) { } @@ -46,7 +75,8 @@ public class ContentIndexPopulator : IndexPopulator int? parentId, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, - IValueSetBuilder contentValueSetBuilder) + IValueSetBuilder contentValueSetBuilder, + IOptionsMonitor indexingSettings) { _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _umbracoDatabaseFactory = umbracoDatabaseFactory ?? throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); @@ -54,6 +84,12 @@ public class ContentIndexPopulator : IndexPopulator _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; + _indexingSettings = indexingSettings.CurrentValue; + + indexingSettings.OnChange(change => + { + _indexingSettings = change; + }); } private IQuery PublishedQuery => _publishedQuery ??= @@ -75,7 +111,6 @@ public class ContentIndexPopulator : IndexPopulator return; } - const int pageSize = 10000; var pageIndex = 0; var contentParentId = -1; @@ -86,11 +121,11 @@ public class ContentIndexPopulator : IndexPopulator if (_publishedValuesOnly) { - IndexPublishedContent(contentParentId, pageIndex, pageSize, indexes); + IndexPublishedContent(contentParentId, pageIndex, _indexingSettings.BatchSize, indexes); } else { - IndexAllContent(contentParentId, pageIndex, pageSize, indexes); + IndexAllContent(contentParentId, pageIndex, _indexingSettings.BatchSize, indexes); } } diff --git a/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexHelper.cs b/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexHelper.cs index d7f2fc0c69..2747773e11 100644 --- a/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexHelper.cs +++ b/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexHelper.cs @@ -1,5 +1,7 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Services; @@ -14,22 +16,33 @@ internal sealed class DeliveryApiContentIndexHelper : IDeliveryApiContentIndexHe private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; private DeliveryApiSettings _deliveryApiSettings; + private IndexingSettings _indexingSettings; + + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] public DeliveryApiContentIndexHelper( IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IOptionsMonitor deliveryApiSettings) + : this(contentService, umbracoDatabaseFactory, deliveryApiSettings, StaticServiceProvider.Instance.GetRequiredService>()) + { + } + + public DeliveryApiContentIndexHelper( + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IOptionsMonitor deliveryApiSettings, + IOptionsMonitor indexingSettings) { _contentService = contentService; _umbracoDatabaseFactory = umbracoDatabaseFactory; _deliveryApiSettings = deliveryApiSettings.CurrentValue; + _indexingSettings = indexingSettings.CurrentValue; deliveryApiSettings.OnChange(settings => _deliveryApiSettings = settings); + indexingSettings.OnChange(settings => _indexingSettings = settings); } public void EnumerateApplicableDescendantsForContentIndex(int rootContentId, Action actionToPerform) - { - const int pageSize = 10000; - EnumerateApplicableDescendantsForContentIndex(rootContentId, actionToPerform, pageSize); - } + => EnumerateApplicableDescendantsForContentIndex(rootContentId, actionToPerform, _indexingSettings.BatchSize); internal void EnumerateApplicableDescendantsForContentIndex(int rootContentId, Action actionToPerform, int pageSize) { diff --git a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs index 6f4a4db4a3..b9872da5c8 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs @@ -1,5 +1,9 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -15,23 +19,43 @@ public class MediaIndexPopulator : IndexPopulator private readonly IValueSetBuilder _mediaValueSetBuilder; private readonly int? _parentId; + private IndexingSettings _indexingSettings; + + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] + public MediaIndexPopulator(ILogger logger, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + : this(logger, null, mediaService, mediaValueSetBuilder, StaticServiceProvider.Instance.GetRequiredService>()) + { + } + /// /// Default constructor to lookup all content data /// - public MediaIndexPopulator(ILogger logger, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) - : this(logger, null, mediaService, mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder, IOptionsMonitor indexingSettings) + : this(logger, null, mediaService, mediaValueSetBuilder, indexingSettings) + { + } + + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] + public MediaIndexPopulator(ILogger logger, int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + : this(logger, parentId, mediaService, mediaValueSetBuilder, StaticServiceProvider.Instance.GetRequiredService>()) { } /// /// Optional constructor allowing specifying custom query parameters /// - public MediaIndexPopulator(ILogger logger, int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder, IOptionsMonitor indexingSettings) { _logger = logger; _parentId = parentId; _mediaService = mediaService; _mediaValueSetBuilder = mediaValueSetBuilder; + _indexingSettings = indexingSettings.CurrentValue; + + indexingSettings.OnChange(change => + { + _indexingSettings = change; + }); } protected override void PopulateIndexes(IReadOnlyList indexes) @@ -46,7 +70,6 @@ public class MediaIndexPopulator : IndexPopulator return; } - const int pageSize = 10000; var pageIndex = 0; var mediaParentId = -1; @@ -60,7 +83,7 @@ public class MediaIndexPopulator : IndexPopulator do { - media = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out _).ToArray(); + media = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, _indexingSettings.BatchSize, out _).ToArray(); // ReSharper disable once PossibleMultipleEnumeration foreach (IIndex index in indexes) @@ -70,6 +93,6 @@ public class MediaIndexPopulator : IndexPopulator pageIndex++; } - while (media.Length == pageSize); + while (media.Length == _indexingSettings.BatchSize); } } diff --git a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs index 67d59d02d9..4f07a7283f 100644 --- a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs @@ -1,4 +1,6 @@ using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; @@ -15,6 +17,7 @@ namespace Umbraco.Cms.Infrastructure.Examine; /// public class PublishedContentIndexPopulator : ContentIndexPopulator { + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V19.")] public PublishedContentIndexPopulator( ILogger logger, IContentService contentService, @@ -23,4 +26,14 @@ public class PublishedContentIndexPopulator : ContentIndexPopulator : base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } + + public PublishedContentIndexPopulator( + ILogger logger, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IPublishedContentValueSetBuilder contentValueSetBuilder, + IOptionsMonitor indexingSettings) + : base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder, indexingSettings) + { + } } diff --git a/src/Umbraco.Infrastructure/Services/IndexingRebuilderService.cs b/src/Umbraco.Infrastructure/Services/IndexingRebuilderService.cs index 1263f52e5e..aaab2497ff 100644 --- a/src/Umbraco.Infrastructure/Services/IndexingRebuilderService.cs +++ b/src/Umbraco.Infrastructure/Services/IndexingRebuilderService.cs @@ -42,12 +42,6 @@ public class IndexingRebuilderService : IIndexingRebuilderService /// public async Task TryRebuildAsync(IIndex index, string indexName) { - // Remove it in case there's a handler there already - index.IndexOperationComplete -= Indexer_IndexOperationComplete; - - // Now add a single handler - index.IndexOperationComplete += Indexer_IndexOperationComplete; - try { Attempt attempt = await _indexRebuilder.RebuildIndexAsync(indexName); @@ -55,8 +49,6 @@ public class IndexingRebuilderService : IIndexingRebuilderService } catch (Exception exception) { - // Ensure it's not listening - index.IndexOperationComplete -= Indexer_IndexOperationComplete; _logger.LogError(exception, "An error occurred rebuilding index"); return false; } @@ -70,19 +62,4 @@ public class IndexingRebuilderService : IIndexingRebuilderService /// public Task IsRebuildingAsync(string indexName) => _indexRebuilder.IsRebuildingAsync(indexName); - - private void Indexer_IndexOperationComplete(object? sender, EventArgs e) - { - var indexer = (IIndex?)sender; - - _logger.LogDebug("Logging operation completed for index {IndexName}", indexer?.Name); - - if (indexer is not null) - { - //ensure it's not listening anymore - indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; - } - - _logger.LogInformation("Rebuilding index '{IndexerName}' done.", indexer?.Name); - } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index 1d7cf2e841..0b1f602907 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -39,6 +39,7 @@ public class IndexInitializer private readonly IContentTypeService _contentTypeService; private readonly IDocumentUrlService _documentUrlService; private readonly ILanguageService _languageService; + private readonly IOptionsMonitor _indexSettings; public IndexInitializer( IShortStringHelper shortStringHelper, @@ -50,7 +51,8 @@ public class IndexInitializer ILocalizationService localizationService, IContentTypeService contentTypeService, IDocumentUrlService documentUrlService, - ILanguageService languageService) + ILanguageService languageService, + IOptionsMonitor indexSettings) { _shortStringHelper = shortStringHelper; _propertyEditors = propertyEditors; @@ -62,6 +64,7 @@ public class IndexInitializer _contentTypeService = contentTypeService; _documentUrlService = documentUrlService; _languageService = languageService; + _indexSettings = indexSettings; } public ContentValueSetBuilder GetContentValueSetBuilder(bool publishedValuesOnly) @@ -91,7 +94,8 @@ public class IndexInitializer null, contentService, umbracoDatabaseFactory, - contentValueSetBuilder); + contentValueSetBuilder, + _indexSettings); return contentIndexDataSource; } @@ -105,7 +109,7 @@ public class IndexInitializer _shortStringHelper, _contentSettings, StaticServiceProvider.Instance.GetRequiredService()); - var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); + var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder, _indexSettings); return mediaIndexDataSource; }