add time out
This commit is contained in:
parent
f6b1e7779d
commit
9abda9c219
1 changed files with 48 additions and 18 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -23,6 +24,7 @@ namespace BMA.EHR.Recruit.Service.Services
|
||||||
private readonly MetadataDbContext _contextMetadata;
|
private readonly MetadataDbContext _contextMetadata;
|
||||||
private readonly OrgDbContext _contextOrg;
|
private readonly OrgDbContext _contextOrg;
|
||||||
private readonly MinIOService _minIOService;
|
private readonly MinIOService _minIOService;
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
|
@ -31,12 +33,14 @@ namespace BMA.EHR.Recruit.Service.Services
|
||||||
OrgDbContext contextOrg,
|
OrgDbContext contextOrg,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
MinIOService minIOService,
|
MinIOService minIOService,
|
||||||
IConfiguration configuration)
|
IConfiguration configuration,
|
||||||
|
IHttpClientFactory httpClientFactory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_contextMetadata = contextMetadata;
|
_contextMetadata = contextMetadata;
|
||||||
_contextOrg = contextOrg;
|
_contextOrg = contextOrg;
|
||||||
_minIOService = minIOService;
|
_minIOService = minIOService;
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
@ -180,13 +184,25 @@ namespace BMA.EHR.Recruit.Service.Services
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 🚀 Prepare HTTP client once
|
// 🚀 Prepare HTTP client once via factory with timeout
|
||||||
var httpClient1 = new HttpClient();
|
var clientForPos = _httpClientFactory.CreateClient("default");
|
||||||
httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
|
clientForPos.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
|
||||||
httpClient1.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]);
|
clientForPos.DefaultRequestHeaders.Remove("api_key");
|
||||||
|
clientForPos.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"] ?? "");
|
||||||
var apiUrl1 = $"{_configuration["API"]}/org/pos/level";
|
var apiUrl1 = $"{_configuration["API"]}/org/pos/level";
|
||||||
var response1 = await httpClient1.GetStringAsync(apiUrl1);
|
var response1 = string.Empty;
|
||||||
var posOptions = JsonConvert.DeserializeObject<RecruitPosRequest>(response1);
|
try
|
||||||
|
{
|
||||||
|
using var ctsPos = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
response1 = await clientForPos.GetStringAsync(apiUrl1, ctsPos.Token);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// timeout - fallback to empty posOptions
|
||||||
|
response1 = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var posOptions = string.IsNullOrWhiteSpace(response1) ? null : JsonConvert.DeserializeObject<RecruitPosRequest>(response1);
|
||||||
|
|
||||||
var recruitImport = await _context.RecruitImports.AsQueryable()
|
var recruitImport = await _context.RecruitImports.AsQueryable()
|
||||||
.FirstOrDefaultAsync(x => x.Id == examId);
|
.FirstOrDefaultAsync(x => x.Id == examId);
|
||||||
|
|
@ -245,26 +261,41 @@ namespace BMA.EHR.Recruit.Service.Services
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x.ExamId))
|
.Where(x => !string.IsNullOrWhiteSpace(x.ExamId))
|
||||||
.ToDictionary(x => x.ExamId, x => x);
|
.ToDictionary(x => x.ExamId, x => x);
|
||||||
|
|
||||||
// 🚀 Prepare HTTP client once
|
// 🚀 Batch HTTP requests using IHttpClientFactory with concurrency limit and cancellation
|
||||||
var httpClient = new HttpClient();
|
var clientForOrg = _httpClientFactory.CreateClient("default");
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
|
clientForOrg.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
|
||||||
httpClient.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]);
|
clientForOrg.DefaultRequestHeaders.Remove("api_key");
|
||||||
|
clientForOrg.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"] ?? "");
|
||||||
|
|
||||||
// 🚀 Batch HTTP requests
|
var semaphore = new SemaphoreSlim(10); // limit concurrency
|
||||||
var orgTasks = candidates.Select(async candidate =>
|
var orgTasks = candidates.Select(async candidate =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(candidate.CitizenId))
|
if (string.IsNullOrWhiteSpace(candidate.CitizenId))
|
||||||
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
|
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
|
||||||
|
|
||||||
var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}";
|
await semaphore.WaitAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await httpClient.GetStringAsync(apiUrl);
|
var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}";
|
||||||
return new { CitizenId = candidate.CitizenId, org = JsonConvert.DeserializeObject<dynamic>(response) };
|
try
|
||||||
|
{
|
||||||
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
var response = await clientForOrg.GetStringAsync(apiUrl, cts.Token);
|
||||||
|
return new { CitizenId = candidate.CitizenId, org = JsonConvert.DeserializeObject<dynamic>(response) };
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// timeout
|
||||||
|
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
finally
|
||||||
{
|
{
|
||||||
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
|
semaphore.Release();
|
||||||
}
|
}
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
|
@ -445,7 +476,6 @@ namespace BMA.EHR.Recruit.Service.Services
|
||||||
// 🚀 Single SaveChanges at the end
|
// 🚀 Single SaveChanges at the end
|
||||||
await _contextMetadata.SaveChangesAsync();
|
await _contextMetadata.SaveChangesAsync();
|
||||||
|
|
||||||
httpClient.Dispose();
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue