Fix/by approver silent failures #25

Merged
gorbenko.pavel merged 6 commits from fix/by-approver-silent-failures into main 2026-10-05 04:38:37 +00:00
Showing only changes of commit feb95f5621 - Show all commits
@@ -152,13 +152,21 @@ public sealed class RequestManageService(
var statusFilter = string.Join(" or ", activeStatuses.Select(s => $"{StatusField} eq '{Escape(s)}'"));
var email = approverEmail.Trim();
// DbContext не потокобезопасен: архивные партиции читаем из БД ПОСЛЕДОВАТЕЛЬНО,
// и только дальнейшее чтение SharePoint (без БД) идёт параллельно. Раньше
// GetArchivedAsync вызывался из параллельных задач — «вторая операция на
// контексте» падала, а пустой catch превращал это в пустой список.
var archivedByType = new Dictionary<int, IReadOnlyList<RequestListPartition>>();
foreach (var rt in requestTypes)
archivedByType[rt.Id] = await partitionRegistry.GetArchivedAsync(rt, cancellationToken);
using var gate = new SemaphoreSlim(MaxParallelTypes);
var results = await Task.WhenAll(requestTypes.Select(async rt =>
{
await gate.WaitAsync(cancellationToken);
try
{
return await FindForTypeSafeAsync(rt, statusFilter, email, cancellationToken);
return await FindForTypeSafeAsync(rt, archivedByType[rt.Id], statusFilter, email, cancellationToken);
}
finally
{
@@ -170,11 +178,11 @@ public sealed class RequestManageService(
}
private async Task<IReadOnlyList<UserApprovalRowDto>> FindForTypeSafeAsync(
RequestType requestType, string statusFilter, string approverEmail, CancellationToken cancellationToken)
RequestType requestType, IReadOnlyList<RequestListPartition> archived, string statusFilter, string approverEmail, CancellationToken cancellationToken)
{
try
{
return await FindForTypeAsync(requestType, statusFilter, approverEmail, cancellationToken);
return await FindForTypeAsync(requestType, archived, statusFilter, approverEmail, cancellationToken);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
@@ -186,10 +194,9 @@ public sealed class RequestManageService(
}
private async Task<IReadOnlyList<UserApprovalRowDto>> FindForTypeAsync(
RequestType requestType, string statusFilter, string approverEmail, CancellationToken cancellationToken)
RequestType requestType, IReadOnlyList<RequestListPartition> archived, string statusFilter, string approverEmail, CancellationToken cancellationToken)
{
var siteUrl = requestType.SiteUrl!;
var archived = await partitionRegistry.GetArchivedAsync(requestType, cancellationToken);
var current = await partitionRegistry.BuildCurrentAsync(requestType, archived, cancellationToken);
var partitions = archived