diff --git a/.github/workflows/slo.yml b/.github/workflows/slo.yml index fc3b424e..873b1fc1 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: @@ -32,7 +19,17 @@ jobs: workload: - AdoNet - Dapper -# - EF + - EF + include: + - workload: AdoNet + read_rps: 1000 + write_rps: 1000 + - workload: Dapper + read_rps: 1000 + write_rps: 1000 + - workload: EF + read_rps: 400 + write_rps: 400 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/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index ad4d265c..a85b35a6 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; @@ -29,9 +31,46 @@ int operationTimeout int writeTimeout ) { - await using var dbContext = await client.CreateDbContextAsync(); - dbContext.SloEntities.Add(sloTable); - await dbContext.SaveChangesAsync(); + await using var context = await client.CreateDbContextAsync(); + 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.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 + }); + }); return (1, StatusCode.Success); } @@ -43,15 +82,14 @@ int readTimeout ) { await using var dbContext = await client.CreateDbContextAsync(); - await dbContext.SloEntities.FindAsync(select.Guid, select.Id); - - return (0, StatusCode.Success, null); + return (0, StatusCode.Success, + await dbContext.SloEntities.FirstOrDefaultAsync(table => + table.Guid == select.Guid && table.Id == select.Id)); } protected override async Task SelectCount(PooledDbContextFactory client) { await using var dbContext = await client.CreateDbContextAsync(); - return await dbContext.SloEntities.CountAsync(); } } \ No newline at end of file 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/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..50e03665 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/YdbTypeMappingSource.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs index 7a745aef..db2a04d6 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs @@ -30,7 +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 = YdbGuidTypeMapping.Default; private static readonly YdbTextTypeMapping Text = YdbTextTypeMapping.Default; private static readonly YdbBytesTypeMapping Bytes = YdbBytesTypeMapping.Default; @@ -64,6 +66,10 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Float", [Float] }, { "Double", [Double] }, + { "Decimal", [Decimal] }, + + { "Guid", [Guid] }, + { "Date", [Date] }, { "DateTime", [DateTime] }, { "Timestamp", [Timestamp] }, @@ -72,8 +78,6 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Text", [Text] }, { "Bytes", [Bytes] }, - { "Decimal", [Decimal] }, - { "Json", [Json] } }; @@ -95,6 +99,8 @@ RelationalTypeMappingSourceDependencies relationalDependencies { typeof(double), Double }, { typeof(decimal), Decimal }, + { typeof(Guid), Guid }, + { typeof(string), Text }, { typeof(byte[]), Bytes }, { typeof(JsonElement), Json }, diff --git a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs index 37b5fcd9..2dc88751 100644 --- a/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs +++ b/src/Ydb.Sdk/tests/Topic/ReaderUnitTests.cs @@ -1086,14 +1086,13 @@ 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)); _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 =>