Merge pull request '#20386366 Кэш телеметрии' (#140) from feature/telemetry into dev

Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/140
This commit is contained in:
Никита Фролов 2023-10-26 09:14:19 +05:00
commit 58c9b55631
3 changed files with 109 additions and 43 deletions

View File

@ -6,7 +6,6 @@ using System.Linq;
using Microsoft.EntityFrameworkCore;
using Mapster;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AsbCloudInfrastructure.Background;
using System.Threading;
@ -24,8 +23,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
public CyclycArray<TDto> LastData { get; init; } = null!;
public double TimezoneHours { get; init; } = 5;
}
private IServiceProvider provider = null!;
private const int activeWellCapacity = 12 * 60 * 60;
private const int doneWellCapacity = 65 * 60;
@ -56,7 +54,6 @@ namespace AsbCloudInfrastructure.Services.SAUB
work.Timeout = TimeSpan.FromMinutes(15);
worker.WorkStore.RunOnceQueue.Enqueue(work);
}
instance.provider = provider;
return instance;
}
@ -70,10 +67,9 @@ namespace AsbCloudInfrastructure.Services.SAUB
if (!range.Any())
return;
var newItems = range
.OrderBy(i => i.DateTime);
range = range.OrderBy(x => x.DateTime);
foreach (var item in newItems)
foreach (var item in range)
item.IdTelemetry = idTelemetry;
TelemetryDataCacheItem cacheItem;
@ -86,14 +82,14 @@ namespace AsbCloudInfrastructure.Services.SAUB
}
else
{
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{
FirstByDate = newItems.ElementAt(0),
LastData = new CyclycArray<TDto>(activeWellCapacity)
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{
FirstByDate = range.ElementAt(0),
LastData = new CyclycArray<TDto>(activeWellCapacity)
});
}
cacheItem.LastData.AddRange(newItems);
cacheItem.LastData.AddRange(range);
}
/// <summary>
@ -167,39 +163,44 @@ namespace AsbCloudInfrastructure.Services.SAUB
private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, Action<string, double?> onProgress, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
if (isLoading)
throw new Exception("Multiple cache loading detected.");
isLoading = true;
var defaultTimeout = db.Database.GetCommandTimeout();
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
Well[] wells = await db.Set<Well>()
.Include(well => well.Telemetry)
.Include(well => well.Cluster)
.Where(well => well.IdTelemetry != null)
.ToArrayAsync(token);
if (isLoading)
throw new Exception("Multiple cache loading detected.");
var count = wells.Length;
var i = 0d;
foreach (Well well in wells)
{
var capacity = well.IdState == 1
? activeWellCapacity
: doneWellCapacity;
try
{
isLoading = true;
Well[] wells = await db.Set<Well>()
.Include(well => well.Telemetry)
.Include(well => well.Cluster)
.Where(well => well.IdTelemetry != null)
.ToArrayAsync(token);
var idTelemetry = well.IdTelemetry!.Value;
var hoursOffset = well.Timezone.Hours;
var count = wells.Length;
var i = 0d;
foreach (Well well in wells)
{
var capacity = well.IdState == 1
? activeWellCapacity
: doneWellCapacity;
onProgress($"Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}", i++ / count);
var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
if (cacheItem is not null)
caches.TryAdd(idTelemetry, cacheItem);
var idTelemetry = well.IdTelemetry!.Value;
var hoursOffset = well.Timezone.Hours;
onProgress($"Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}", i++ / count);
var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
if (cacheItem is not null)
caches.TryAdd(idTelemetry, cacheItem);
}
}
finally
{
isLoading = false;
db.Database.SetCommandTimeout(defaultTimeout);
}
isLoading = false;
db.Database.SetCommandTimeout(defaultTimeout);
}
private static async Task<TelemetryDataCacheItem?> GetOrDefaultCacheDataFromDbAsync<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token)
@ -249,12 +250,12 @@ namespace AsbCloudInfrastructure.Services.SAUB
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
IEnumerable<TDto> data = cacheItem.LastData;
if (!data.Any())
return null;
if (request.GeDate.HasValue)
{
var geDate = request.GeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
@ -274,7 +275,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
var leDate = request.LeDate.Value.ToRemoteDateTime(cacheItem.TimezoneHours);
data = data.Where(d => d.DateTime >= request.LeDate);
}
if (request.Divider > 1)
data = data.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0);

View File

@ -13,7 +13,7 @@ namespace AsbCloudInfrastructure.Services;
public class WellboreService : IWellboreService
{
const string WellboreNameFormat = "Ñòâîë {0}";
const string WellboreNameFormat = "Ствол {0}";
private readonly IWellService wellService;
private readonly IWellOperationRepository wellOperationRepository;
private readonly ITelemetryDataCache<TelemetryDataSaubDto> telemetryDataCache;

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AsbCloudApp.Data.SAUB;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.SAUB;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace AsbCloudWebApi.Tests.ServicesTests.SAUB;
public class TelemetryDataSaubCacheTests
{
private const int idTelemetry = 1;
private readonly IEnumerable<TelemetryDataSaubDto> fakeTelemetries = new[]
{
new TelemetryDataSaubDto()
};
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider>();
private readonly TelemetryDataCache<TelemetryDataSaubDto> telemetryDataCache;
private readonly Type telemetryDataCacheType;
public TelemetryDataSaubCacheTests()
{
serviceProviderMock.GetService<BackgroundWorker>().Returns(new BackgroundWorker(serviceProviderMock));
telemetryDataCache = TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(serviceProviderMock);
telemetryDataCacheType = telemetryDataCache.GetType();
}
[Fact]
public void AddRange_ShouldReturn_AddedElementToCache()
{
//arrange
telemetryDataCacheType.GetField("isLoading", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(telemetryDataCache, false);
//act
telemetryDataCache.AddRange(idTelemetry, fakeTelemetries);
var lastTelemetry = telemetryDataCache.GetLastOrDefault(idTelemetry);
//assert
Assert.Equal(lastTelemetry, fakeTelemetries.Last());
}
[Fact]
public void AddRange_ShouldReturn_NotAddedToCache()
{
//arrange
telemetryDataCacheType.GetField("isLoading", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(telemetryDataCache, true);
//act
telemetryDataCache.AddRange(idTelemetry, fakeTelemetries);
var lastTelemetry = telemetryDataCache.GetLastOrDefault(idTelemetry);
//assert
Assert.NotEqual(lastTelemetry, fakeTelemetries.Last());
}
}