From e3b12be87d00b6e1054c354aea0e64475ed9c8dd Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Thu, 23 Oct 2025 15:53:27 +0300 Subject: [PATCH 1/5] feat: EFCore supports YdbRetryPolicy --- .../Program.cs | 4 +-- slo/src/EF/SloTableContext.cs | 3 ++- .../Design/Internal/YdbDesignTimeServices.cs | 2 +- .../Internal/YdbOptionsExtension.cs | 15 +---------- .../YdbDbContextOptionsBuilder.cs | 20 +++++++++++--- .../Storage/Internal/YdbExecutionStrategy.cs | 27 ++++++++++++++++--- .../Internal/YdbExecutionStrategyFactory.cs | 5 ++-- .../Internal/YdbRelationalConnection.cs | 10 ++----- .../QueryExpressionInterceptionYdbTestBase.cs | 3 ++- .../TestUtilities/YdbTestStoreFactory.cs | 2 ++ 10 files changed, 54 insertions(+), 37 deletions(-) diff --git a/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs index abce795c..5e2a7322 100644 --- a/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs +++ b/examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs @@ -12,8 +12,8 @@ await Parser.Default.ParseArguments(args).WithParsedAsync(async cmd var options = new DbContextOptionsBuilder() .UseYdb(cmd.ConnectionString, builder => builder - .WithCredentialsProvider(saProvider) - .WithServerCertificates(YcCerts.GetYcServerCertificates()) + .UseCredentialsProvider(saProvider) + .UseServerCertificates(YcCerts.GetYcServerCertificates()) ) .Options; diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 651ef93c..76db0978 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -12,7 +12,8 @@ public class SloTableContext : SloTableContext "EF"; protected override PooledDbContextFactory CreateClient(Config config) => - new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString).Options); + new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString, + builder => builder.EnableRetryIdempotence()).Options); protected override async Task Create( PooledDbContextFactory client, diff --git a/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs b/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs index 09fd6846..7d3f785e 100644 --- a/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs +++ b/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs @@ -10,7 +10,7 @@ public class YdbDesignTimeServices : IDesignTimeServices { public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) { - serviceCollection.AddEntityFrameworkYdb(useYdbExecutionStrategy: false); + serviceCollection.AddEntityFrameworkYdb(); new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection) .TryAdd() diff --git a/src/EFCore.Ydb/src/Infrastructure/Internal/YdbOptionsExtension.cs b/src/EFCore.Ydb/src/Infrastructure/Internal/YdbOptionsExtension.cs index 34c258db..12047dae 100644 --- a/src/EFCore.Ydb/src/Infrastructure/Internal/YdbOptionsExtension.cs +++ b/src/EFCore.Ydb/src/Infrastructure/Internal/YdbOptionsExtension.cs @@ -13,8 +13,6 @@ public class YdbOptionsExtension : RelationalOptionsExtension public X509Certificate2Collection? ServerCertificates { get; private set; } - public bool DisableRetryExecutionStrategy { get; private set; } - private DbContextOptionsExtensionInfo? _info; public YdbOptionsExtension() @@ -25,13 +23,11 @@ private YdbOptionsExtension(YdbOptionsExtension copyFrom) : base(copyFrom) { CredentialsProvider = copyFrom.CredentialsProvider; ServerCertificates = copyFrom.ServerCertificates; - DisableRetryExecutionStrategy = copyFrom.DisableRetryExecutionStrategy; } protected override RelationalOptionsExtension Clone() => new YdbOptionsExtension(this); - public override void ApplyServices(IServiceCollection services) => - services.AddEntityFrameworkYdb(!DisableRetryExecutionStrategy); + public override void ApplyServices(IServiceCollection services) => services.AddEntityFrameworkYdb(); public override DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this); @@ -53,15 +49,6 @@ public YdbOptionsExtension WithServerCertificates(X509Certificate2Collection? se return clone; } - public YdbOptionsExtension DisableRetryOnFailure() - { - var clone = (YdbOptionsExtension)Clone(); - - clone.DisableRetryExecutionStrategy = true; - - return clone; - } - private sealed class ExtensionInfo(YdbOptionsExtension extension) : RelationalExtensionInfo(extension) { public override bool IsDatabaseProvider => true; diff --git a/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs b/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs index 8a35327c..bced86b0 100644 --- a/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs +++ b/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs @@ -1,20 +1,32 @@ using System.Security.Cryptography.X509Certificates; using EntityFrameworkCore.Ydb.Infrastructure.Internal; +using EntityFrameworkCore.Ydb.Storage.Internal; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Ydb.Sdk.Ado.RetryPolicy; using Ydb.Sdk.Auth; namespace EntityFrameworkCore.Ydb.Infrastructure; -public class YdbDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) +public sealed class YdbDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) : RelationalDbContextOptionsBuilder(optionsBuilder) { - public YdbDbContextOptionsBuilder WithCredentialsProvider(ICredentialsProvider? credentialsProvider) => + public YdbDbContextOptionsBuilder UseCredentialsProvider(ICredentialsProvider? credentialsProvider) => WithOption(optionsBuilder => optionsBuilder.WithCredentialsProvider(credentialsProvider)); - public YdbDbContextOptionsBuilder WithServerCertificates(X509Certificate2Collection? serverCertificates) => + public YdbDbContextOptionsBuilder UseServerCertificates(X509Certificate2Collection? serverCertificates) => WithOption(optionsBuilder => optionsBuilder.WithServerCertificates(serverCertificates)); + public YdbDbContextOptionsBuilder EnableRetryIdempotence() + => UseRetryPolicy(new YdbRetryPolicyConfig { EnableRetryIdempotence = true }); + + public YdbDbContextOptionsBuilder UseRetryPolicy(YdbRetryPolicyConfig retryPolicyConfig) + => ExecutionStrategy(d => new YdbExecutionStrategy(d, new YdbRetryPolicy(retryPolicyConfig))); + + public YdbDbContextOptionsBuilder UseRetryPolicy(IRetryPolicy retryPolicy) + => ExecutionStrategy(d => new YdbExecutionStrategy(d, retryPolicy)); + public YdbDbContextOptionsBuilder DisableRetryOnFailure() => - WithOption(optionsBuilder => optionsBuilder.DisableRetryOnFailure()); + ExecutionStrategy(d => new NonRetryingExecutionStrategy(d)); } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 46cea9ed..1af6ba95 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -1,12 +1,31 @@ using System; using Microsoft.EntityFrameworkCore.Storage; using Ydb.Sdk.Ado; +using Ydb.Sdk.Ado.RetryPolicy; namespace EntityFrameworkCore.Ydb.Storage.Internal; -public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies) - : ExecutionStrategy(dependencies, maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(10)) // TODO User settings +/// +/// Retry strategy for YDB. +/// +/// IMPORTANT: +///
- The maximum number of attempts and backoff logic are encapsulated in . +/// The base ExecutionStrategy parameters (maxRetryCount, maxRetryDelay) are not used. +///
- This strategy must be invoked in the correct EF Core context/connection (YDB), +/// so that exception types and ShouldRetryOn semantics match the provider. +///
- This base is a good place to emit metrics/logs (attempt number, delay, exception type, etc.). +///
+public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, IRetryPolicy retryPolicy) +// We pass "placeholders" to the base class: +// - int.MaxValue and TimeSpan.Zero are not used in the real retry logic. +// - Actual limits/delays are driven by IRetryPolicy. + : ExecutionStrategy(dependencies, int.MaxValue /* unused! */, TimeSpan.Zero /* unused! */) { - protected override bool ShouldRetryOn(Exception exception) - => exception is YdbException { IsTransient: true }; + public override bool RetriesOnFailure => true; + + protected override bool ShouldRetryOn(Exception exception) => exception is YdbException; + + protected override TimeSpan? GetNextDelay(Exception lastException) => lastException is YdbException ydbException + ? retryPolicy.GetNextDelay(ydbException, ExceptionsEncountered.Count - 1) + : null; } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs index d9c3a1f2..f94deaa3 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs @@ -1,10 +1,11 @@ using Microsoft.EntityFrameworkCore.Storage; +using Ydb.Sdk.Ado.RetryPolicy; namespace EntityFrameworkCore.Ydb.Storage.Internal; public class YdbExecutionStrategyFactory(ExecutionStrategyDependencies dependencies) : RelationalExecutionStrategyFactory(dependencies) { - protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies) - => new YdbExecutionStrategy(dependencies); + protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies) => + new YdbExecutionStrategy(dependencies, YdbRetryPolicy.Default); } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs index 23990cfe..91f3423c 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs @@ -13,7 +13,6 @@ public class YdbRelationalConnection : RelationalConnection, IYdbRelationalConne { private readonly ICredentialsProvider? _credentialsProvider; private readonly X509Certificate2Collection? _serverCertificates; - private readonly bool _disableRetryExecuteStrategy; public YdbRelationalConnection(RelationalConnectionDependencies dependencies) : base(dependencies) { @@ -22,7 +21,6 @@ public YdbRelationalConnection(RelationalConnectionDependencies dependencies) : _credentialsProvider = ydbOptionsExtension.CredentialsProvider; _serverCertificates = ydbOptionsExtension.ServerCertificates; - _disableRetryExecuteStrategy = ydbOptionsExtension.DisableRetryExecutionStrategy; } protected override DbConnection CreateDbConnection() @@ -40,12 +38,8 @@ public IYdbRelationalConnection Clone() { var options = new DbContextOptionsBuilder().UseYdb(GetValidatedConnectionString(), builder => { - builder.WithCredentialsProvider(_credentialsProvider); - builder.WithServerCertificates(_serverCertificates); - if (_disableRetryExecuteStrategy) - { - builder.DisableRetryOnFailure(); - } + builder.UseCredentialsProvider(_credentialsProvider); + builder.UseServerCertificates(_serverCertificates); }).Options; return new YdbRelationalConnection(Dependencies with { ContextOptions = options }); diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs index f9869944..30261346 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; using Xunit; +using Ydb.Sdk.Ado.RetryPolicy; namespace EntityFrameworkCore.Ydb.FunctionalTests; @@ -28,7 +29,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build { new YdbDbContextOptionsBuilder(base.AddOptions(builder)) #pragma warning disable EF1001 - .ExecutionStrategy(d => new YdbExecutionStrategy(d)); + .ExecutionStrategy(d => new YdbExecutionStrategy(d, YdbRetryPolicy.Default)); #pragma warning restore EF1001 return builder; } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs index 72a0942f..176f5305 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs @@ -1,4 +1,6 @@ using EntityFrameworkCore.Ydb.Extensions; +using EntityFrameworkCore.Ydb.Storage.Internal; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; From b2b354930b9a2c684a5380b05fb721d5e7fa509e Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Thu, 23 Oct 2025 16:12:52 +0300 Subject: [PATCH 2/5] Update CHANGELOG.md --- src/EFCore.Ydb/CHANGELOG.md | 5 +++++ src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Ydb/CHANGELOG.md b/src/EFCore.Ydb/CHANGELOG.md index fab05259..2c26113d 100644 --- a/src/EFCore.Ydb/CHANGELOG.md +++ b/src/EFCore.Ydb/CHANGELOG.md @@ -1,3 +1,8 @@ +- Added support for the YDB retry policy (ADO.NET) and new configuration methods in `YdbDbContextOptionsBuilder`: + - `EnableRetryIdempotence()`: enables retries for errors classified as idempotent. You must ensure the operation itself is idempotent. + - `UseRetryPolicy(YdbRetryPolicyConfig retryPolicyConfig)`: configures custom backoff parameters and the maximum number of retry attempts. + - `UseRetryPolicy(IRetryPolicy retryPolicy)`: advanced option giving full control over retry behavior. The provider does not manage the attempt counter; your implementation must handle it. + ## v0.1.0 - Fixed bug: incompatible coalesce types ([#531](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/531)). diff --git a/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs b/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs index 7d3f785e..09fd6846 100644 --- a/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs +++ b/src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs @@ -10,7 +10,7 @@ public class YdbDesignTimeServices : IDesignTimeServices { public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) { - serviceCollection.AddEntityFrameworkYdb(); + serviceCollection.AddEntityFrameworkYdb(useYdbExecutionStrategy: false); new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection) .TryAdd() From 2f3bfe0dc66f8c5d01c95a155e8464c7a321f212 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Thu, 23 Oct 2025 18:18:34 +0300 Subject: [PATCH 3/5] fix tests --- src/EFCore.Ydb/CHANGELOG.md | 1 - .../YdbDbContextOptionsBuilder.cs | 5 +---- .../Storage/Internal/YdbExecutionStrategy.cs | 21 ++++++++++++------- .../Internal/YdbExecutionStrategyFactory.cs | 2 +- .../QueryExpressionInterceptionYdbTestBase.cs | 2 +- .../src/Ado/RetryPolicy/YdbRetryPolicy.cs | 2 +- .../RetryPolicy/YdbRetryPolicyTests.cs | 1 + 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/EFCore.Ydb/CHANGELOG.md b/src/EFCore.Ydb/CHANGELOG.md index 2c26113d..3b4ec968 100644 --- a/src/EFCore.Ydb/CHANGELOG.md +++ b/src/EFCore.Ydb/CHANGELOG.md @@ -1,7 +1,6 @@ - Added support for the YDB retry policy (ADO.NET) and new configuration methods in `YdbDbContextOptionsBuilder`: - `EnableRetryIdempotence()`: enables retries for errors classified as idempotent. You must ensure the operation itself is idempotent. - `UseRetryPolicy(YdbRetryPolicyConfig retryPolicyConfig)`: configures custom backoff parameters and the maximum number of retry attempts. - - `UseRetryPolicy(IRetryPolicy retryPolicy)`: advanced option giving full control over retry behavior. The provider does not manage the attempt counter; your implementation must handle it. ## v0.1.0 diff --git a/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs b/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs index bced86b0..b94bac18 100644 --- a/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs +++ b/src/EFCore.Ydb/src/Infrastructure/YdbDbContextOptionsBuilder.cs @@ -22,10 +22,7 @@ public YdbDbContextOptionsBuilder EnableRetryIdempotence() => UseRetryPolicy(new YdbRetryPolicyConfig { EnableRetryIdempotence = true }); public YdbDbContextOptionsBuilder UseRetryPolicy(YdbRetryPolicyConfig retryPolicyConfig) - => ExecutionStrategy(d => new YdbExecutionStrategy(d, new YdbRetryPolicy(retryPolicyConfig))); - - public YdbDbContextOptionsBuilder UseRetryPolicy(IRetryPolicy retryPolicy) - => ExecutionStrategy(d => new YdbExecutionStrategy(d, retryPolicy)); + => ExecutionStrategy(d => new YdbExecutionStrategy(d, retryPolicyConfig)); public YdbDbContextOptionsBuilder DisableRetryOnFailure() => ExecutionStrategy(d => new NonRetryingExecutionStrategy(d)); diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 1af6ba95..2aaf336d 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Storage; +using Ydb.Sdk; using Ydb.Sdk.Ado; using Ydb.Sdk.Ado.RetryPolicy; @@ -15,17 +16,23 @@ namespace EntityFrameworkCore.Ydb.Storage.Internal; /// so that exception types and ShouldRetryOn semantics match the provider. ///
- This base is a good place to emit metrics/logs (attempt number, delay, exception type, etc.). /// -public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, IRetryPolicy retryPolicy) +public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, YdbRetryPolicyConfig retryPolicyConfig) // We pass "placeholders" to the base class: -// - int.MaxValue and TimeSpan.Zero are not used in the real retry logic. +// - TimeSpan.Zero is not used in the real retry logic. // - Actual limits/delays are driven by IRetryPolicy. - : ExecutionStrategy(dependencies, int.MaxValue /* unused! */, TimeSpan.Zero /* unused! */) + : ExecutionStrategy(dependencies, retryPolicyConfig.MaxAttempts, TimeSpan.Zero /* unused! */) { + private readonly YdbRetryPolicy _retryPolicy = new(retryPolicyConfig); + public override bool RetriesOnFailure => true; - protected override bool ShouldRetryOn(Exception exception) => exception is YdbException; + protected override bool ShouldRetryOn(Exception exception) => + exception is YdbException ydbException && + (ydbException.IsTransient || retryPolicyConfig.EnableRetryIdempotence && ydbException.Code is + StatusCode.ClientTransportUnknown or + StatusCode.ClientTransportUnavailable or + StatusCode.Undetermined); - protected override TimeSpan? GetNextDelay(Exception lastException) => lastException is YdbException ydbException - ? retryPolicy.GetNextDelay(ydbException, ExceptionsEncountered.Count - 1) - : null; + protected override TimeSpan? GetNextDelay(Exception lastException) => + _retryPolicy.GetNextDelay((YdbException)lastException, ExceptionsEncountered.Count - 1); } diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs index f94deaa3..40acc7a6 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategyFactory.cs @@ -7,5 +7,5 @@ public class YdbExecutionStrategyFactory(ExecutionStrategyDependencies dependenc : RelationalExecutionStrategyFactory(dependencies) { protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies) => - new YdbExecutionStrategy(dependencies, YdbRetryPolicy.Default); + new YdbExecutionStrategy(dependencies, YdbRetryPolicyConfig.Default); } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs index 30261346..36e571ac 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs @@ -29,7 +29,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build { new YdbDbContextOptionsBuilder(base.AddOptions(builder)) #pragma warning disable EF1001 - .ExecutionStrategy(d => new YdbExecutionStrategy(d, YdbRetryPolicy.Default)); + .ExecutionStrategy(d => new YdbExecutionStrategy(d, YdbRetryPolicyConfig.Default)); #pragma warning restore EF1001 return builder; } diff --git a/src/Ydb.Sdk/src/Ado/RetryPolicy/YdbRetryPolicy.cs b/src/Ydb.Sdk/src/Ado/RetryPolicy/YdbRetryPolicy.cs index 728ce873..e35fe92e 100644 --- a/src/Ydb.Sdk/src/Ado/RetryPolicy/YdbRetryPolicy.cs +++ b/src/Ydb.Sdk/src/Ado/RetryPolicy/YdbRetryPolicy.cs @@ -85,7 +85,7 @@ internal YdbRetryPolicy(YdbRetryPolicyConfig config, IRandom random) : this(conf return ydbException.Code switch { - StatusCode.BadSession or StatusCode.SessionBusy => TimeSpan.Zero, + StatusCode.BadSession or StatusCode.SessionBusy or StatusCode.SessionExpired => TimeSpan.Zero, StatusCode.Aborted or StatusCode.Undetermined => FullJitter(_fastBackoffBaseMs, _fastCapBackoffMs, _fastCeiling, attempt, _random), StatusCode.Unavailable or StatusCode.ClientTransportUnknown or StatusCode.ClientTransportUnavailable => diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/RetryPolicy/YdbRetryPolicyTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/RetryPolicy/YdbRetryPolicyTests.cs index fd411445..06ac64d5 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/RetryPolicy/YdbRetryPolicyTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/RetryPolicy/YdbRetryPolicyTests.cs @@ -10,6 +10,7 @@ public class YdbRetryPolicyTests [Theory] [InlineData(StatusCode.BadSession)] [InlineData(StatusCode.SessionBusy)] + [InlineData(StatusCode.SessionExpired)] public void GetNextDelay_WhenStatusIsBadSessionOrBusySession_ReturnTimeSpanZero(StatusCode statusCode) { var ydbRetryPolicy = new YdbRetryPolicy(new YdbRetryPolicyConfig { MaxAttempts = 2 }); From e59b1803e88e5301f8dbb4064e9c11075d3cc1a0 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Thu, 23 Oct 2025 18:29:43 +0300 Subject: [PATCH 4/5] fix linter and comments --- slo/src/EF/SloTableContext.cs | 2 +- .../src/Storage/Internal/YdbExecutionStrategy.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/slo/src/EF/SloTableContext.cs b/slo/src/EF/SloTableContext.cs index 76db0978..fc220ac0 100644 --- a/slo/src/EF/SloTableContext.cs +++ b/slo/src/EF/SloTableContext.cs @@ -12,7 +12,7 @@ public class SloTableContext : SloTableContext "EF"; protected override PooledDbContextFactory CreateClient(Config config) => - new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString, + new(new DbContextOptionsBuilder().UseYdb(config.ConnectionString, builder => builder.EnableRetryIdempotence()).Options); protected override async Task Create( diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 2aaf336d..344546e4 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -7,19 +7,19 @@ namespace EntityFrameworkCore.Ydb.Storage.Internal; /// -/// Retry strategy for YDB. +/// Retry strategy for YDB.
/// -/// IMPORTANT: -///
- The maximum number of attempts and backoff logic are encapsulated in . +///
IMPORTANT: +///
- The maximum number of attempts and backoff logic are encapsulated in . /// The base ExecutionStrategy parameters (maxRetryCount, maxRetryDelay) are not used. ///
- This strategy must be invoked in the correct EF Core context/connection (YDB), /// so that exception types and ShouldRetryOn semantics match the provider. -///
- This base is a good place to emit metrics/logs (attempt number, delay, exception type, etc.). +///
- The base is a good place to emit metrics/logs (attempt number, delay, exception type, etc.). ///
public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, YdbRetryPolicyConfig retryPolicyConfig) // We pass "placeholders" to the base class: -// - TimeSpan.Zero is not used in the real retry logic. -// - Actual limits/delays are driven by IRetryPolicy. +// - MaxAttempts and TimeSpan.Zero are not used in the real retry logic. +// - Actual limits/delays are driven by YdbRetryPolicy. : ExecutionStrategy(dependencies, retryPolicyConfig.MaxAttempts, TimeSpan.Zero /* unused! */) { private readonly YdbRetryPolicy _retryPolicy = new(retryPolicyConfig); From c74f3ec6baaac5d898d05c9085003396dfab2dd3 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Thu, 23 Oct 2025 19:34:33 +0300 Subject: [PATCH 5/5] fix linter --- src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs | 4 ++-- .../TestUtilities/YdbTestStoreFactory.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs index 344546e4..d88a4634 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbExecutionStrategy.cs @@ -28,10 +28,10 @@ public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, Yd protected override bool ShouldRetryOn(Exception exception) => exception is YdbException ydbException && - (ydbException.IsTransient || retryPolicyConfig.EnableRetryIdempotence && ydbException.Code is + (ydbException.IsTransient || (retryPolicyConfig.EnableRetryIdempotence && ydbException.Code is StatusCode.ClientTransportUnknown or StatusCode.ClientTransportUnavailable or - StatusCode.Undetermined); + StatusCode.Undetermined)); protected override TimeSpan? GetNextDelay(Exception lastException) => _retryPolicy.GetNextDelay((YdbException)lastException, ExceptionsEncountered.Count - 1); diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs index 176f5305..72a0942f 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs @@ -1,6 +1,4 @@ using EntityFrameworkCore.Ydb.Extensions; -using EntityFrameworkCore.Ydb.Storage.Internal; -using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection;