Оптимизирован WellboreService.GetWellboresAsync()

Добавлен WellOperationRepository.GetSectionsAsync()
Оптимизирован WellOperationRepository.FirstOperationDate()
This commit is contained in:
Frolov-Nikita 2023-10-06 15:19:02 +05:00
parent a6c7ddc94b
commit 01f04c7ea5
No known key found for this signature in database
GPG Key ID: 719E3386D12B0760
5 changed files with 532 additions and 415 deletions

View File

@ -0,0 +1,47 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data;
/// <summary>
/// Параметры секции определяемые по операциям из ГГД
/// </summary>
public class SectionByOperationsDto
{
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// 0 = план или 1 = факт или прогноз = 2
/// </summary>
public int IdType { get; set; }
/// <summary>
/// id секции скважины
/// </summary>
public int IdWellSectionType { get; set; }
/// <summary>
/// Глубина начала первой операции в секции, м
/// </summary>
[Range(0, 50_000)]
public double DepthStart { get; set; }
/// <summary>
/// Дата начала первой операции в секции
/// </summary>
public DateTimeOffset DateStart { get; set; }
/// <summary>
/// Глубина после завершения последней операции операции в секции, м
/// </summary>
[Range(0, 50_000)]
public double DepthEnd { get; set; }
/// <summary>
/// Дата после завершения последней операции операции в секции
/// </summary>
public DateTimeOffset DateEnd { get; set; }
}

View File

@ -97,5 +97,13 @@ namespace AsbCloudApp.Repositories
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token);
/// <summary>
/// Получить секции скважин из операций ГГД. Секцие поделены на плановые и фактические.
/// </summary>
/// <param name="idsWells"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token);
}
}

View File

@ -0,0 +1,5 @@
# Проблемы фонового сервиса
- нет понимания по загрузке очереди работ
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
- отключать/включать целиком
- отключать/включать отдельную периодическую задачу

View File

@ -13,18 +13,18 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
namespace AsbCloudInfrastructure.Repository;
/// <summary>
/// репозиторий операций по скважине
/// </summary>
public class WellOperationRepository : IWellOperationRepository
{
private const string KeyCacheSections = "OperationsBySectionSummarties";
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
private readonly IWellService wellService;
private static Dictionary<int, DateTimeOffset?>? firstOperationsCache = null;
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
{
@ -63,8 +63,6 @@ namespace AsbCloudInfrastructure.Repository
.OrderBy(s => s.Order)
.Select(s => s.Adapt<WellSectionTypeDto>());
public async Task<WellOperationPlanDto> GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token)
{
var timezone = wellService.GetTimezone(idWell);
@ -118,24 +116,77 @@ namespace AsbCloudInfrastructure.Repository
}
/// <inheritdoc/>
public DateTimeOffset? FirstOperationDate(int idWell)
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
{
if (firstOperationsCache is null)
var cache = await memoryCache.GetOrCreateAsync(KeyCacheSections, async (entry) =>
{
var query = db.WellOperations
.GroupBy(o => o.IdWell)
.Select(g => new Tuple<int, DateTimeOffset?, DateTimeOffset?>
(
g.Key,
g.Where(o => o.IdType == WellOperation.IdOperationTypePlan).Min(o => o.DateStart),
g.Where(o => o.IdType == WellOperation.IdOperationTypeFact).Min(o => o.DateStart)
));
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
firstOperationsCache = query
.ToDictionary(f => f.Item1, f => f.Item3 ?? f.Item2);
var query = db.Set<WellOperation>()
.GroupBy(operation => new
{
operation.IdWell,
operation.IdType,
operation.IdWellSectionType,
})
.Select(group => new
{
group.Key.IdWell,
group.Key.IdType,
group.Key.IdWellSectionType,
First = group
.OrderBy(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DepthStart,
})
.First(),
Last = group
.OrderByDescending(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DurationHours,
operation.DepthEnd,
})
.First(),
});
var dbData = await query.ToArrayAsync(token);
var sections = dbData.Select(
item => new SectionByOperationsDto
{
IdWell = item.IdWell,
IdType = item.IdType,
IdWellSectionType = item.IdWellSectionType,
DateStart = item.First.DateStart,
DepthStart = item.First.DepthStart,
DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours),
DepthEnd = item.Last.DepthEnd,
})
.ToArray()
.AsEnumerable();
entry.Value = sections;
return sections;
});
var sections = cache.Where(s => idsWells.Contains(s.IdWell));
return sections;
}
return firstOperationsCache?.GetValueOrDefault(idWell);
/// <inheritdoc/>
public DateTimeOffset? FirstOperationDate(int idWell)
{
var sections = GetSectionsAsync(new[] { idWell }, CancellationToken.None).Result;
var first = sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypeFact)
?? sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypePlan);
return first?.DateStart;
}
/// <inheritdoc/>
@ -277,8 +328,11 @@ namespace AsbCloudInfrastructure.Repository
db.WellOperations.Add(entity);
}
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <inheritdoc/>
@ -289,8 +343,11 @@ namespace AsbCloudInfrastructure.Repository
var entity = dto.Adapt<WellOperation>();
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
db.WellOperations.Update(entity);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <inheritdoc/>
@ -299,8 +356,11 @@ namespace AsbCloudInfrastructure.Repository
{
var query = db.WellOperations.Where(e => ids.Contains(e.Id));
db.WellOperations.RemoveRange(query);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <summary>
@ -402,5 +462,3 @@ namespace AsbCloudInfrastructure.Repository
return result;
}
}
}

