diff --git a/DD.Persistence.Database.Postgres.Test/DD.Persistence.Database.Postgres.Test.csproj b/DD.Persistence.Database.Postgres.Test/DD.Persistence.Database.Postgres.Test.csproj
new file mode 100644
index 0000000..859acb5
--- /dev/null
+++ b/DD.Persistence.Database.Postgres.Test/DD.Persistence.Database.Postgres.Test.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net9.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DD.Persistence.Database.Postgres.Test/DbFixture.cs b/DD.Persistence.Database.Postgres.Test/DbFixture.cs
new file mode 100644
index 0000000..c3a0c64
--- /dev/null
+++ b/DD.Persistence.Database.Postgres.Test/DbFixture.cs
@@ -0,0 +1,40 @@
+using DD.Persistence.Database.Model;
+using DD.Persistence.Database.Postgres.Extensions;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DD.Persistence.Database.Postgres.Test;
+public class DbFixture : IDisposable
+{
+ private string connectionString { get; }
+ public ServiceProvider serviceProvider { get; private set; }
+
+ public DbFixture()
+ {
+ connectionString = $"Host=localhost;Port=5462;Username=postgres;Password=postgres;Database={Guid.CreateVersion7()}";
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection
+ .AddDbContext(options => options.UseNpgsql(connectionString),
+ ServiceLifetime.Transient);
+
+ serviceProvider = serviceCollection.BuildServiceProvider();
+
+ var context = serviceProvider.GetRequiredService();
+ context.Database.EnsureCreated();
+ context.Database.AddPartitioning();
+ context.SaveChanges();
+ }
+
+ public void Dispose()
+ {
+ var dbContext = new PersistencePostgresContext(
+ new DbContextOptionsBuilder()
+ .UseNpgsql(connectionString)
+ .Options);
+
+ dbContext.Database.EnsureDeleted();
+
+ GC.SuppressFinalize(this);
+ }
+}
diff --git a/DD.Persistence.Database.Postgres.Test/UnitTestCheckHyperTables.cs b/DD.Persistence.Database.Postgres.Test/UnitTestCheckHyperTables.cs
new file mode 100644
index 0000000..6f58046
--- /dev/null
+++ b/DD.Persistence.Database.Postgres.Test/UnitTestCheckHyperTables.cs
@@ -0,0 +1,65 @@
+using DD.Persistence.Database.Entity;
+using Mapster;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Npgsql;
+
+namespace DD.Persistence.Database.Postgres.Test;
+
+public class UnitTestCheckHyperTables : IClassFixture
+{
+ private ServiceProvider _serviceProvider;
+
+ public UnitTestCheckHyperTables(DbFixture fixture)
+ {
+ _serviceProvider = fixture.serviceProvider;
+ }
+
+ [Fact]
+ public void CreateHyperTable_For_ParameterData_Return_Success()
+ {
+ var chunksCount = 0;
+
+ var entity = new ParameterData()
+ {
+ DiscriminatorId = Guid.NewGuid(),
+ ParameterId = 1,
+ Timestamp = DateTime.UtcNow,
+ Value = "123"
+ };
+
+ using (var context = _serviceProvider.GetService()!)
+ {
+ context.ParameterData.Add(entity);
+
+ var entity2 = entity.Adapt();
+ entity2.ParameterId = 2;
+ context.ParameterData.Add(entity2);
+
+ var entity3 = entity2.Adapt();
+ entity3.ParameterId = 3;
+ context.ParameterData.Add(entity3);
+
+ var entity4 = entity3.Adapt();
+ entity4.ParameterId = 4;
+ context.ParameterData.Add(entity4);
+
+ var entity5 = entity3.Adapt();
+ entity5.Timestamp = DateTime.UtcNow.AddDays(1).AddHours(1);
+ context.ParameterData.Add(entity5);
+
+ var entity6 = entity3.Adapt();
+ entity6.DiscriminatorId = Guid.CreateVersion7();
+ context.ParameterData.Add(entity6);
+
+ context.SaveChanges();
+
+ string sql = "select count(*) from (select show_chunks('parameter_data'));";
+ var queryRow = context.Database.SqlQueryRaw(sql);
+
+ chunksCount = queryRow.AsEnumerable().FirstOrDefault();
+ }
+
+ Assert.Equal(5, chunksCount);
+ }
+}
diff --git a/DD.Persistence.Database.Postgres/Extensions/EFExtensionsPartitioning.cs b/DD.Persistence.Database.Postgres/Extensions/EFExtensionsPartitioning.cs
index 4ee2890..0860dc3 100644
--- a/DD.Persistence.Database.Postgres/Extensions/EFExtensionsPartitioning.cs
+++ b/DD.Persistence.Database.Postgres/Extensions/EFExtensionsPartitioning.cs
@@ -25,6 +25,8 @@ public static class EFExtensionsPartitioning
///
private static void AddParameterDataPartitioning(this DatabaseFacade db)
{
+ var dayCount = 1;
+ var sectionCount = 128;
var type = typeof(ParameterData);
var tableAttribute = type.GetCustomAttribute();
if (tableAttribute is null)
@@ -32,13 +34,16 @@ public static class EFExtensionsPartitioning
return;
}
- const int sectionsNumber = 2;
- const int chunkTimeInterval = 5;
- var sqlString = $"SELECT create_hypertable('{tableAttribute.Name}'," +
- $"'{nameof(ParameterData.Timestamp)}'," +
- $"'{nameof(ParameterData.ParameterId)}'," +
- $"{sectionsNumber}," +
- $"chunk_time_interval => INTERVAL '{chunkTimeInterval} day');";
- db.ExecuteSqlRaw(sqlString);
+ var sqlCreateHypertableString = $"SELECT create_hypertable('{tableAttribute.Name}'," +
+ $"by_range('{nameof(ParameterData.Timestamp)}', INTERVAL '{dayCount} day'), if_not_exists => {true});";
+ db.ExecuteSqlRaw(sqlCreateHypertableString);
+
+ var sqlCreateDimensionParameterId = $"SELECT add_dimension('{tableAttribute.Name}'," +
+ $"by_hash('{nameof(ParameterData.ParameterId)}', {sectionCount}), if_not_exists => {true});";
+ db.ExecuteSqlRaw(sqlCreateDimensionParameterId);
+
+ var sqlCreateDimensionDiscriminatorId = $"SELECT add_dimension('{tableAttribute.Name}'," +
+ $"by_hash('{nameof(ParameterData.DiscriminatorId)}', {sectionCount}), if_not_exists => {true});";
+ db.ExecuteSqlRaw(sqlCreateDimensionDiscriminatorId);
}
}
diff --git a/DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs
similarity index 91%
rename from DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.Designer.cs
rename to DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs
index 81695fb..bdeaf87 100644
--- a/DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.Designer.cs
+++ b/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs
@@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
- [Migration("20250122120353_Init")]
+ [Migration("20250203061429_Init")]
partial class Init
{
///
@@ -26,6 +26,47 @@ namespace DD.Persistence.Database.Postgres.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+ modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasComment("Ключ записи");
+
+ b.Property("Creation")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата создания записи");
+
+ b.Property("IdAuthor")
+ .HasColumnType("uuid")
+ .HasComment("Автор изменения");
+
+ b.Property("IdDiscriminator")
+ .HasColumnType("uuid")
+ .HasComment("Дискриминатор таблицы");
+
+ b.Property("IdEditor")
+ .HasColumnType("uuid")
+ .HasComment("Редактор");
+
+ b.Property("IdNext")
+ .HasColumnType("uuid")
+ .HasComment("Id заменяющей записи");
+
+ b.Property("Obsolete")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата устаревания (например при удалении)");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasComment("Значение");
+
+ b.HasKey("Id");
+
+ b.ToTable("change_log");
+ });
+
modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b =>
{
b.Property("DiscriminatorId")
@@ -88,6 +129,29 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("parameter_data");
});
+ modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("uuid")
+ .HasComment("Ключ");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата создания уставки");
+
+ b.Property("IdUser")
+ .HasColumnType("uuid")
+ .HasComment("Id автора последнего изменения");
+
+ b.Property("Value")
+ .HasColumnType("jsonb")
+ .HasComment("Значение уставки");
+
+ b.HasKey("Key", "Timestamp");
+
+ b.ToTable("setpoint");
+ });
+
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.Property("EventId")
@@ -143,82 +207,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("timestamped_values");
});
- modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasComment("Ключ записи");
-
- b.Property("Creation")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата создания записи");
-
- b.Property("DepthEnd")
- .HasColumnType("double precision")
- .HasComment("Глубина забоя на дату окончания интервала");
-
- b.Property("DepthStart")
- .HasColumnType("double precision")
- .HasComment("Глубина забоя на дату начала интервала");
-
- b.Property("IdAuthor")
- .HasColumnType("uuid")
- .HasComment("Автор изменения");
-
- b.Property("IdDiscriminator")
- .HasColumnType("uuid")
- .HasComment("Дискриминатор таблицы");
-
- b.Property("IdEditor")
- .HasColumnType("uuid")
- .HasComment("Редактор");
-
- b.Property("IdNext")
- .HasColumnType("uuid")
- .HasComment("Id заменяющей записи");
-
- b.Property("IdSection")
- .HasColumnType("uuid")
- .HasComment("Ключ секции");
-
- b.Property("Obsolete")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата устаревания (например при удалении)");
-
- b.Property("Value")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasComment("Значение");
-
- b.HasKey("Id");
-
- b.ToTable("change_log");
- });
-
- modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
- {
- b.Property("Key")
- .HasColumnType("uuid")
- .HasComment("Ключ");
-
- b.Property("Timestamp")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата создания уставки");
-
- b.Property("IdUser")
- .HasColumnType("uuid")
- .HasComment("Id автора последнего изменения");
-
- b.Property("Value")
- .HasColumnType("jsonb")
- .HasComment("Значение уставки");
-
- b.HasKey("Key", "Timestamp");
-
- b.ToTable("setpoint");
- });
-
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
diff --git a/DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs
similarity index 94%
rename from DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.cs
rename to DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs
index 6f56873..df996bc 100644
--- a/DD.Persistence.Database.Postgres/Migrations/20250122120353_Init.cs
+++ b/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs
@@ -23,9 +23,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
Creation = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
Obsolete = table.Column(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
IdNext = table.Column(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
- DepthStart = table.Column(type: "double precision", nullable: false, comment: "Глубина забоя на дату начала интервала"),
- DepthEnd = table.Column(type: "double precision", nullable: false, comment: "Глубина забоя на дату окончания интервала"),
- IdSection = table.Column(type: "uuid", nullable: false, comment: "Ключ секции"),
Value = table.Column(type: "jsonb", nullable: false, comment: "Значение")
},
constraints: table =>
diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs
index 3db9dda..5c5ad2e 100644
--- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs
+++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs
@@ -23,6 +23,47 @@ namespace DD.Persistence.Database.Postgres.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+ modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasComment("Ключ записи");
+
+ b.Property("Creation")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата создания записи");
+
+ b.Property("IdAuthor")
+ .HasColumnType("uuid")
+ .HasComment("Автор изменения");
+
+ b.Property("IdDiscriminator")
+ .HasColumnType("uuid")
+ .HasComment("Дискриминатор таблицы");
+
+ b.Property("IdEditor")
+ .HasColumnType("uuid")
+ .HasComment("Редактор");
+
+ b.Property("IdNext")
+ .HasColumnType("uuid")
+ .HasComment("Id заменяющей записи");
+
+ b.Property("Obsolete")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата устаревания (например при удалении)");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasComment("Значение");
+
+ b.HasKey("Id");
+
+ b.ToTable("change_log");
+ });
+
modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b =>
{
b.Property("DiscriminatorId")
@@ -85,6 +126,29 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("parameter_data");
});
+ modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("uuid")
+ .HasComment("Ключ");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone")
+ .HasComment("Дата создания уставки");
+
+ b.Property("IdUser")
+ .HasColumnType("uuid")
+ .HasComment("Id автора последнего изменения");
+
+ b.Property("Value")
+ .HasColumnType("jsonb")
+ .HasComment("Значение уставки");
+
+ b.HasKey("Key", "Timestamp");
+
+ b.ToTable("setpoint");
+ });
+
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.Property("EventId")
@@ -140,82 +204,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("timestamped_values");
});
- modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasComment("Ключ записи");
-
- b.Property("Creation")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата создания записи");
-
- b.Property("DepthEnd")
- .HasColumnType("double precision")
- .HasComment("Глубина забоя на дату окончания интервала");
-
- b.Property("DepthStart")
- .HasColumnType("double precision")
- .HasComment("Глубина забоя на дату начала интервала");
-
- b.Property("IdAuthor")
- .HasColumnType("uuid")
- .HasComment("Автор изменения");
-
- b.Property("IdDiscriminator")
- .HasColumnType("uuid")
- .HasComment("Дискриминатор таблицы");
-
- b.Property("IdEditor")
- .HasColumnType("uuid")
- .HasComment("Редактор");
-
- b.Property("IdNext")
- .HasColumnType("uuid")
- .HasComment("Id заменяющей записи");
-
- b.Property("IdSection")
- .HasColumnType("uuid")
- .HasComment("Ключ секции");
-
- b.Property("Obsolete")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата устаревания (например при удалении)");
-
- b.Property("Value")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasComment("Значение");
-
- b.HasKey("Id");
-
- b.ToTable("change_log");
- });
-
- modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
- {
- b.Property("Key")
- .HasColumnType("uuid")
- .HasComment("Ключ");
-
- b.Property("Timestamp")
- .HasColumnType("timestamp with time zone")
- .HasComment("Дата создания уставки");
-
- b.Property("IdUser")
- .HasColumnType("uuid")
- .HasComment("Id автора последнего изменения");
-
- b.Property("Value")
- .HasColumnType("jsonb")
- .HasComment("Значение уставки");
-
- b.HasKey("Key", "Timestamp");
-
- b.ToTable("setpoint");
- });
-
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
diff --git a/DD.Persistence.Database.Postgres/PersistencePostgresContext.cs b/DD.Persistence.Database.Postgres/PersistencePostgresContext.cs
index 6cd8955..3da502f 100644
--- a/DD.Persistence.Database.Postgres/PersistencePostgresContext.cs
+++ b/DD.Persistence.Database.Postgres/PersistencePostgresContext.cs
@@ -1,9 +1,9 @@
-using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
namespace DD.Persistence.Database.Model;
///
-/// EF Postgres
+/// EF контекст для базы данных Postgres
///
public partial class PersistencePostgresContext : PersistenceDbContext
{
diff --git a/DD.Persistence.sln b/DD.Persistence.sln
index 926e688..ca91e47 100644
--- a/DD.Persistence.sln
+++ b/DD.Persistence.sln
@@ -35,6 +35,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решен
.gitea\workflows\integrationTests.yaml = .gitea\workflows\integrationTests.yaml
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Database.Postgres.Test", "DD.Persistence.Database.Postgres.Test\DD.Persistence.Database.Postgres.Test.csproj", "{47142566-9EAB-4FB5-92EC-9DCB02CAC890}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -85,6 +87,10 @@ Global
{B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {47142566-9EAB-4FB5-92EC-9DCB02CAC890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {47142566-9EAB-4FB5-92EC-9DCB02CAC890}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {47142566-9EAB-4FB5-92EC-9DCB02CAC890}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {47142566-9EAB-4FB5-92EC-9DCB02CAC890}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE