Compare commits

...

10 Commits

28 changed files with 492 additions and 59 deletions

View File

@ -1,10 +1,10 @@
{ {
"DbConnection": { "DbConnection": {
"Host": "postgres", "Host": "localhost",
"Port": 5432, "Port": 5432,
"Database": "persistence", "Database": "persistence",
"Username": "postgres", "Username": "postgres",
"Password": "postgres" "Password": "q"
}, },
"NeedUseKeyCloak": false, "NeedUseKeyCloak": false,
"AuthUser": { "AuthUser": {

View File

@ -2,7 +2,8 @@
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Error"
} }
}, },
"ConnectionStrings": { "ConnectionStrings": {

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DD.Persistence.API\DD.Persistence.API.csproj" />
<ProjectReference Include="..\DD.Persistence.App\DD.Persistence.App.csproj" />
<ProjectReference Include="..\DD.Persistence.Client\DD.Persistence.Client.csproj" />
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
<ProjectReference Include="..\DD.Persistence.Database\DD.Persistence.Database.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,14 @@
namespace Persistence.Benchmark.Database;
public class DbConnection
{
public string Host { get; set; } = null!;
public int Port { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string GetConnectionString() =>
$"Host={Host};Database=persistence;Port={Port};Username={Username};Password={Password};Persist Security Info=True;Include Error Detail=True";
}

View File

@ -0,0 +1,10 @@
using Persistence.Benchmark.Tests;
public class Program
{
private static void Main(string[] args)
{
var count = Convert.ToInt32(Console.ReadLine());
WitsDataBenchmark.ExecuteTest(count).Wait();
}
}

View File

@ -0,0 +1,28 @@
# Тестирование скорости работы с данными Wits
Данный проект предназначен для замеров времени сохранения / получения данных с параметром и отметкой времени. Осуществляется сравнение скорости работы стандартных таблиц и гипертаблиц (timescale), а также сохранения параметра в формате string и jsonb, составного ключа и int ключа.
## Партиционирование (timescale) | Формат параметра - Jsonb | Составной ПК
|Кол-во|Сохранение|Вычитка|
|-|--------|---|
|1.000.000|55 сек.|6.83 сек.|
|2.000.000|1 мин. 57 сек.|11 сек.|
|5.000.000|4 мин. 11 сек.|35 сек.|
|10.000.000|8 мин. 20 сек.|58 сек.|
|80.000.000|>100 мин.|20 мин. 3 сек.|
## Без партиционирования | Формат параметра - String | Составной ПК
|Кол-во|Сохранение|Вычитка|
|-|--------|---|
|1.000.000|40 сек.|26 сек.|
|2.000.000|1 мин. 26 сек.|48 сек.|
|5.000.000|3 мин. 5 сек.|2 мин. 28 сек.|
|10.000.000|8 мин. 42 сек.|4 мин. 49 сек.|
## Без партиционирования | Формат параметра - String | Int ПК
|Кол-во|Сохранение|Вычитка|
|-|--------|---|
|1.000.000|42 сек.|21 сек.|
|2.000.000|1 мин. 22 сек.|51 сек.|
|5.000.000|3 мин. 19 сек.|2 мин. 23 сек.|
|10.000.000|8 мин. 31 сек.|4 мин. 55 сек.|

View File

@ -0,0 +1,18 @@
namespace Persistence.Benchmark;
/// <summary>
/// Фабрика HTTP клиентов для интеграционных тестов
/// </summary>
public class TestHttpClientFactory : IHttpClientFactory
{
private readonly WebAppFactoryFixture factory;
public TestHttpClientFactory(WebAppFactoryFixture factory)
{
this.factory = factory;
}
public HttpClient CreateClient(string name)
{
return factory.CreateClient();
}
}

View File

@ -0,0 +1,100 @@
using DD.Persistence.Client;
using DD.Persistence.Models;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
namespace Persistence.Benchmark.Tests;
public static class WitsDataBenchmark
{
private const string discriminatorId = "fef21bfd-e924-473d-b6c8-e4377e38245d";
private const int chunkSize = 25_000;
public static async Task ExecuteTest(int count)
{
try
{
var factory = new WebAppFactoryFixture();
var scope = factory.Services.CreateScope();
var persistenceClientFactory = scope.ServiceProvider
.GetRequiredService<PersistenceClientFactory>();
var client = persistenceClientFactory.GetWitsDataClient();
var source = new CancellationTokenSource(TimeSpan.FromSeconds(6000));
var data = GenerateData(count);
var sw = new Stopwatch();
var saved = 0;
foreach (var item in data)
{
var time = sw.Elapsed;
sw.Start();
var response = await client.AddRange(item, source.Token);
sw.Stop();
saved = saved + response;
Console.WriteLine($"Сохранено: {saved.ToString()}");
}
Console.WriteLine($"Затрачено времени на сохранение: {sw.Elapsed}");
var discriminator = Guid.Parse(discriminatorId);
var date = DateTime.Now.AddDays(-365);
Console.WriteLine($"\nВычитка...");
sw = new Stopwatch();
sw.Start();
var get = await client.GetPart(discriminator, date, count, source.Token);
sw.Stop();
Console.WriteLine($"Затрачено времени на вычитку {get.SelectMany(e => e.Values).Count()} записей: {sw.Elapsed}");
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
private static IEnumerable<IEnumerable<WitsDataDto>> GenerateData(int countToCreate)
{
var result = new List<List<WitsDataDto>>();
var time = DateTimeOffset.UtcNow;
var seconds = -1;
int enumerableCount = countToCreate / chunkSize + (countToCreate % chunkSize == 0 ? 0 : 1);
for (var k = 0; k < enumerableCount; k++)
{
var dtos = new List<WitsDataDto>();
for (var j = 0; j < 1000; j++)
{
for (var i = 0; i < chunkSize / 1000; i++)
{
var random = new Random();
var timestamped = time.AddSeconds(seconds);
seconds = seconds - 1;
dtos.Add(new WitsDataDto()
{
DiscriminatorId = Guid.Parse(discriminatorId),
Timestamped = timestamped.AddSeconds(i),
Values = new List<WitsValueDto>()
{
new WitsValueDto()
{
RecordId = i + 1,
ItemId = i + 1,
Value = random.Next(1, 100)
}
}
});
}
}
result.Add(dtos);
}
return result;
}
}

View File

@ -0,0 +1,69 @@
using DD.Persistence.Client;
using DD.Persistence.Database.Model;
using DD.Persistence.Database.Postgres;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Persistence.Benchmark.Database;
namespace Persistence.Benchmark;
public class WebAppFactoryFixture : WebApplicationFactory<DD.Persistence.App.Program>
{
private string connectionString = string.Empty;
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.Tests.json");
var dbConnection = config.Build().GetSection("DbConnection").Get<DbConnection>()!;
connectionString = dbConnection.GetConnectionString();
});
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersistencePostgresContext>));
if (descriptor != null)
services.Remove(descriptor);
services.AddDbContext<PersistencePostgresContext>(options =>
options.UseNpgsql(connectionString));
services.AddLogging(builder => builder.AddConsole());
services.RemoveAll<IHttpClientFactory>();
services.AddSingleton<IHttpClientFactory>(provider =>
{
return new TestHttpClientFactory(this);
});
services.AddSingleton<PersistenceClientFactory>();
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider;
var dbContext = scopedServices.GetRequiredService<PersistencePostgresContext>();
//dbContext.Database.EnsureCreatedAndMigrated();
dbContext.SaveChanges();
});
}
public override async ValueTask DisposeAsync()
{
var dbContext = new PersistencePostgresContext(
new DbContextOptionsBuilder<PersistencePostgresContext>()
.UseNpgsql(connectionString)
.Options);
await dbContext.Database.EnsureDeletedAsync();
GC.SuppressFinalize(this);
}
}

View File

@ -31,6 +31,8 @@ namespace DD.Persistence.Client
httpClient = httpClientFactory.CreateClient(); httpClient = httpClientFactory.CreateClient();
httpClient.Authorize(configuration); httpClient.Authorize(configuration);
httpClient.Timeout = TimeSpan.FromSeconds(6000);
} }
public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IAuthTokenFactory authTokenFactory, IServiceProvider provider, IConfiguration configuration) public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IAuthTokenFactory authTokenFactory, IServiceProvider provider, IConfiguration configuration)
@ -41,6 +43,8 @@ namespace DD.Persistence.Client
var token = authTokenFactory.GetToken(); var token = authTokenFactory.GetToken();
httpClient.Authorize(token); httpClient.Authorize(token);
httpClient.Timeout = TimeSpan.FromSeconds(6000);
} }
/// <summary> /// <summary>

View File

@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DD.Persistence.Database.Postgres.Migrations namespace DD.Persistence.Database.Postgres.Migrations
{ {
[DbContext(typeof(PersistencePostgresContext))] [DbContext(typeof(PersistencePostgresContext))]
[Migration("20241220062251_Init")] [Migration("20241225122815_Init")]
partial class Init partial class Init
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "8.0.10") .HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -43,7 +43,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("SystemId"); b.HasKey("SystemId");
b.ToTable("DataSourceSystem"); b.ToTable("data_source_system");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b => modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
@ -60,14 +60,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasComment("Временная отметка"); .HasComment("Временная отметка");
b.Property<string>("Value") b.Property<object>("Value")
.IsRequired() .IsRequired()
.HasColumnType("varchar(256)") .HasColumnType("jsonb")
.HasComment("Значение параметра в виде строки"); .HasComment("Значение параметра");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp"); b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("ParameterData"); b.ToTable("parameter_data");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
@ -102,7 +102,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasIndex("SystemId"); b.HasIndex("SystemId");
b.ToTable("TechMessage"); b.ToTable("tech_message");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b =>
@ -122,7 +122,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("IdDiscriminator", "Timestamp"); b.HasKey("IdDiscriminator", "Timestamp");
b.ToTable("TimestampedSets", t => b.ToTable("timestamped_set", t =>
{ {
t.HasComment("Общая таблица данных временных рядов"); t.HasComment("Общая таблица данных временных рядов");
}); });
@ -178,7 +178,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("ChangeLog"); b.ToTable("change_log");
}); });
modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b =>
@ -261,7 +261,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Date"); b.HasKey("Date");
b.ToTable("DataSaub"); b.ToTable("data_saub");
}); });
modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
@ -285,7 +285,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Key", "Created"); b.HasKey("Key", "Created");
b.ToTable("Setpoint"); b.ToTable("setpoint");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>

View File

@ -12,7 +12,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "ChangeLog", name: "change_log",
columns: table => new columns: table => new
{ {
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"), Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
@ -29,11 +29,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_ChangeLog", x => x.Id); table.PrimaryKey("PK_change_log", x => x.Id);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "DataSaub", name: "data_saub",
columns: table => new columns: table => new
{ {
date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false), date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
@ -58,11 +58,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_DataSaub", x => x.date); table.PrimaryKey("PK_data_saub", x => x.date);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "DataSourceSystem", name: "data_source_system",
columns: table => new columns: table => new
{ {
SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы - источника данных"), SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы - источника данных"),
@ -71,25 +71,25 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_DataSourceSystem", x => x.SystemId); table.PrimaryKey("PK_data_source_system", x => x.SystemId);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "ParameterData", name: "parameter_data",
columns: table => new columns: table => new
{ {
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор системы"), DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор системы"),
ParameterId = table.Column<int>(type: "integer", nullable: false, comment: "Id параметра"), ParameterId = table.Column<int>(type: "integer", nullable: false, comment: "Id параметра"),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"),
Value = table.Column<string>(type: "varchar(256)", nullable: false, comment: "Значение параметра в виде строки") Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение параметра")
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_ParameterData", x => new { x.DiscriminatorId, x.ParameterId, x.Timestamp }); table.PrimaryKey("PK_parameter_data", x => new { x.DiscriminatorId, x.ParameterId, x.Timestamp });
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Setpoint", name: "setpoint",
columns: table => new columns: table => new
{ {
Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"), Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"),
@ -99,11 +99,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created }); table.PrimaryKey("PK_setpoint", x => new { x.Key, x.Created });
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "TimestampedSets", name: "timestamped_set",
columns: table => new columns: table => new
{ {
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"),
@ -112,12 +112,12 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp }); table.PrimaryKey("PK_timestamped_set", x => new { x.IdDiscriminator, x.Timestamp });
}, },
comment: "Общая таблица данных временных рядов"); comment: "Общая таблица данных временных рядов");
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "TechMessage", name: "tech_message",
columns: table => new columns: table => new
{ {
EventId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id события"), EventId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id события"),
@ -129,44 +129,47 @@ namespace DD.Persistence.Database.Postgres.Migrations
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_TechMessage", x => x.EventId); table.PrimaryKey("PK_tech_message", x => x.EventId);
table.ForeignKey( table.ForeignKey(
name: "FK_TechMessage_DataSourceSystem_SystemId", name: "FK_tech_message_data_source_system_SystemId",
column: x => x.SystemId, column: x => x.SystemId,
principalTable: "DataSourceSystem", principalTable: "data_source_system",
principalColumn: "SystemId", principalColumn: "SystemId",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_TechMessage_SystemId", name: "IX_tech_message_SystemId",
table: "TechMessage", table: "tech_message",
column: "SystemId"); column: "SystemId");
migrationBuilder.Sql
("SELECT create_hypertable('parameter_data','Timestamp','ParameterId',2,chunk_time_interval => INTERVAL '5 day');");
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "ChangeLog"); name: "change_log");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "DataSaub"); name: "data_saub");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "ParameterData"); name: "parameter_data");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Setpoint"); name: "setpoint");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "TechMessage"); name: "tech_message");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "TimestampedSets"); name: "timestamped_set");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "DataSourceSystem"); name: "data_source_system");
} }
} }
} }

