Skip to content

Commit e3b12be

Browse files
feat: EFCore supports YdbRetryPolicy
1 parent cadf257 commit e3b12be

File tree

10 files changed

+54
-37
lines changed

10 files changed

+54
-37
lines changed

examples/EntityFrameworkCore.Ydb.Yandex.Cloud/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ await Parser.Default.ParseArguments<CmdOptions>(args).WithParsedAsync(async cmd
1212

1313
var options = new DbContextOptionsBuilder<AppDbContext>()
1414
.UseYdb(cmd.ConnectionString, builder => builder
15-
.WithCredentialsProvider(saProvider)
16-
.WithServerCertificates(YcCerts.GetYcServerCertificates())
15+
.UseCredentialsProvider(saProvider)
16+
.UseServerCertificates(YcCerts.GetYcServerCertificates())
1717
)
1818
.Options;
1919

slo/src/EF/SloTableContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ public class SloTableContext : SloTableContext<PooledDbContextFactory<TableDbCon
1212
protected override string Job => "EF";
1313

1414
protected override PooledDbContextFactory<TableDbContext> CreateClient(Config config) =>
15-
new(new DbContextOptionsBuilder<TableDbContext>().UseYdb(config.ConnectionString).Options);
15+
new(new DbContextOptionsBuilder<TableDbContext>().UseYdb(config.ConnectionString,
16+
builder => builder.EnableRetryIdempotence()).Options);
1617

1718
protected override async Task Create(
1819
PooledDbContextFactory<TableDbContext> client,

src/EFCore.Ydb/src/Design/Internal/YdbDesignTimeServices.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class YdbDesignTimeServices : IDesignTimeServices
1010
{
1111
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
1212
{
13-
serviceCollection.AddEntityFrameworkYdb(useYdbExecutionStrategy: false);
13+
serviceCollection.AddEntityFrameworkYdb();
1414

1515
new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection)
1616
.TryAdd<IDatabaseModelFactory, YdbDatabaseModelFactory>()

src/EFCore.Ydb/src/Infrastructure/Internal/YdbOptionsExtension.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public class YdbOptionsExtension : RelationalOptionsExtension
1313

1414
public X509Certificate2Collection? ServerCertificates { get; private set; }
1515

16-
public bool DisableRetryExecutionStrategy { get; private set; }
17-
1816
private DbContextOptionsExtensionInfo? _info;
1917

2018
public YdbOptionsExtension()
@@ -25,13 +23,11 @@ private YdbOptionsExtension(YdbOptionsExtension copyFrom) : base(copyFrom)
2523
{
2624
CredentialsProvider = copyFrom.CredentialsProvider;
2725
ServerCertificates = copyFrom.ServerCertificates;
28-
DisableRetryExecutionStrategy = copyFrom.DisableRetryExecutionStrategy;
2926
}
3027

3128
protected override RelationalOptionsExtension Clone() => new YdbOptionsExtension(this);
3229

33-
public override void ApplyServices(IServiceCollection services) =>
34-
services.AddEntityFrameworkYdb(!DisableRetryExecutionStrategy);
30+
public override void ApplyServices(IServiceCollection services) => services.AddEntityFrameworkYdb();
3531

3632
public override DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
3733

@@ -53,15 +49,6 @@ public YdbOptionsExtension WithServerCertificates(X509Certificate2Collection? se
5349
return clone;
5450
}
5551

56-
public YdbOptionsExtension DisableRetryOnFailure()
57-
{
58-
var clone = (YdbOptionsExtension)Clone();
59-
60-
clone.DisableRetryExecutionStrategy = true;
61-
62-
return clone;
63-
}
64-
6552
private sealed class ExtensionInfo(YdbOptionsExtension extension) : RelationalExtensionInfo(extension)
6653
{
6754
public override bool IsDatabaseProvider => true;
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
using System.Security.Cryptography.X509Certificates;
22
using EntityFrameworkCore.Ydb.Infrastructure.Internal;
3+
using EntityFrameworkCore.Ydb.Storage.Internal;
34
using Microsoft.EntityFrameworkCore;
45
using Microsoft.EntityFrameworkCore.Infrastructure;
6+
using Microsoft.EntityFrameworkCore.Storage;
7+
using Ydb.Sdk.Ado.RetryPolicy;
58
using Ydb.Sdk.Auth;
69

710
namespace EntityFrameworkCore.Ydb.Infrastructure;
811

9-
public class YdbDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
12+
public sealed class YdbDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
1013
: RelationalDbContextOptionsBuilder<YdbDbContextOptionsBuilder, YdbOptionsExtension>(optionsBuilder)
1114
{
12-
public YdbDbContextOptionsBuilder WithCredentialsProvider(ICredentialsProvider? credentialsProvider) =>
15+
public YdbDbContextOptionsBuilder UseCredentialsProvider(ICredentialsProvider? credentialsProvider) =>
1316
WithOption(optionsBuilder => optionsBuilder.WithCredentialsProvider(credentialsProvider));
1417

15-
public YdbDbContextOptionsBuilder WithServerCertificates(X509Certificate2Collection? serverCertificates) =>
18+
public YdbDbContextOptionsBuilder UseServerCertificates(X509Certificate2Collection? serverCertificates) =>
1619
WithOption(optionsBuilder => optionsBuilder.WithServerCertificates(serverCertificates));
1720

21+
public YdbDbContextOptionsBuilder EnableRetryIdempotence()
22+
=> UseRetryPolicy(new YdbRetryPolicyConfig { EnableRetryIdempotence = true });
23+
24+
public YdbDbContextOptionsBuilder UseRetryPolicy(YdbRetryPolicyConfig retryPolicyConfig)
25+
=> ExecutionStrategy(d => new YdbExecutionStrategy(d, new YdbRetryPolicy(retryPolicyConfig)));
26+
27+
public YdbDbContextOptionsBuilder UseRetryPolicy(IRetryPolicy retryPolicy)
28+
=> ExecutionStrategy(d => new YdbExecutionStrategy(d, retryPolicy));
29+
1830
public YdbDbContextOptionsBuilder DisableRetryOnFailure() =>
19-
WithOption(optionsBuilder => optionsBuilder.DisableRetryOnFailure());
31+
ExecutionStrategy(d => new NonRetryingExecutionStrategy(d));
2032
}
Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
using System;
22
using Microsoft.EntityFrameworkCore.Storage;
33
using Ydb.Sdk.Ado;
4+
using Ydb.Sdk.Ado.RetryPolicy;
45

56
namespace EntityFrameworkCore.Ydb.Storage.Internal;
67

7-
public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies)
8-
: ExecutionStrategy(dependencies, maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(10)) // TODO User settings
8+
/// <summary>
9+
/// Retry strategy for YDB.
10+
///
11+
/// IMPORTANT:
12+
/// <br/>- The maximum number of attempts and backoff logic are encapsulated in <see cref="IRetryPolicy"/>.
13+
/// The base ExecutionStrategy parameters (maxRetryCount, maxRetryDelay) are not used.
14+
/// <br/>- This strategy must be invoked in the correct EF Core context/connection (YDB),
15+
/// so that exception types and ShouldRetryOn semantics match the provider.
16+
/// <br/>- This base <see cref="ExecutionStrategy"/> is a good place to emit metrics/logs (attempt number, delay, exception type, etc.).
17+
/// </summary>
18+
public class YdbExecutionStrategy(ExecutionStrategyDependencies dependencies, IRetryPolicy retryPolicy)
19+
// We pass "placeholders" to the base class:
20+
// - int.MaxValue and TimeSpan.Zero are not used in the real retry logic.
21+
// - Actual limits/delays are driven by IRetryPolicy.
22+
: ExecutionStrategy(dependencies, int.MaxValue /* unused! */, TimeSpan.Zero /* unused! */)
923
{
10-
protected override bool ShouldRetryOn(Exception exception)
11-
=> exception is YdbException { IsTransient: true };
24+
public override bool RetriesOnFailure => true;
25+
26+
protected override bool ShouldRetryOn(Exception exception) => exception is YdbException;
27+
28+
protected override TimeSpan? GetNextDelay(Exception lastException) => lastException is YdbException ydbException
29+
? retryPolicy.GetNextDelay(ydbException, ExceptionsEncountered.Count - 1)
30+
: null;
1231
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using Microsoft.EntityFrameworkCore.Storage;
2+
using Ydb.Sdk.Ado.RetryPolicy;
23

34
namespace EntityFrameworkCore.Ydb.Storage.Internal;
45

56
public class YdbExecutionStrategyFactory(ExecutionStrategyDependencies dependencies)
67
: RelationalExecutionStrategyFactory(dependencies)
78
{
8-
protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies)
9-
=> new YdbExecutionStrategy(dependencies);
9+
protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies) =>
10+
new YdbExecutionStrategy(dependencies, YdbRetryPolicy.Default);
1011
}

src/EFCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public class YdbRelationalConnection : RelationalConnection, IYdbRelationalConne
1313
{
1414
private readonly ICredentialsProvider? _credentialsProvider;
1515
private readonly X509Certificate2Collection? _serverCertificates;
16-
private readonly bool _disableRetryExecuteStrategy;
1716

1817
public YdbRelationalConnection(RelationalConnectionDependencies dependencies) : base(dependencies)
1918
{
@@ -22,7 +21,6 @@ public YdbRelationalConnection(RelationalConnectionDependencies dependencies) :
2221

2322
_credentialsProvider = ydbOptionsExtension.CredentialsProvider;
2423
_serverCertificates = ydbOptionsExtension.ServerCertificates;
25-
_disableRetryExecuteStrategy = ydbOptionsExtension.DisableRetryExecutionStrategy;
2624
}
2725

2826
protected override DbConnection CreateDbConnection()
@@ -40,12 +38,8 @@ public IYdbRelationalConnection Clone()
4038
{
4139
var options = new DbContextOptionsBuilder().UseYdb(GetValidatedConnectionString(), builder =>
4240
{
43-
builder.WithCredentialsProvider(_credentialsProvider);
44-
builder.WithServerCertificates(_serverCertificates);
45-
if (_disableRetryExecuteStrategy)
46-
{
47-
builder.DisableRetryOnFailure();
48-
}
41+
builder.UseCredentialsProvider(_credentialsProvider);
42+
builder.UseServerCertificates(_serverCertificates);
4943
}).Options;
5044

5145
return new YdbRelationalConnection(Dependencies with { ContextOptions = options });

src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/QueryExpressionInterceptionYdbTestBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.EntityFrameworkCore.TestUtilities;
88
using Microsoft.Extensions.DependencyInjection;
99
using Xunit;
10+
using Ydb.Sdk.Ado.RetryPolicy;
1011

1112
namespace EntityFrameworkCore.Ydb.FunctionalTests;
1213

@@ -28,7 +29,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
2829
{
2930
new YdbDbContextOptionsBuilder(base.AddOptions(builder))
3031
#pragma warning disable EF1001
31-
.ExecutionStrategy(d => new YdbExecutionStrategy(d));
32+
.ExecutionStrategy(d => new YdbExecutionStrategy(d, YdbRetryPolicy.Default));
3233
#pragma warning restore EF1001
3334
return builder;
3435
}

src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/TestUtilities/YdbTestStoreFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using EntityFrameworkCore.Ydb.Extensions;
2+
using EntityFrameworkCore.Ydb.Storage.Internal;
3+
using Microsoft.EntityFrameworkCore.Storage;
24
using Microsoft.EntityFrameworkCore.TestUtilities;
35
using Microsoft.Extensions.DependencyInjection;
46

0 commit comments

Comments
 (0)