Convert DeletionService to use IScopeProvider (#3721)

Since the DeletionService is used by the `$bulk-delete` operation, the scope of `IFhirDataStore` and `ISearchService` need to be managed manually - not by the root container. Using `IScopeProvider` enables us to manage and dispose correctly - fixing the bug of leaking memory due to growth of services tracked by the root container.
This commit is contained in:
Mikael Weaver 2024-02-21 11:45:06 -08:00 коммит произвёл GitHub
Родитель 2720e1aa8c
Коммит 12c8365db8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 9 добавлений и 7 удалений

Просмотреть файл

@ -120,7 +120,7 @@ namespace Microsoft.Health.Fhir.Core.UnitTests.Features.Resources
var auditLogger = Substitute.For<IAuditLogger>();
var logger = Substitute.For<ILogger<DeletionService>>();
var deleter = new DeletionService(_resourceWrapperFactory, lazyConformanceProvider, _fhirDataStore.CreateMockScopeFactory(), _searchService.CreateMockScopeFactory(), _resourceIdProvider, contextAccessor, auditLogger, logger);
var deleter = new DeletionService(_resourceWrapperFactory, lazyConformanceProvider, _fhirDataStore.CreateMockScopeProvider(), _searchService.CreateMockScopeProvider(), _resourceIdProvider, contextAccessor, auditLogger, logger);
var conditionalCreateLogger = Substitute.For<ILogger<ConditionalCreateResourceHandler>>();
var conditionalUpsertLogger = Substitute.For<ILogger<ConditionalUpsertResourceHandler>>();

Просмотреть файл

@ -26,6 +26,7 @@ using Microsoft.Health.Fhir.Core.Extensions;
using Microsoft.Health.Fhir.Core.Features.Audit;
using Microsoft.Health.Fhir.Core.Features.Conformance;
using Microsoft.Health.Fhir.Core.Features.Context;
using Microsoft.Health.Fhir.Core.Features.Operations;
using Microsoft.Health.Fhir.Core.Features.Search;
using Microsoft.Health.Fhir.Core.Messages.Delete;
using Newtonsoft.Json.Linq;
@ -39,8 +40,8 @@ namespace Microsoft.Health.Fhir.Core.Features.Persistence
{
private readonly IResourceWrapperFactory _resourceWrapperFactory;
private readonly Lazy<IConformanceProvider> _conformanceProvider;
private readonly Func<IScoped<IFhirDataStore>> _fhirDataStoreFactory;
private readonly Func<IScoped<ISearchService>> _searchServiceFactory;
private readonly IScopeProvider<IFhirDataStore> _fhirDataStoreFactory;
private readonly IScopeProvider<ISearchService> _searchServiceFactory;
private readonly ResourceIdProvider _resourceIdProvider;
private readonly AsyncRetryPolicy _retryPolicy;
private readonly FhirRequestContextAccessor _contextAccessor;
@ -53,8 +54,8 @@ namespace Microsoft.Health.Fhir.Core.Features.Persistence
public DeletionService(
IResourceWrapperFactory resourceWrapperFactory,
Lazy<IConformanceProvider> conformanceProvider,
Func<IScoped<IFhirDataStore>> fhirDataStoreFactory,
Func<IScoped<ISearchService>> searchServiceFactory,
IScopeProvider<IFhirDataStore> fhirDataStoreFactory,
IScopeProvider<ISearchService> searchServiceFactory,
ResourceIdProvider resourceIdProvider,
FhirRequestContextAccessor contextAccessor,
IAuditLogger auditLogger,
@ -274,10 +275,11 @@ namespace Microsoft.Health.Fhir.Core.Features.Persistence
var parallelBag = new ConcurrentBag<string>();
try
{
using var fhirDataStore = _fhirDataStoreFactory.Invoke();
// This throws AggrigateExceptions
await Parallel.ForEachAsync(resourcesToDelete, cancellationToken, async (item, innerCt) =>
{
using var fhirDataStore = _fhirDataStoreFactory.Invoke();
await _retryPolicy.ExecuteAsync(async () => await fhirDataStore.Value.HardDeleteAsync(new ResourceKey(item.Resource.ResourceTypeName, item.Resource.ResourceId), request.DeleteOperation == DeleteOperation.PurgeHistory, innerCt));
parallelBag.Add(item.Resource.ResourceId);
});

Просмотреть файл

@ -190,7 +190,7 @@ namespace Microsoft.Health.Fhir.Tests.Integration.Persistence
var auditLogger = Substitute.For<IAuditLogger>();
var logger = Substitute.For<ILogger<DeletionService>>();
var deleter = new DeletionService(resourceWrapperFactory, new Lazy<IConformanceProvider>(() => ConformanceProvider), DataStore.CreateMockScopeFactory(), SearchService.CreateMockScopeFactory(), _resourceIdProvider, new FhirRequestContextAccessor(), auditLogger, logger);
var deleter = new DeletionService(resourceWrapperFactory, new Lazy<IConformanceProvider>(() => ConformanceProvider), DataStore.CreateMockScopeProvider(), SearchService.CreateMockScopeProvider(), _resourceIdProvider, new FhirRequestContextAccessor(), auditLogger, logger);
var collection = new ServiceCollection();