Ограничить параллелизм и логировать пропуски в by-approver
Экран «Заявки по участнику» показывал пустой список: чтение 18 типов заявок шло разом, ошибки SharePoint глотались молча. Теперь не более 3 типов одновременно, а пропущенные типы/списки пишутся в лог. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentResults;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Tnsu.ApprovalRoute.Application.Common.Errors;
|
||||
using Tnsu.ApprovalRoute.Application.ListPartitioning;
|
||||
using Tnsu.ApprovalRoute.Domain.Entities;
|
||||
@@ -25,10 +26,16 @@ namespace Tnsu.ApprovalRoute.Application.RequestManage;
|
||||
public sealed class RequestManageService(
|
||||
ApprovalRouteDbContext db,
|
||||
ISharePointClient sharePoint,
|
||||
ListPartitionRegistry partitionRegistry)
|
||||
ListPartitionRegistry partitionRegistry,
|
||||
ILogger<RequestManageService> logger)
|
||||
{
|
||||
private const int DefaultPageSize = 20;
|
||||
|
||||
// Сколько типов заявок читаем из SharePoint одновременно. Без ограничения
|
||||
// 18 типов × партиции разом упирались в троттлинг (429), а ошибки глотались
|
||||
// и экран показывал пустой список.
|
||||
private const int MaxParallelTypes = 3;
|
||||
|
||||
// Внутренние имена колонок списка согласования — 1:1 с FIELDS в
|
||||
// requestManageService.ts (тот же самый список SharePoint, тот же провижининг).
|
||||
private const string ApproverField = "TansuSiteApprover";
|
||||
@@ -145,8 +152,19 @@ public sealed class RequestManageService(
|
||||
var statusFilter = string.Join(" or ", activeStatuses.Select(s => $"{StatusField} eq '{Escape(s)}'"));
|
||||
var email = approverEmail.Trim();
|
||||
|
||||
var results = await Task.WhenAll(
|
||||
requestTypes.Select(rt => FindForTypeSafeAsync(rt, statusFilter, email, 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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
gate.Release();
|
||||
}
|
||||
}));
|
||||
|
||||
return Result.Ok<IReadOnlyList<UserApprovalRowDto>>(results.SelectMany(r => r).ToList());
|
||||
}
|
||||
@@ -158,10 +176,11 @@ public sealed class RequestManageService(
|
||||
{
|
||||
return await FindForTypeAsync(requestType, statusFilter, approverEmail, cancellationToken);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
// Один недоступный/переименованный список не должен обнулять результат по
|
||||
// остальным типам заявок — та же терпимость, что и в ReminderService.
|
||||
logger.LogWarning(ex, "by-approver: тип заявок {RequestTypeId} «{RequestTypeName}» пропущен", requestType.Id, requestType.Name);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -190,9 +209,11 @@ public sealed class RequestManageService(
|
||||
hits = await sharePoint.GetItemsByListTitleAsync(
|
||||
siteUrl, approvalListTitle, $"{ApprovalRowSelect}&$filter=({statusFilter})&$top=2000", cancellationToken);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
continue; // партиция сломана/переименована вручную — остальные не теряем.
|
||||
// партиция сломана/переименована вручную — остальные не теряем.
|
||||
logger.LogWarning(ex, "by-approver: список «{ApprovalList}» пропущен", approvalListTitle);
|
||||
continue;
|
||||
}
|
||||
|
||||
var matched = hits
|
||||
|
||||
Reference in New Issue
Block a user