View File

@ -35,7 +35,7 @@ public class WellboreService : IWellboreService
}
public async Task<IEnumerable<WellboreDto>> GetWellboresAsync(WellboreRequest request,
CancellationToken cancellationToken)
CancellationToken token)
{
var wellbores = new List<WellboreDto>(request.Ids.Count());
var skip = request.Skip ?? 0;
@ -44,26 +44,43 @@ public class WellboreService : IWellboreService
var sections = wellOperationRepository.GetSectionTypes()
.ToDictionary(w => w.Id, w => w);
var ids = request.Ids.GroupBy(i => i.idWell);
var ids = request.Ids.GroupBy(i => i.idWell, i => i.idSection);
var idsWells = request.Ids.Select(i => i.idWell);
var allSections = await wellOperationRepository.GetSectionsAsync(idsWells, token);
foreach (var id in ids)
{
var well = await wellService.GetOrDefaultAsync(id.Key, cancellationToken);
var well = await wellService.GetOrDefaultAsync(id.Key, token);
if (well is null)
continue;
var wellOperations = await GetFactOperationsAsync(well.Id, id.Select(i => i.idSection), cancellationToken);
var groupedOperations = wellOperations.GroupBy(o => o.IdWellSectionType);
var wellWellbores = groupedOperations.Select(group => new WellboreDto {
Id = group.Key,
Name = sections[group.Key].Caption,
var wellTimezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
var wellFactSections = allSections
.Where(section => section.IdWell == id.Key)
.Where(section => section.IdType == WellOperation.IdOperationTypeFact);
var idsSections = id
.Where(i => i.HasValue)
.Select(i => i!.Value);
if (idsSections.Any())
wellFactSections = wellFactSections
.Where(section => idsSections.Contains(section.IdWellSectionType));
var wellWellbores = wellFactSections.Select(section => new WellboreDto {
Id = section.IdWellSectionType,
Name = sections[section.IdWellSectionType].Caption,
Well = well.Adapt<WellWithTimezoneDto>(),
DateStart = group.Min(operation => operation.DateStart).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)),
DateEnd = group.Max(operation => operation.DateStart.AddHours(operation.DurationHours)).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)),
DepthStart = group.Min(operation => operation.DepthStart),
DepthEnd = group.Max(operation => operation.DepthEnd),
DateStart = section.DateStart.ToOffset(wellTimezoneOffset),
DateEnd = section.DateEnd.ToOffset(wellTimezoneOffset),
DepthStart = section.DepthStart,
DepthEnd = section.DepthEnd,
});
wellbores.AddRange(wellWellbores);
}
@ -71,22 +88,4 @@ public class WellboreService : IWellboreService
.OrderBy(w => w.Well.Id).ThenBy(w => w.Id)
.Skip(skip).Take(take);
}
private async Task<IOrderedEnumerable<WellOperationDto>> GetFactOperationsAsync(int idWell, IEnumerable<int?> idsSections,
CancellationToken cancellationToken)
{
var request = new WellOperationRequest
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
SortFields = new[] { "DateStart asc" },
};
request.SectionTypeIds = idsSections.All(i => i.HasValue)
? idsSections.Select(i => i!.Value)
: null;
return (await wellOperationRepository.GetAsync(request, cancellationToken))
.OrderBy(o => o.DateStart);
}
}