From 20bd27028e2017a1074292f0a9c22cb88b47db0b Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Sun, 6 Jul 2025 18:38:58 +0300 Subject: [PATCH 01/14] dev: update SLO EF test --- slo/src/EF/SloTableContext.cs | 68 ++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index ad4d265c..18ae9728 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -1,57 +1,93 @@ +using System.Data; using EntityFrameworkCore.Ydb.Extensions; using Internal; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Ydb.Sdk; +using Ydb.Sdk.Ado; namespace EF; -public class SloTableContext : SloTableContext> +public class SloTableContext : SloTableContext> { protected override string Job => "EF"; - protected override PooledDbContextFactory CreateClient(Config config) => - new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString).Options); + protected override Func CreateClient(Config config) => + () => new TableDbContext(new DbContextOptionsBuilder().UseYdb(config.ConnectionString) + .UseLoggerFactory(ISloContext.Factory).Options); protected override async Task Create( - PooledDbContextFactory client, + Func client, int operationTimeout ) { - await using var dbContext = await client.CreateDbContextAsync(); + await using var dbContext = client(); await dbContext.Database.EnsureCreatedAsync(); await dbContext.Database.ExecuteSqlRawAsync(SloTable.Options); } protected override async Task<(int, StatusCode)> Save( - PooledDbContextFactory client, + Func client, SloTable sloTable, int writeTimeout ) { - await using var dbContext = await client.CreateDbContextAsync(); - dbContext.SloEntities.Add(sloTable); - await dbContext.SaveChangesAsync(); + await using var dbContext = client(); + var executeStrategy = dbContext.Database.CreateExecutionStrategy(); + await executeStrategy.ExecuteAsync(async () => await dbContext.Database.ExecuteSqlRawAsync( + $"UPSERT INTO `{SloTable.Name}` (Guid, Id, PayloadStr, PayloadDouble, PayloadTimestamp) " + + "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", + new YdbParameter + { + DbType = DbType.String, + ParameterName = "Guid", + Value = sloTable.Guid.ToString() + }, + new YdbParameter + { + DbType = DbType.Int32, + ParameterName = "Id", + Value = sloTable.Id + }, + new YdbParameter + { + DbType = DbType.String, + ParameterName = "PayloadStr", + Value = sloTable.PayloadStr + }, + new YdbParameter + { + DbType = DbType.Double, + ParameterName = "PayloadDouble", + Value = sloTable.PayloadDouble + }, + new YdbParameter + { + DbType = DbType.DateTime2, + ParameterName = "PayloadTimestamp", + Value = sloTable.PayloadTimestamp + })); return (1, StatusCode.Success); } protected override async Task<(int, StatusCode, object?)> Select( - PooledDbContextFactory client, + Func client, (Guid Guid, int Id) select, int readTimeout ) { - await using var dbContext = await client.CreateDbContextAsync(); - await dbContext.SloEntities.FindAsync(select.Guid, select.Id); + await using var dbContext = client(); + await dbContext.SloEntities + .SingleAsync(table => table.Guid == select.Guid && table.Id == select.Id); return (0, StatusCode.Success, null); } - protected override async Task SelectCount(PooledDbContextFactory client) + protected override async Task SelectCount(Func client) { - await using var dbContext = await client.CreateDbContextAsync(); + await using var dbContext = client(); + var count = await dbContext.SloEntities.CountAsync(); - return await dbContext.SloEntities.CountAsync(); + return count; } } \ No newline at end of file From e352ad2a0928480b1207a220911a0ade3d91e63a Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Sun, 6 Jul 2025 18:41:25 +0300 Subject: [PATCH 02/14] delete logging --- slo/src/EF/SloTableContext.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 18ae9728..c8311e37 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -12,8 +12,7 @@ public class SloTableContext : SloTableContext> protected override string Job => "EF"; protected override Func CreateClient(Config config) => - () => new TableDbContext(new DbContextOptionsBuilder().UseYdb(config.ConnectionString) - .UseLoggerFactory(ISloContext.Factory).Options); + () => new TableDbContext(new DbContextOptionsBuilder().UseYdb(config.ConnectionString).Options); protected override async Task Create( Func client, From 4511e8b6c0037262aeb0da5ae21811abfa70a463 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Sun, 6 Jul 2025 19:13:43 +0300 Subject: [PATCH 03/14] change Single on First --- slo/src/EF/SloTableContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index c8311e37..3a7bd6f1 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -77,7 +77,7 @@ int readTimeout { await using var dbContext = client(); await dbContext.SloEntities - .SingleAsync(table => table.Guid == select.Guid && table.Id == select.Id); + .FirstAsync(table => table.Guid == select.Guid && table.Id == select.Id); return (0, StatusCode.Success, null); } From d0a2bdb950b8a1028c67f3f675c6a698fa50cf87 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Sun, 6 Jul 2025 23:04:45 +0300 Subject: [PATCH 04/14] maybe fix on upsert --- slo/src/EF/SloTableContext.cs | 80 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 3a7bd6f1..86b5f95d 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -30,41 +30,45 @@ int operationTimeout int writeTimeout ) { - await using var dbContext = client(); - var executeStrategy = dbContext.Database.CreateExecutionStrategy(); - await executeStrategy.ExecuteAsync(async () => await dbContext.Database.ExecuteSqlRawAsync( - $"UPSERT INTO `{SloTable.Name}` (Guid, Id, PayloadStr, PayloadDouble, PayloadTimestamp) " + - "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", - new YdbParameter - { - DbType = DbType.String, - ParameterName = "Guid", - Value = sloTable.Guid.ToString() - }, - new YdbParameter - { - DbType = DbType.Int32, - ParameterName = "Id", - Value = sloTable.Id - }, - new YdbParameter - { - DbType = DbType.String, - ParameterName = "PayloadStr", - Value = sloTable.PayloadStr - }, - new YdbParameter - { - DbType = DbType.Double, - ParameterName = "PayloadDouble", - Value = sloTable.PayloadDouble - }, - new YdbParameter - { - DbType = DbType.DateTime2, - ParameterName = "PayloadTimestamp", - Value = sloTable.PayloadTimestamp - })); + await using var context = client(); + var executeStrategy = context.Database.CreateExecutionStrategy(); + await executeStrategy.ExecuteAsync(async () => + { + await using var dbContext = client(); + return await dbContext.Database.ExecuteSqlRawAsync( + $"UPSERT INTO `{SloTable.Name}` (Guid, Id, PayloadStr, PayloadDouble, PayloadTimestamp) " + + "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", + new YdbParameter + { + DbType = DbType.String, + ParameterName = "Guid", + Value = sloTable.Guid.ToString() + }, + new YdbParameter + { + DbType = DbType.Int32, + ParameterName = "Id", + Value = sloTable.Id + }, + new YdbParameter + { + DbType = DbType.String, + ParameterName = "PayloadStr", + Value = sloTable.PayloadStr + }, + new YdbParameter + { + DbType = DbType.Double, + ParameterName = "PayloadDouble", + Value = sloTable.PayloadDouble + }, + new YdbParameter + { + DbType = DbType.DateTime2, + ParameterName = "PayloadTimestamp", + Value = sloTable.PayloadTimestamp + }); + }); return (1, StatusCode.Success); } @@ -75,9 +79,9 @@ await executeStrategy.ExecuteAsync(async () => await dbContext.Database.ExecuteS int readTimeout ) { - await using var dbContext = client(); - await dbContext.SloEntities - .FirstAsync(table => table.Guid == select.Guid && table.Id == select.Id); + // await using var dbContext = client(); + // await dbContext.SloEntities + // .FirstAsync(table => table.Guid == select.Guid && table.Id == select.Id); return (0, StatusCode.Success, null); } From 7173b047b71e1ea88c254bacff595eb130732f67 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 18:49:25 +0300 Subject: [PATCH 05/14] added EF SLO --- .github/workflows/slo.yml | 2 +- slo/src/EF/SloTableContext.cs | 12 +++++------- .../src/Storage/Internal/YdbTypeMappingSource.cs | 9 +++++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/slo.yml b/.github/workflows/slo.yml index fc3b424e..254f1a81 100644 --- a/.github/workflows/slo.yml +++ b/.github/workflows/slo.yml @@ -32,7 +32,7 @@ jobs: workload: - AdoNet - Dapper -# - EF + - EF concurrency: group: slo-${{ github.ref }}-${{ matrix.workload }} diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 86b5f95d..28eae410 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -40,9 +40,9 @@ await executeStrategy.ExecuteAsync(async () => "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", new YdbParameter { - DbType = DbType.String, + DbType = DbType.Guid, ParameterName = "Guid", - Value = sloTable.Guid.ToString() + Value = sloTable.Guid }, new YdbParameter { @@ -79,11 +79,9 @@ await executeStrategy.ExecuteAsync(async () => int readTimeout ) { - // await using var dbContext = client(); - // await dbContext.SloEntities - // .FirstAsync(table => table.Guid == select.Guid && table.Id == select.Id); - - return (0, StatusCode.Success, null); + await using var dbContext = client(); + return (0, StatusCode.Success, await dbContext.SloEntities.FirstOrDefaultAsync( + table => table.Guid == select.Guid && table.Id == select.Id)); } protected override async Task SelectCount(Func client) diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs index 7a745aef..eb032a67 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs @@ -32,6 +32,8 @@ RelationalTypeMappingSourceDependencies relationalDependencies private static readonly YdbDecimalTypeMapping Decimal = new(typeof(decimal)); + private static readonly GuidTypeMapping Guid = new("Uuid"); + private static readonly YdbTextTypeMapping Text = YdbTextTypeMapping.Default; private static readonly YdbBytesTypeMapping Bytes = YdbBytesTypeMapping.Default; private static readonly YdbJsonTypeMapping Json = new("Json", typeof(JsonElement), null); @@ -64,6 +66,9 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Float", [Float] }, { "Double", [Double] }, + { "Decimal", [Decimal] }, + { "Guid", [Guid] }, + { "Date", [Date] }, { "DateTime", [DateTime] }, { "Timestamp", [Timestamp] }, @@ -72,8 +77,6 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Text", [Text] }, { "Bytes", [Bytes] }, - { "Decimal", [Decimal] }, - { "Json", [Json] } }; @@ -95,6 +98,8 @@ RelationalTypeMappingSourceDependencies relationalDependencies { typeof(double), Double }, { typeof(decimal), Decimal }, + { typeof(Guid), Guid }, + { typeof(string), Text }, { typeof(byte[]), Bytes }, { typeof(JsonElement), Json }, From e6472dd88ae1bca7624a21ea37bfec858e0642eb Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 19:52:14 +0300 Subject: [PATCH 06/14] Update SloTableContext.cs --- slo/src/EF/SloTableContext.cs | 69 ++++++++--------------------------- 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 28eae410..6da0640d 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -1,94 +1,55 @@ -using System.Data; using EntityFrameworkCore.Ydb.Extensions; using Internal; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Ydb.Sdk; -using Ydb.Sdk.Ado; namespace EF; -public class SloTableContext : SloTableContext> +public class SloTableContext : SloTableContext> { protected override string Job => "EF"; - protected override Func CreateClient(Config config) => - () => new TableDbContext(new DbContextOptionsBuilder().UseYdb(config.ConnectionString).Options); + protected override PooledDbContextFactory CreateClient(Config config) => + new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString).Options); protected override async Task Create( - Func client, + PooledDbContextFactory client, int operationTimeout ) { - await using var dbContext = client(); + await using var dbContext = await client.CreateDbContextAsync(); await dbContext.Database.EnsureCreatedAsync(); await dbContext.Database.ExecuteSqlRawAsync(SloTable.Options); } protected override async Task<(int, StatusCode)> Save( - Func client, + PooledDbContextFactory client, SloTable sloTable, int writeTimeout ) { - await using var context = client(); - var executeStrategy = context.Database.CreateExecutionStrategy(); - await executeStrategy.ExecuteAsync(async () => - { - await using var dbContext = client(); - return await dbContext.Database.ExecuteSqlRawAsync( - $"UPSERT INTO `{SloTable.Name}` (Guid, Id, PayloadStr, PayloadDouble, PayloadTimestamp) " + - "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", - new YdbParameter - { - DbType = DbType.Guid, - ParameterName = "Guid", - Value = sloTable.Guid - }, - new YdbParameter - { - DbType = DbType.Int32, - ParameterName = "Id", - Value = sloTable.Id - }, - new YdbParameter - { - DbType = DbType.String, - ParameterName = "PayloadStr", - Value = sloTable.PayloadStr - }, - new YdbParameter - { - DbType = DbType.Double, - ParameterName = "PayloadDouble", - Value = sloTable.PayloadDouble - }, - new YdbParameter - { - DbType = DbType.DateTime2, - ParameterName = "PayloadTimestamp", - Value = sloTable.PayloadTimestamp - }); - }); + await using var context = await client.CreateDbContextAsync(); + context.SloEntities.Add(sloTable); + await context.SaveChangesAsync(); return (1, StatusCode.Success); } protected override async Task<(int, StatusCode, object?)> Select( - Func client, + PooledDbContextFactory client, (Guid Guid, int Id) select, int readTimeout ) { - await using var dbContext = client(); + await using var dbContext = await client.CreateDbContextAsync(); return (0, StatusCode.Success, await dbContext.SloEntities.FirstOrDefaultAsync( table => table.Guid == select.Guid && table.Id == select.Id)); } - protected override async Task SelectCount(Func client) + protected override async Task SelectCount(PooledDbContextFactory client) { - await using var dbContext = client(); - var count = await dbContext.SloEntities.CountAsync(); - - return count; + await using var dbContext = await client.CreateDbContextAsync(); + return await dbContext.SloEntities.CountAsync(); } } \ No newline at end of file From a9a48e22747e120cc50bdf751a84316d700ff5d8 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 21:13:14 +0300 Subject: [PATCH 07/14] test commit --- src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs | 2 +- src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs | 1 + src/Ydb.Sdk/src/Ado/YdbConnection.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 46cea9ed..0c8fab61 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -8,5 +8,5 @@ public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies) : ExecutionStrategy(dependencies, maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(10)) // TODO User settings { protected override bool ShouldRetryOn(Exception exception) - => exception is YdbException { IsTransient: true }; + => exception is YdbException { IsTransientWhenIdempotent: true }; } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs index eb032a67..5e680a30 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs @@ -67,6 +67,7 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Double", [Double] }, { "Decimal", [Decimal] }, + { "Guid", [Guid] }, { "Date", [Date] }, diff --git a/src/Ydb.Sdk/src/Ado/YdbConnection.cs b/src/Ydb.Sdk/src/Ado/YdbConnection.cs index afadcf1b..48829691 100644 --- a/src/Ydb.Sdk/src/Ado/YdbConnection.cs +++ b/src/Ydb.Sdk/src/Ado/YdbConnection.cs @@ -92,12 +92,15 @@ public override async Task OpenAsync(CancellationToken cancellationToken) { ThrowIfConnectionOpen(); + ConnectionState = ConnectionState.Connecting; try { Session = await PoolManager.GetSession(ConnectionStringBuilder, cancellationToken); } catch (Exception e) { + ConnectionState = ConnectionState.Closed; + throw e switch { OperationCanceledException => throw new YdbException(StatusCode.Cancelled, From efc15dcef2c7e6b06f40cda2d60ff7b9e86c8648 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 21:16:14 +0300 Subject: [PATCH 08/14] using upsert --- slo/src/EF/SloTableContext.cs | 43 +++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 6da0640d..0ddf531a 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -1,8 +1,10 @@ +using System.Data; using EntityFrameworkCore.Ydb.Extensions; using Internal; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Ydb.Sdk; +using Ydb.Sdk.Ado; namespace EF; @@ -30,8 +32,45 @@ int writeTimeout ) { await using var context = await client.CreateDbContextAsync(); - context.SloEntities.Add(sloTable); - await context.SaveChangesAsync(); + var executeStrategy = context.Database.CreateExecutionStrategy(); + await executeStrategy.ExecuteAsync(async () => + { + var dbContext = await client.CreateDbContextAsync(); + + return await dbContext.Database.ExecuteSqlRawAsync( + $"UPSERT INTO `{SloTable.Name}` (Guid, Id, PayloadStr, PayloadDouble, PayloadTimestamp) " + + "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", + new YdbParameter + { + DbType = DbType.String, + ParameterName = "Guid", + Value = sloTable.Guid.ToString() + }, + new YdbParameter + { + DbType = DbType.Int32, + ParameterName = "Id", + Value = sloTable.Id + }, + new YdbParameter + { + DbType = DbType.String, + ParameterName = "PayloadStr", + Value = sloTable.PayloadStr + }, + new YdbParameter + { + DbType = DbType.Double, + ParameterName = "PayloadDouble", + Value = sloTable.PayloadDouble + }, + new YdbParameter + { + DbType = DbType.DateTime2, + ParameterName = "PayloadTimestamp", + Value = sloTable.PayloadTimestamp + }); + }); return (1, StatusCode.Success); } From 8b472b357097768e44aa1962f825bbd29c74a8e9 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 23:01:41 +0300 Subject: [PATCH 09/14] fix --- slo/src/EF/SloTableContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 0ddf531a..964a559c 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -44,7 +44,7 @@ await executeStrategy.ExecuteAsync(async () => { DbType = DbType.String, ParameterName = "Guid", - Value = sloTable.Guid.ToString() + Value = sloTable.Guid }, new YdbParameter { From 6706b1c82d90a317f71a8aed615fc2d4b504dcaf Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 7 Jul 2025 23:07:43 +0300 Subject: [PATCH 10/14] fix --- slo/src/EF/SloTableContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 964a559c..c7daf0d7 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -42,7 +42,7 @@ await executeStrategy.ExecuteAsync(async () => "VALUES (@Guid, @Id, @PayloadStr, @PayloadDouble, @PayloadTimestamp)", new YdbParameter { - DbType = DbType.String, + DbType = DbType.Guid, ParameterName = "Guid", Value = sloTable.Guid }, From 29de4e5d417d50db11770a6fb2ceafd702db11cb Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 8 Jul 2025 13:48:04 +0300 Subject: [PATCH 11/14] fix --- .github/workflows/slo.yml | 29 +++++++++---------- ...ator.cs => YdbDateTimeMemberTranslator.cs} | 0 .../Internal/Mapping/YdbDecimalTypeMapping.cs | 7 +++-- .../Internal/Mapping/YdbGuidTypeMapping.cs | 21 ++++++++++++++ .../Storage/Internal/YdbExecutionStrategy.cs | 2 +- .../Storage/Internal/YdbTypeMappingSource.cs | 4 +-- src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs | 5 ++-- 7 files changed, 43 insertions(+), 25 deletions(-) rename src/EFCore.Ydb/src/Query/Internal/Translators/{YdbDAteTimeMemberTranslator.cs => YdbDateTimeMemberTranslator.cs} (100%) create mode 100644 src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbGuidTypeMapping.cs diff --git a/.github/workflows/slo.yml b/.github/workflows/slo.yml index 254f1a81..1292e24f 100644 --- a/.github/workflows/slo.yml +++ b/.github/workflows/slo.yml @@ -7,19 +7,6 @@ on: pull_request: branches: - main - workflow_dispatch: - inputs: - github_pull_request_number: - required: true - slo_workload_duration_seconds: - default: '600' - required: false - slo_workload_read_max_rps: - default: '1000' - required: false - slo_workload_write_max_rps: - default: '1000' - required: false jobs: ydb-slo-action: @@ -33,6 +20,16 @@ jobs: - AdoNet - Dapper - EF + include: + - workload: AdoNet + read_rps: 1000 + write_rps: 1000 + - workload: Dapper + read_rps: 1000 + write_rps: 1000 + - workload: EF + read_rps: 200 + write_rps: 200 concurrency: group: slo-${{ github.ref }}-${{ matrix.workload }} @@ -66,9 +63,9 @@ jobs: dotnet run run "Host=localhost;Port=2135;Database=/Root/testdb" \ --prom-pgw http://localhost:9091 \ --report-period 250 \ - --time ${{inputs.slo_workload_duration_seconds || 600 }} \ - --read-rps ${{inputs.slo_workload_read_max_rps || 1000 }} \ - --write-rps ${{inputs.slo_workload_write_max_rps || 1000 }} \ + --time 600 \ + --read-rps ${{matrix.read_rps || 1000 }} \ + --write-rps ${{matrix.write_rps || 1000 }} \ --read-timeout 1000 \ --write-timeout 1000 diff --git a/src/EFCore.Ydb/src/Query/Internal/Translators/YdbDAteTimeMemberTranslator.cs b/src/EFCore.Ydb/src/Query/Internal/Translators/YdbDateTimeMemberTranslator.cs similarity index 100% rename from src/EFCore.Ydb/src/Query/Internal/Translators/YdbDAteTimeMemberTranslator.cs rename to src/EFCore.Ydb/src/Query/Internal/Translators/YdbDateTimeMemberTranslator.cs diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs index 31680856..bdd36ece 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore.Storage; namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; @@ -8,9 +7,11 @@ public class YdbDecimalTypeMapping : DecimalTypeMapping private const byte DefaultPrecision = 22; private const byte DefaultScale = 9; - public YdbDecimalTypeMapping(Type? type) : this( + public new static YdbDecimalTypeMapping Default => new(); + + public YdbDecimalTypeMapping() : this( new RelationalTypeMappingParameters( - new CoreTypeMappingParameters(type ?? typeof(decimal)), + new CoreTypeMappingParameters(typeof(decimal)), storeType: "Decimal", dbType: System.Data.DbType.Decimal ) diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbGuidTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbGuidTypeMapping.cs new file mode 100644 index 00000000..613fb89a --- /dev/null +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbGuidTypeMapping.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore.Storage; + +namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; + +public class YdbGuidTypeMapping : GuidTypeMapping +{ + public new static YdbGuidTypeMapping Default => new(); + + private YdbGuidTypeMapping() : base("Uuid", System.Data.DbType.Guid) + { + } + + protected YdbGuidTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) + { + } + + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => + new YdbGuidTypeMapping(parameters); + + protected override string SqlLiteralFormatString => "Uuid('{0}')"; +} diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 0c8fab61..46cea9ed 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -8,5 +8,5 @@ public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies) : ExecutionStrategy(dependencies, maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(10)) // TODO User settings { protected override bool ShouldRetryOn(Exception exception) - => exception is YdbException { IsTransientWhenIdempotent: true }; + => exception is YdbException { IsTransient: true }; } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs index 5e680a30..db2a04d6 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs @@ -30,9 +30,9 @@ RelationalTypeMappingSourceDependencies relationalDependencies private static readonly FloatTypeMapping Float = new("Float", DbType.Single); private static readonly DoubleTypeMapping Double = new("Double", DbType.Double); - private static readonly YdbDecimalTypeMapping Decimal = new(typeof(decimal)); + private static readonly YdbDecimalTypeMapping Decimal = YdbDecimalTypeMapping.Default; - private static readonly GuidTypeMapping Guid = new("Uuid"); + private static readonly GuidTypeMapping Guid = YdbGuidTypeMapping.Default; private static readonly YdbTextTypeMapping Text = YdbTextTypeMapping.Default; private static readonly YdbBytesTypeMapping Bytes = YdbBytesTypeMapping.Default; diff --git a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs index 37b5fcd9..681d3d96 100644 --- a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs +++ b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs @@ -1091,9 +1091,8 @@ public async Task _mockStream.Verify(stream => stream.Current, Times.Exactly(7)); _mockStream.Verify(stream => stream.Write(It.Is(msg => - msg.InitRequest != null && - msg.InitRequest.Consumer == "Consumer" && - msg.InitRequest.TopicsReadSettings[0].Path == "/topic")), Times.Exactly(2)); + msg.InitRequest != null && msg.InitRequest.Consumer == "Consumer" && + msg.InitRequest.TopicsReadSettings[0].Path == "/topic")), Times.Between(2, 3, Range.Inclusive)); _mockStream.Verify(stream => stream.Write(It.Is(msg => msg.ReadRequest != null && msg.ReadRequest.BytesSize == 100)), Times.Exactly(2)); _mockStream.Verify(stream => stream.Write(It.Is(msg => From 40c1f72531edf2fc97e57ba88ca461aac70a3ac9 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 8 Jul 2025 13:52:47 +0300 Subject: [PATCH 12/14] fix linter --- slo/src/EF/SloTableContext.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index c7daf0d7..6200b966 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -82,8 +82,9 @@ int readTimeout ) { await using var dbContext = await client.CreateDbContextAsync(); - return (0, StatusCode.Success, await dbContext.SloEntities.FirstOrDefaultAsync( - table => table.Guid == select.Guid && table.Id == select.Id)); + return (0, StatusCode.Success, + await dbContext.SloEntities.FirstOrDefaultAsync( + table => table.Guid == select.Guid && table.Id == select.Id)); } protected override async Task SelectCount(PooledDbContextFactory client) From dc17326c1cd283b6491a2b9c2ece2044055835ac Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 8 Jul 2025 14:39:58 +0300 Subject: [PATCH 13/14] Update CHANGELOG.md --- .github/workflows/slo.yml | 4 ++-- src/EFCore.Ydb/CHANGELOG.md | 1 + .../src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs | 2 +- src/Ydb.Sdk/src/Ado/YdbConnection.cs | 3 --- src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/slo.yml b/.github/workflows/slo.yml index 1292e24f..873b1fc1 100644 --- a/.github/workflows/slo.yml +++ b/.github/workflows/slo.yml @@ -28,8 +28,8 @@ jobs: read_rps: 1000 write_rps: 1000 - workload: EF - read_rps: 200 - write_rps: 200 + read_rps: 400 + write_rps: 400 concurrency: group: slo-${{ github.ref }}-${{ matrix.workload }} diff --git a/src/EFCore.Ydb/CHANGELOG.md b/src/EFCore.Ydb/CHANGELOG.md index 7d9e48f7..991b3693 100644 --- a/src/EFCore.Ydb/CHANGELOG.md +++ b/src/EFCore.Ydb/CHANGELOG.md @@ -1,3 +1,4 @@ +- Supported Guid (Uuid YDB type). - PrivateAssets="none" is set to flow the EF Core analyzer to users referencing this package [issue](https://github.com/aspnet/EntityFrameworkCore/pull/11350). ## v0.0.2 diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs index bdd36ece..50e03665 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs @@ -8,7 +8,7 @@ public class YdbDecimalTypeMapping : DecimalTypeMapping private const byte DefaultScale = 9; public new static YdbDecimalTypeMapping Default => new(); - + public YdbDecimalTypeMapping() : this( new RelationalTypeMappingParameters( new CoreTypeMappingParameters(typeof(decimal)), diff --git a/src/Ydb.Sdk/src/Ado/YdbConnection.cs b/src/Ydb.Sdk/src/Ado/YdbConnection.cs index 48829691..afadcf1b 100644 --- a/src/Ydb.Sdk/src/Ado/YdbConnection.cs +++ b/src/Ydb.Sdk/src/Ado/YdbConnection.cs @@ -92,15 +92,12 @@ public override async Task OpenAsync(CancellationToken cancellationToken) { ThrowIfConnectionOpen(); - ConnectionState = ConnectionState.Connecting; try { Session = await PoolManager.GetSession(ConnectionStringBuilder, cancellationToken); } catch (Exception e) { - ConnectionState = ConnectionState.Closed; - throw e switch { OperationCanceledException => throw new YdbException(StatusCode.Cancelled, diff --git a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs index 681d3d96..2dc88751 100644 --- a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs +++ b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs @@ -1086,7 +1086,7 @@ public async Task Assert.Equal("Hello", batch.Batch[0].Data); Assert.Equal("World!", batch.Batch[1].Data); - _mockStream.Verify(stream => stream.Write(It.IsAny()), Times.Between(11, 12, Range.Inclusive)); + _mockStream.Verify(stream => stream.Write(It.IsAny()), Times.AtLeast(11)); _mockStream.Verify(stream => stream.MoveNextAsync(), Times.Between(8, 9, Range.Inclusive)); _mockStream.Verify(stream => stream.Current, Times.Exactly(7)); From 1612d3b771dca4d4770ab34b81cdfd30c8f231a7 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 8 Jul 2025 14:53:06 +0300 Subject: [PATCH 14/14] fix linter --- slo/src/EF/SloTableContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 6200b966..a85b35a6 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -83,8 +83,8 @@ int readTimeout { await using var dbContext = await client.CreateDbContextAsync(); return (0, StatusCode.Success, - await dbContext.SloEntities.FirstOrDefaultAsync( - table => table.Guid == select.Guid && table.Id == select.Id)); + await dbContext.SloEntities.FirstOrDefaultAsync(table => + table.Guid == select.Guid && table.Id == select.Id)); } protected override async Task SelectCount(PooledDbContextFactory client)