View File

@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "8.0.10") .HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -40,7 +40,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("SystemId"); b.HasKey("SystemId");
b.ToTable("DataSourceSystem"); b.ToTable("data_source_system");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b => modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
@ -57,14 +57,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasComment("Временная отметка"); .HasComment("Временная отметка");
b.Property<string>("Value") b.Property<object>("Value")
.IsRequired() .IsRequired()
.HasColumnType("varchar(256)") .HasColumnType("jsonb")
.HasComment("Значение параметра в виде строки"); .HasComment("Значение параметра");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp"); b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("ParameterData"); b.ToTable("parameter_data");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
@ -99,7 +99,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasIndex("SystemId"); b.HasIndex("SystemId");
b.ToTable("TechMessage"); b.ToTable("tech_message");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b =>
@ -119,7 +119,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("IdDiscriminator", "Timestamp"); b.HasKey("IdDiscriminator", "Timestamp");
b.ToTable("TimestampedSets", t => b.ToTable("timestamped_set", t =>
{ {
t.HasComment("Общая таблица данных временных рядов"); t.HasComment("Общая таблица данных временных рядов");
}); });
@ -175,7 +175,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("ChangeLog"); b.ToTable("change_log");
}); });
modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b =>
@ -258,7 +258,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Date"); b.HasKey("Date");
b.ToTable("DataSaub"); b.ToTable("data_saub");
}); });
modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
@ -282,7 +282,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Key", "Created"); b.HasKey("Key", "Created");
b.ToTable("Setpoint"); b.ToTable("setpoint");
}); });
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b => modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>

View File

@ -9,6 +9,7 @@ namespace DD.Persistence.Database.Model;
/// <summary> /// <summary>
/// Часть записи, описывающая изменение /// Часть записи, описывающая изменение
/// </summary> /// </summary>
[Table("change_log")]
public class ChangeLog : IChangeLog, IWithSectionPart public class ChangeLog : IChangeLog, IWithSectionPart
{ {
[Key, Comment("Ключ записи")] [Key, Comment("Ключ записи")]

View File

@ -2,6 +2,8 @@
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Model; namespace DD.Persistence.Database.Model;
[Table("data_saub")]
public class DataSaub : ITimestampedData public class DataSaub : ITimestampedData
{ {
[Key, Column("date")] [Key, Column("date")]

View File

@ -3,6 +3,8 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Entity; namespace DD.Persistence.Database.Entity;
[Table("data_source_system")]
public class DataSourceSystem public class DataSourceSystem
{ {
[Key, Comment("Id системы - источника данных")] [Key, Comment("Id системы - источника данных")]

View File

@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Entity; namespace DD.Persistence.Database.Entity;
[Table("parameter_data")]
[PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))] [PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))]
public class ParameterData public class ParameterData
{ {
@ -13,8 +14,8 @@ public class ParameterData
[Comment("Id параметра")] [Comment("Id параметра")]
public int ParameterId { get; set; } public int ParameterId { get; set; }
[Column(TypeName = "varchar(256)"), Comment("Значение параметра в виде строки")] [Column(TypeName = "jsonb"), Comment("Значение параметра")]
public required string Value { get; set; } public required object Value { get; set; }
[Comment("Временная отметка")] [Comment("Временная отметка")]
public DateTimeOffset Timestamp { get; set; } public DateTimeOffset Timestamp { get; set; }

View File

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Model namespace DD.Persistence.Database.Model
{ {
[Table("setpoint")]
[PrimaryKey(nameof(Key), nameof(Created))] [PrimaryKey(nameof(Key), nameof(Created))]
public class Setpoint public class Setpoint
{ {

View File

@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Entity namespace DD.Persistence.Database.Entity
{ {
[Table("tech_message")]
public class TechMessage public class TechMessage
{ {
[Key, Comment("Id события")] [Key, Comment("Id события")]

View File

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace DD.Persistence.Database.Entity; namespace DD.Persistence.Database.Entity;
[Table("timestamped_set")]
[Comment("Общая таблица данных временных рядов")] [Comment("Общая таблица данных временных рядов")]
[PrimaryKey(nameof(IdDiscriminator), nameof(Timestamp))] [PrimaryKey(nameof(IdDiscriminator), nameof(Timestamp))]
public record TimestampedSet( public record TimestampedSet(

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.UsefulQueries
{
class partition_function
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.UsefulQueries
{
class partition_function
{
}
}

View File

@ -1,10 +1,10 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using DD.Persistence.Database.Entity;
using DD.Persistence.Models; using DD.Persistence.Models;
using System.Net; using System.Net;
using Xunit; using Xunit;
using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces;
using DD.Persistence.Client; using DD.Persistence.Client;
using DD.Persistence.Database.Entity;
namespace DD.Persistence.IntegrationTests.Controllers; namespace DD.Persistence.IntegrationTests.Controllers;
public class WitsDataControllerTest : BaseIntegrationTest public class WitsDataControllerTest : BaseIntegrationTest

View File

@ -1,8 +1,8 @@
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Models; using DD.Persistence.Models;
using DD.Persistence.Repositories; using DD.Persistence.Repositories;
using DD.Persistence.Database.Entity;
namespace DD.Persistence.Repository.Repositories; namespace DD.Persistence.Repository.Repositories;
public class ParameterRepository : IParameterRepository public class ParameterRepository : IParameterRepository
@ -83,4 +83,105 @@ public class ParameterRepository : IParameterRepository
return result; return result;
} }
//public async Task<int> AddRange(IEnumerable<ParameterDto> dtos, CancellationToken token)
//{
// int result = 0;
// var groups = dtos.GroupBy(e => e.ParameterId / 1000);
// foreach (var group in groups)
// {
// switch (group.Key)
// {
// case 1:
// result += await AddRange<ParameterData1>(dtos, token);
// break;
// case 2:
// result += await AddRange<ParameterData2>(dtos, token);
// break;
// case 3:
// result += await AddRange<ParameterData3>(dtos, token);
// break;
// case 4:
// result += await AddRange<ParameterData4>(dtos, token);
// break;
// case 5:
// result += await AddRange<ParameterData5>(dtos, token);
// break;
// case 6:
// result += await AddRange<ParameterData6>(dtos, token);
// break;
// case 7:
// result += await AddRange<ParameterData7>(dtos, token);
// break;
// case 8:
// result += await AddRange<ParameterData8>(dtos, token);
// break;
// case 9:
// result += await AddRange<ParameterData9>(dtos, token);
// break;
// case 10:
// result += await AddRange<ParameterData10>(dtos, token);
// break;
// case 11:
// result += await AddRange<ParameterData11>(dtos, token);
// break;
// case 12:
// result += await AddRange<ParameterData12>(dtos, token);
// break;
// case 13:
// result += await AddRange<ParameterData13>(dtos, token);
// break;
// case 14:
// result += await AddRange<ParameterData14>(dtos, token);
// break;
// case 15:
// result += await AddRange<ParameterData15>(dtos, token);
// break;
// case 16:
// result += await AddRange<ParameterData16>(dtos, token);
// break;
// case 17:
// result += await AddRange<ParameterData17>(dtos, token);
// break;
// case 18:
// result += await AddRange<ParameterData18>(dtos, token);
// break;
// case 19:
// result += await AddRange<ParameterData19>(dtos, token);
// break;
// case 20:
// result += await AddRange<ParameterData20>(dtos, token);
// break;
// case 21:
// result += await AddRange<ParameterData21>(dtos, token);
// break;
// case 22:
// result += await AddRange<ParameterData22>(dtos, token);
// break;
// case 23:
// result += await AddRange<ParameterData23>(dtos, token);
// break;
// case 24:
// result += await AddRange<ParameterData24>(dtos, token);
// break;
// case 25:
// result += await AddRange<ParameterData25>(dtos, token);
// break;
// }
// }
// return result;
//}
//private async Task<int> AddRange<T>(IEnumerable<ParameterDto> dtos, CancellationToken token)
// where T : ParameterData
//{
// var t = dtos.Where(e => e.Value == null);
// var entities = dtos.Select(e => e.Adapt<T>());
// var tt = entities.Where(e => e.Value == null);
// await db.Set<T>().AddRangeAsync(entities, token);
// var result = await db.SaveChangesAsync(token);
// return result;
//}
} }

View File

@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.9.34714.143 VisualStudioVersion = 17.9.34714.143
@ -19,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.Client", "DD
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.App", "DD.Persistence.App\DD.Persistence.App.csproj", "{063238BF-E982-43FA-9DDB-7D7D279086D8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.App", "DD.Persistence.App\DD.Persistence.App.csproj", "{063238BF-E982-43FA-9DDB-7D7D279086D8}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Benchmark", "DD.Persistence.Benchmark\DD.Persistence.Benchmark.csproj", "{89975A38-C78B-4464-A04D-D63E8745062D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -57,6 +58,10 @@ Global
{063238BF-E982-43FA-9DDB-7D7D279086D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {063238BF-E982-43FA-9DDB-7D7D279086D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.Build.0 = Release|Any CPU {063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.Build.0 = Release|Any CPU
{89975A38-C78B-4464-A04D-D63E8745062D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89975A38-C78B-4464-A04D-D63E8745062D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89975A38-C78B-4464-A04D-D63E8745062D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89975A38-C78B-4464-A04D-D63E8745062D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Persistence.Benchmark.Database.Entities;
public class ParameterData
{
[Key]
public Guid Id { get; set; }
[Required, Comment("Дискриминатор системы")]
public Guid DiscriminatorId { get; set; }
[Comment("Id параметра")]
public int ParameterId { get; set; }
[Column(TypeName = "varchar(256)"), Comment("Значение параметра в виде строки")]
public required string Value { get; set; }
[Comment("Временная отметка")]
public DateTimeOffset Timestamp { get; set; }
}