연결 복원력 및 다시 시도 논리

참고 항목

EF6 이상만 - 이 페이지에서 다루는 기능, API 등은 Entity Framework 6에 도입되었습니다. 이전 버전을 사용하는 경우 이 정보의 일부 또는 전체가 적용되지 않습니다.

데이터베이스 서버에 연결하는 애플리케이션은 백 엔드 오류 및 네트워크 불안정으로 인해 연결 중단에 항상 취약했습니다. 그러나 전용 데이터베이스 서버에서 작업하는 LAN 기반 환경에서는 이러한 오류가 거의 발생하지 않아 해당 오류를 처리하기 위한 추가 논리가 자주 필요하지 않습니다. Windows Azure SQL Database와 같은 클라우드 기반 데이터베이스 서버와 안정성이 떨어지는 네트워크를 통한 연결이 증가하면서 연결 끊김이 발생하는 것이 더 일반적입니다. 이는 연결 제한과 같이 서비스의 공정성을 보장하기 위해 클라우드 데이터베이스가 사용하는 방어 기술이나 간헐적인 시간 초과 및 기타 일시적인 오류를 유발하는 네트워크의 불안정성 때문일 수 있습니다.

연결 복원력은 EF가 이러한 연결 끊기로 인해 실패한 모든 명령을 자동으로 다시 시도하는 기능을 나타냅니다.

실행 전략

연결 다시 시도는 IDbExecutionStrategy 인터페이스의 구현에 의해 처리됩니다. IDbExecutionStrategy의 구현은 작업을 수락하고, 예외가 발생하는 경우 다시 시도가 적절한지 확인하고 적절한 경우 다시 시도하는 역할을 담당합니다. EF와 함께 제공되는 네 가지 실행 전략이 있습니다.

  1. DefaultExecutionStrategy: 이 실행 전략은 어떤 작업도 다시 시도하지 않으며 SQL Server 이외의 데이터베이스에 대한 기본값입니다.
  2. DefaultSqlExecutionStrategy: 기본적으로 사용되는 내부 실행 전략입니다. 이 전략은 전혀 다시 시도하지 않지만 사용자에게 연결 복원력을 사용하도록 설정할 수 있음을 알리기 위해 일시적일 수 있는 예외를 래핑합니다.
  3. DbExecutionStrategy: 이 클래스는 사용자 정의 전략을 포함하여 다른 실행 전략의 기본 클래스로 적합합니다. 초기 다시 시도는 지연이 전혀 발생하지 않고 최대 다시 시도 횟수가 적중될 때까지 지연이 기하급수적으로 증가하는 지수 다시 시도 정책을 구현합니다. 이 클래스에는 다시 시도해야 하는 예외를 제어하기 위해 파생된 실행 전략에서 구현할 수 있는 추상 ShouldRetryOn 메서드가 있습니다.
  4. SqlAzureExecutionStrategy: 이 실행 전략은 DbExecutionStrategy에서 상속되며 Azure SQL Database로 작업할 때 일시적일 수 있는 것으로 알려진 예외를 다시 시도합니다.

참고 항목

실행 전략 2와 4는 EF와 함께 제공되는 Sql Server 공급자에 포함되어 있습니다. 이는 EntityFramework.SqlServer 어셈블리에 있으며 SQL Server와 함께 작동하도록 설계되었습니다.

실행 전략 사용

EF에 실행 전략을 사용하도록 지시하는 가장 쉬운 방법은 DbConfiguration 클래스의 SetExecutionStrategy 메서드를 사용하는 것입니다.

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
    }
}

이 코드는 SQL Server에 연결할 때 SqlAzureExecutionStrategy를 사용하도록 EF에 지시합니다.

실행 전략 구성

SqlAzureExecutionStrategy의 생성자는 MaxRetryCount 및 MaxDelay라는 두 가지 매개 변수를 허용할 수 있습니다. MaxRetry 수는 전략이 다시 시도할 최대 횟수입니다. MaxDelay는 실행 전략에서 사용할 다시 시도 사이의 최대 지연 시간을 나타내는 TimeSpan입니다.

최대 다시 시도 횟수를 1로 설정하고 최대 지연 시간을 30초로 설정하려면 다음을 실행합니다.

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy(
            "System.Data.SqlClient",
            () => new SqlAzureExecutionStrategy(1, TimeSpan.FromSeconds(30)));
    }
}

SqlAzureExecutionStrategy는 일시적인 오류가 처음 발생할 때 즉시 다시 시도하지만 최대 다시 시도 한도가 초과되거나 총 시간이 최대 지연에 도달할 때까지 각 다시 시도 사이에 더 오래 지연됩니다.

실행 전략은 일반적으로 일시적인 제한된 수의 예외만 다시 시도합니다. 오류가 일시적이지 않거나 해결하는 데 너무 오래 걸리는 경우에는 다른 오류를 처리하고 RetryLimitExceeded 예외를 포착해야 합니다.

다시 시도 실행 전략을 사용하는 경우 알려진 몇 가지 제한 사항이 있습니다.

스트리밍 쿼리는 지원되지 않습니다.

기본적으로 EF6 이상 버전은 쿼리 결과를 스트리밍하지 않고 버퍼링합니다. 결과를 스트리밍하려면 AsStreaming 메서드를 사용하여 LINQ to Entities 쿼리를 스트리밍으로 변경할 수 있습니다.

using (var db = new BloggingContext())
{
    var query = (from b in db.Blogs
                orderby b.Url
                select b).AsStreaming();
    }
}

다시 시도 실행 전략이 등록되면 스트리밍이 지원되지 않습니다. 이 제한은 반환되는 결과 중에 연결이 부분적으로 삭제될 수 있기 때문에 존재합니다. 이 경우 EF는 전체 쿼리를 다시 실행해야 하지만 어떤 결과가 이미 반환되었는지 알 수 있는 신뢰할 수 있는 방법이 없습니다(초기 쿼리가 전송된 이후 데이터가 변경되었을 수 있고, 결과가 다른 순서로 돌아올 수 있으며, 결과에 고유 식별자가 없을 수 있음 등).

사용자가 시작한 트랜잭션은 지원되지 않습니다.

다시 시도를 초래하는 실행 전략을 구성한 경우 트랜잭션 사용에 대한 몇 가지 제한 사항이 있습니다.

기본적으로 EF는 트랜잭션 내에서 데이터베이스 업데이트를 수행합니다. 이 기능을 사용하도록 설정하기 위해 아무 작업도 수행할 필요가 없습니다. EF는 항상 이 작업을 자동으로 수행합니다.

예를 들어 다음 코드에서 SaveChanges는 트랜잭션 내에서 자동으로 수행됩니다. 새 사이트 중 하나를 삽입한 후 SaveChanges가 실패하면 트랜잭션이 롤백되고 데이터베이스에 변경 내용이 적용되지 않습니다. 또한 컨텍스트는 SaveChanges를 다시 호출하여 변경 사항 적용을 다시 시도할 수 있는 상태로 남아 있습니다.

using (var db = new BloggingContext())
{
    db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
    db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
    db.SaveChanges();
}

다시 시도 실행 전략을 사용하지 않는 경우 단일 트랜잭션에서 여러 작업을 래핑할 수 있습니다. 예를 들어 다음 코드는 단일 트랜잭션에서 두 개의 SaveChanges 호출을 래핑합니다. 두 작업의 일부가 실패하면 변경 내용이 적용되지 않습니다.

using (var db = new BloggingContext())
{
    using (var trn = db.Database.BeginTransaction())
    {
        db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
        db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
        db.SaveChanges();

        db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
        db.SaveChanges();

        trn.Commit();
    }
}

EF는 이전 작업과 다시 시도 방법을 인식하지 못하기 때문에 다시 시도 실행 전략을 사용할 때는 지원되지 않습니다. 예를 들어 두 번째 SaveChanges가 실패한 경우 EF에는 더 이상 첫 번째 SaveChanges 호출을 다시 시도하는 데 필요한 정보가 없습니다.

해결 방법: 수동으로 실행 전략 호출

이 해결 방법은 실행 전략을 수동으로 사용하고 실행할 전체 논리 집합을 제공하여 작업 중 하나가 실패할 경우 모든 것을 다시 시도할 수 있도록 하는 것입니다. DbExecutionStrategy에서 파생된 실행 전략이 실행 중이면 SaveChanges에 사용되는 암시적 실행 전략이 일시 중단됩니다.

컨텍스트는 다시 시도할 코드 블록 내에서 구성되어야 합니다. 이렇게 하면 다시 시도를 수행할 때마다 정리된 상태로 시작할 수 있습니다.

var executionStrategy = new SqlAzureExecutionStrategy();

executionStrategy.Execute(
    () =>
    {
        using (var db = new BloggingContext())
        {
            using (var trn = db.Database.BeginTransaction())
            {
                db.Blogs.Add(new Blog { Url = "http://msdn.com/data/ef" });
                db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
                db.SaveChanges();

                db.Blogs.Add(new Blog { Url = "http://twitter.com/efmagicunicorns" });
                db.SaveChanges();

                trn.Commit();
            }
        }
    });