Кэш телеметрии

1. Небольшой рефакторинг
2. Покрытие кода тестами
This commit is contained in:
Степанов Дмитрий 2023-10-24 10:55:50 +05:00
parent a5c1722712
commit 86af253df7
3 changed files with 111 additions and 38 deletions

View File

@ -6,7 +6,6 @@ using System.Linq;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Mapster; using Mapster;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using System.Threading; using System.Threading;
@ -25,7 +24,6 @@ namespace AsbCloudInfrastructure.Services.SAUB
public double TimezoneHours { get; init; } = 5; public double TimezoneHours { get; init; } = 5;
} }
private IServiceProvider provider = null!;
private const int activeWellCapacity = 12 * 60 * 60; private const int activeWellCapacity = 12 * 60 * 60;
private const int doneWellCapacity = 65 * 60; private const int doneWellCapacity = 65 * 60;
@ -40,6 +38,8 @@ namespace AsbCloudInfrastructure.Services.SAUB
private static TelemetryDataCache<TDto>? instance; private static TelemetryDataCache<TDto>? instance;
public int CacheItemCount => caches.Count;
public static TelemetryDataCache<TDto> GetInstance<TEntity>(IServiceProvider provider) public static TelemetryDataCache<TDto> GetInstance<TEntity>(IServiceProvider provider)
where TEntity : class, AsbCloudDb.Model.ITelemetryData where TEntity : class, AsbCloudDb.Model.ITelemetryData
{ {
@ -55,7 +55,6 @@ namespace AsbCloudInfrastructure.Services.SAUB
work.Timeout = TimeSpan.FromMinutes(15); work.Timeout = TimeSpan.FromMinutes(15);
worker.WorkStore.RunOnceQueue.Enqueue(work); worker.WorkStore.RunOnceQueue.Enqueue(work);
} }
instance.provider = provider;
return instance; return instance;
} }
@ -69,10 +68,9 @@ namespace AsbCloudInfrastructure.Services.SAUB
if (!range.Any()) if (!range.Any())
return; return;
var newItems = range range = range.OrderBy(x => x.DateTime);
.OrderBy(i => i.DateTime);
foreach (var item in newItems) foreach (var item in range)
item.IdTelemetry = idTelemetry; item.IdTelemetry = idTelemetry;
TelemetryDataCacheItem cacheItem; TelemetryDataCacheItem cacheItem;
@ -87,12 +85,12 @@ namespace AsbCloudInfrastructure.Services.SAUB
{ {
cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem() cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{ {
FirstByDate = newItems.ElementAt(0), FirstByDate = range.ElementAt(0),
LastData = new CyclycArray<TDto>(activeWellCapacity) LastData = new CyclycArray<TDto>(activeWellCapacity)
}); });
} }
cacheItem.LastData.AddRange(newItems); cacheItem.LastData.AddRange(range);
} }
/// <summary> /// <summary>
@ -153,39 +151,44 @@ namespace AsbCloudInfrastructure.Services.SAUB
private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, Action<string, double?> onProgress, CancellationToken token) private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, Action<string, double?> onProgress, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData where TEntity : class, AsbCloudDb.Model.ITelemetryData
{ {
if (isLoading) try
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);
var count = wells.Length;
var i = 0d;
foreach (Well well in wells)
{ {
var capacity = well.IdState == 1 if (isLoading)
? activeWellCapacity throw new Exception("Multiple cache loading detected.");
: doneWellCapacity;
var idTelemetry = well.IdTelemetry!.Value; isLoading = true;
var hoursOffset = well.Timezone.Hours;
onProgress($"Loading for well: {well.Cluster?.Caption}/{well.Caption} (capacity:{capacity}) idTelemetry:{idTelemetry}", i++/count); db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
if(cacheItem is not null) Well[] wells = await db.Set<Well>()
caches.TryAdd(idTelemetry, cacheItem); .Include(well => well.Telemetry)
.Include(well => well.Cluster)
.Where(well => well.IdTelemetry != null)
.ToArrayAsync(token);
var count = wells.Length;
var i = 0d;
foreach (Well well in wells)
{
var capacity = well.IdState == 1
? activeWellCapacity
: doneWellCapacity;
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;
var defaultTimeout = db.Database.GetCommandTimeout();
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) private static async Task<TelemetryDataCacheItem?> GetOrDefaultCacheDataFromDbAsync<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token)

View File

@ -9,6 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.8" />

View File

@ -0,0 +1,69 @@
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 Bogus;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace AsbCloudWebApi.Tests.ServicesTests.SAUB;
public class TelemetryDataSaubCacheTests
{
private readonly IEnumerable<TelemetryDataSaubDto> fakeTelemetries = new Faker<TelemetryDataSaubDto>()
.RuleFor(t => t.DateTime, DateTime.UtcNow)
.Generate(5)
.OrderBy(t => t.DateTime);
private readonly IServiceProvider serviceProvider = Substitute.For<IServiceProvider>();
private readonly TelemetryDataCache<TelemetryDataSaubDto> telemetryDataCache;
public TelemetryDataSaubCacheTests()
{
serviceProvider.GetService<BackgroundWorker>().Returns(new BackgroundWorker(serviceProvider));
telemetryDataCache = TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(serviceProvider);
}
[Fact]
public void AddRange_ShouldReturn_OneAddedElementToCache()
{
//arrange
const int expectedCacheItemCount = 1;
var idTelemetry = new Random().Next(1, 100);
var telemetryDataCacheType = telemetryDataCache.GetType();
telemetryDataCacheType.GetField("isLoading", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(telemetryDataCache, false);
//act
telemetryDataCache.AddRange(idTelemetry, fakeTelemetries);
//assert
Assert.True(telemetryDataCache.CacheItemCount == expectedCacheItemCount);
}
[Fact]
public void AddRange_ShouldReturn_ZeroAddedElementToCache()
{
//arrange
const int expectedCacheItemCount = 0;
var idTelemetry = new Random().Next(1, 100);
var telemetryDataCacheType = telemetryDataCache.GetType();
telemetryDataCacheType.GetField("isLoading", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(telemetryDataCache, true);
//act
telemetryDataCache.AddRange(idTelemetry, fakeTelemetries);
//assert
Assert.True(telemetryDataCache.CacheItemCount == expectedCacheItemCount);
}
}