Skip to content

Commit

Permalink
Add connection resiliency support for Sql Server.
Browse files Browse the repository at this point in the history
All high-level API that need database connection will use the configured execution strategy except migrations.
Handle external connection state changes in RelationalConnection

Add to .Core:
  IExecutionStrategy
  ExecutionStrategy
  IExecutionStrategyFactory
  IDatabaseProviderServices
    IExecutionStrategyFactory ExecutionStrategyFactory
  DatabaseFacade
    IExecutionStrategy CreateExecutionStrategy()
    TResult ExecuteWithStrategyInTransaction<TResult>(Func<DbContext, TResult> operation)
    Task<TResult> ExecuteWithStrategyInTransactionAsync<TResult>(Func<DbContext, CancellationToken, Task<TResult>> operation, CancellationToken cancellationToken)

Add to .Relational:
  RelationalDbContextOptionsBuilder
    ExecutionStrategy(Func<ExecutionStrategyContext, IExecutionStrategy> getExecutionStrategy)

Add to .SqlServer:
  SqlAzureExecutionStrategy
  SqlServerDbContextOptionsBuilder
    EnableRetryOnFailure()
    EnableRetryOnFailure(int maxRetryCount)
  • Loading branch information
AndriySvyryd committed Sep 27, 2016
1 parent 8e9315b commit 48cb312
Show file tree
Hide file tree
Showing 57 changed files with 2,522 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(

public virtual IDbContextTransaction CurrentTransaction => null;

private void LogWarning()
protected virtual void LogWarning()
{
_logger.LogWarning(
InMemoryEventId.TransactionIgnoredWarning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Infrastructure
{
Expand Down Expand Up @@ -95,6 +97,14 @@ public virtual TBuilder MigrationsHistoryTable([NotNull] string tableName, [CanB
public virtual TBuilder UseRelationalNulls()
=> SetOption(e => e.UseRelationalNulls = true);

/// <summary>
/// Configures the context to use the provided <see cref="IExecutionStrategy" />.
/// </summary>
/// <param name="getExecutionStrategy"> A function that returns a new instance of an execution strategy. </param>
public virtual TBuilder ExecutionStrategy(
[NotNull] Func<ExecutionStrategyContext, IExecutionStrategy> getExecutionStrategy)
=> SetOption(e => e.ExecutionStrategyFactory = Check.NotNull(getExecutionStrategy, nameof(getExecutionStrategy)));

/// <summary>
/// Sets an option by cloning the extension used to store the settings. This ensures the builder
/// does not modify options that are already in use elsewhere.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -21,6 +22,7 @@ public abstract class RelationalOptionsExtension : IDbContextOptionsExtension
private string _migrationsAssembly;
private string _migrationsHistoryTableName;
private string _migrationsHistoryTableSchema;
private Func<ExecutionStrategyContext, IExecutionStrategy> _executionStrategyFactory;

protected RelationalOptionsExtension()
{
Expand All @@ -40,6 +42,7 @@ protected RelationalOptionsExtension([NotNull] RelationalOptionsExtension copyFr
_migrationsAssembly = copyFrom._migrationsAssembly;
_migrationsHistoryTableName = copyFrom._migrationsHistoryTableName;
_migrationsHistoryTableSchema = copyFrom._migrationsHistoryTableSchema;
_executionStrategyFactory = copyFrom._executionStrategyFactory;
}

public virtual string ConnectionString
Expand Down Expand Up @@ -122,6 +125,12 @@ public virtual string MigrationsHistoryTableSchema
[param: CanBeNull] set { _migrationsHistoryTableSchema = value; }
}

public virtual Func<ExecutionStrategyContext, IExecutionStrategy> ExecutionStrategyFactory
{
get { return _executionStrategyFactory; }
[param: CanBeNull] set { _executionStrategyFactory = value; }
}

public static RelationalOptionsExtension Extract([NotNull] IDbContextOptions options)
{
Check.NotNull(options, nameof(options));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
Expand Down Expand Up @@ -73,6 +72,7 @@ public static IServiceCollection AddRelational([NotNull] this IServiceCollection
.AddScoped<CommandBatchPreparer>()
.AddScoped<IMigrationsModelDiffer, MigrationsModelDiffer>()
.AddScoped<MigrationsSqlGenerator>()
.AddScoped<RelationalExecutionStrategyFactory>()
.AddScoped(p => GetProviderServices(p).ParameterNameGeneratorFactory)
.AddScoped(p => GetProviderServices(p).SqlGenerationHelper)
.AddScoped(p => GetProviderServices(p).CompositeMethodCallTranslator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@
<Compile Include="Storage\Internal\RelationalCommand.cs" />
<Compile Include="Storage\Internal\RelationalCommandBuilder.cs" />
<Compile Include="Storage\Internal\RelationalCommandBuilderFactory.cs" />
<Compile Include="Storage\Internal\RelationalExecutionStrategyFactory.cs" />
<Compile Include="Storage\Internal\RelationalLoggerExtensions.cs" />
<Compile Include="Storage\Internal\RelationalParameterBuilder.cs" />
<Compile Include="Storage\Internal\RemappingUntypedRelationalValueBufferFactory.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,38 +71,8 @@ public async Task<bool> MoveNext(CancellationToken cancellationToken)

if (_buffer == null)
{
if (_dataReader == null)
{
await _queryingEnumerable._relationalQueryContext.Connection
.OpenAsync(cancellationToken);

var relationalCommand
= _queryingEnumerable._shaperCommandContext
.GetRelationalCommand(_queryingEnumerable._relationalQueryContext.ParameterValues);

await _queryingEnumerable._relationalQueryContext
.RegisterValueBufferCursorAsync(this, _queryingEnumerable._queryIndex, cancellationToken);

_dataReader
= await relationalCommand.ExecuteReaderAsync(
_queryingEnumerable._relationalQueryContext.Connection,
_queryingEnumerable._relationalQueryContext.ParameterValues,
manageConnection: false,
cancellationToken: cancellationToken);

_dbDataReader = _dataReader.DbDataReader;
_queryingEnumerable._shaperCommandContext.NotifyReaderCreated(_dbDataReader);
_valueBufferFactory = _queryingEnumerable._shaperCommandContext.ValueBufferFactory;
}

var hasNext = await _dbDataReader.ReadAsync(cancellationToken);

_current
= hasNext
? _valueBufferFactory.Create(_dbDataReader)
: default(ValueBuffer);

return hasNext;
var executionStrategy = _queryingEnumerable._relationalQueryContext.ExecutionStrategyFactory.Create();
return await executionStrategy.ExecuteAsync(BufferlessMoveNext, executionStrategy.RetriesOnFailure, cancellationToken);
}

if (_buffer.Count > 0)
Expand All @@ -120,6 +90,58 @@ await _queryingEnumerable._relationalQueryContext
}
}

private async Task<bool> BufferlessMoveNext(bool buffer, CancellationToken cancellationToken)
{
try
{
if (_dataReader == null)
{
await _queryingEnumerable._relationalQueryContext.Connection
.OpenAsync(cancellationToken);

var relationalCommand
= _queryingEnumerable._shaperCommandContext
.GetRelationalCommand(_queryingEnumerable._relationalQueryContext.ParameterValues);

await _queryingEnumerable._relationalQueryContext
.RegisterValueBufferCursorAsync(this, _queryingEnumerable._queryIndex, cancellationToken);

_dataReader
= await relationalCommand.ExecuteReaderAsync(
_queryingEnumerable._relationalQueryContext.Connection,
_queryingEnumerable._relationalQueryContext.ParameterValues,
manageConnection: false,
cancellationToken: cancellationToken);

_dbDataReader = _dataReader.DbDataReader;
_queryingEnumerable._shaperCommandContext.NotifyReaderCreated(_dbDataReader);
_valueBufferFactory = _queryingEnumerable._shaperCommandContext.ValueBufferFactory;
}

var hasNext = await _dbDataReader.ReadAsync(cancellationToken);

_current
= hasNext
? _valueBufferFactory.Create(_dbDataReader)
: default(ValueBuffer);

if (buffer)
{
await BufferAllAsync(cancellationToken);
}

return hasNext;
}
catch (Exception)
{
_queryingEnumerable._relationalQueryContext.DeregisterValueBufferCursor(this);
_dataReader = null;
_dbDataReader = null;

throw;
}
}

// ReSharper disable once ConvertToAutoPropertyWithPrivateSetter
public ValueBuffer Current => _current;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ public Enumerator(QueryingEnumerable queryingEnumerable)
public bool MoveNext()
{
if (_buffer == null)
{
var executionStrategy = _queryingEnumerable._relationalQueryContext.ExecutionStrategyFactory.Create();
return executionStrategy.Execute(BufferlessMoveNext, executionStrategy.RetriesOnFailure);
}

if (_buffer.Count > 0)
{
_current = _buffer.Dequeue();

return true;
}

return false;
}

private bool BufferlessMoveNext(bool buffer)
{
try
{
if (_dataReader == null)
{
Expand Down Expand Up @@ -96,17 +114,21 @@ var relationalCommand
? _valueBufferFactory.Create(_dbDataReader)
: default(ValueBuffer);

if (buffer)
{
BufferAll();
}

return hasNext;
}

if (_buffer.Count > 0)
catch (Exception)
{
_current = _buffer.Dequeue();
_queryingEnumerable._relationalQueryContext.DeregisterValueBufferCursor(this);
_dataReader = null;
_dbDataReader = null;

return true;
throw;
}

return false;
}

// ReSharper disable once ConvertToAutoPropertyWithPrivateSetter
Expand All @@ -128,6 +150,7 @@ public void BufferAll()

_queryingEnumerable._relationalQueryContext.Connection?.Close();
_dataReader = null;
_dbDataReader = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,27 @@ public class RelationalQueryContextFactory : QueryContextFactory
public RelationalQueryContextFactory(
[NotNull] ICurrentDbContext currentContext,
[NotNull] IConcurrencyDetector concurrencyDetector,
[NotNull] IRelationalConnection connection)
[NotNull] IRelationalConnection connection,
[NotNull] IExecutionStrategyFactory executionStrategyFactory)
: base(currentContext, concurrencyDetector)
{
_connection = connection;
ExecutionStrategyFactory = executionStrategyFactory;
}

/// <summary>
/// The execution strategy factory.
/// </summary>
/// <value>
/// The execution strategy factory.
/// </value>
protected virtual IExecutionStrategyFactory ExecutionStrategyFactory { get; }

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override QueryContext Create()
=> new RelationalQueryContext(CreateQueryBuffer, _connection, StateManager, ConcurrencyDetector);
=> new RelationalQueryContext(CreateQueryBuffer, _connection, StateManager, ConcurrencyDetector, ExecutionStrategyFactory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public RelationalQueryContext(
[NotNull] Func<IQueryBuffer> queryBufferFactory,
[NotNull] IRelationalConnection connection,
[NotNull] LazyRef<IStateManager> stateManager,
[NotNull] IConcurrencyDetector concurrencyDetector)
[NotNull] IConcurrencyDetector concurrencyDetector,
[NotNull] IExecutionStrategyFactory executionStrategyFactory)
: base(
Check.NotNull(queryBufferFactory, nameof(queryBufferFactory)),
Check.NotNull(stateManager, nameof(stateManager)),
Expand All @@ -42,6 +43,7 @@ public RelationalQueryContext(
Check.NotNull(connection, nameof(connection));

Connection = connection;
ExecutionStrategyFactory = executionStrategyFactory;
}

/// <summary>
Expand All @@ -60,6 +62,14 @@ public RelationalQueryContext(
/// </value>
public virtual SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1);

/// <summary>
/// The execution strategy factory.
/// </summary>
/// <value>
/// The execution strategy factory.
/// </value>
public virtual IExecutionStrategyFactory ExecutionStrategyFactory { get; }

/// <summary>
/// Registers a value buffer cursor.
/// </summary>
Expand Down
Loading

0 comments on commit 48cb312

Please sign in to comment.