원격 postgres 서버에 연결할 서비스를 작성하고 있습니다. 어떤 예외가 일시적 (재 시도 가치)으로 취급되어야하는지, 그리고 원격 데이터베이스에 연결하기위한 적절한 정책을 정의하는 방법을 결정하는 좋은 방법을 찾고 있습니다.호출이 재 시도할만한 가치가있는 경우 Npgsql 예외를 알려주는 방법 (일시적 결함 전략)
서비스가 데이터 액세스에 Npgsql을 사용하고 있습니다. 설명서에 따르면 Npgsql은 SQL 오류에 대해 PostgresException을 발생시키고 "서버 관련 문제"에 대해서는 NpgsqlException을 발생시킵니다.
지금까지 내가 생각해 낼 수 있었던 가장 좋은 점은 PostgresExceptions가 일시적으로 재 시도 할 가치가있는 것으로 간주되어야한다고 생각하는 것입니다. 그러나 PostgresException은 쿼리에 문제가 있다는 것을 의미합니다. 다시 시도하는 것은 도움이되지 않습니다. 이 가정에서 나는 맞습니까?
Polly를 사용하여 재시도 및 회로 차단기 정책을 만들고 있습니다. 따라서, 내 정책은 다음과 같습니다
Policy.Handle<Exception>(AllButPotgresExceptions()) // if its a postgres exception we know its not going to work even with a retry, so don't
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(4)
}, onRetry: (exception, span) => Log.Warning(exception, "Postgres Retry Failure: "))
.WrapAsync(
Policy.Handle<Exception>(AllButPotgresExceptions())
.AdvancedCircuitBreakerAsync(
failureThreshold:.7,
samplingDuration: TimeSpan.FromSeconds(30),
minimumThroughput: 20,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (ex, timeSpan, context) => Log.Warning(ex, "Postres Circuit Breaker Broken: "),
onReset: (context) => Log.Warning("Postres Circuit Breaker Reset: "),
onHalfOpen:() => Log.Warning("Postres Circuit Breaker Half Open: ")
)));
}
}
private static Func<Exception, bool> AllButPotgresExceptions()
{
return ex => ex.GetType() != typeof(PostgresException);
}
일시적 일 수있는 오류를 확인할 수있는 더 나은 방법이 있나요?
UPDATE : 셰이의 제안 다음은
나는 Npgsql에 새로운 문제를 열고 다음과 같이 내 정책을 업데이트 :
public static Policy PostresTransientFaultPolicy
{
get
{
return postgresTransientPolicy ?? (postgresTransientPolicy = Policy.Handle<Exception>(PostgresDatabaseTransientErrorDetectionStrategy())
.WaitAndRetryAsync(
retryCount: 10,
sleepDurationProvider: retryAttempt => ExponentialBackoff(retryAttempt, 1.4),
onRetry: (exception, span) => Log.Warning(exception, "Postgres Retry Failure: "))
.WrapAsync(
Policy.Handle<Exception>(PostgresDatabaseTransientErrorDetectionStrategy())
.AdvancedCircuitBreakerAsync(
failureThreshold:.4,
samplingDuration: TimeSpan.FromSeconds(30),
minimumThroughput: 20,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (ex, timeSpan, context) => Log.Warning(ex, "Postres Circuit Breaker Broken: "),
onReset: (context) => Log.Warning("Postres Circuit Breaker Reset: "),
onHalfOpen:() => Log.Warning("Postres Circuit Breaker Half Open: ")
)));
}
}
private static TimeSpan ExponentialBackoff(int retryAttempt, double exponent)
{
//TODO add random %20 variance on the exponent
return TimeSpan.FromSeconds(Math.Pow(retryAttempt, exponent));
}
private static Func<Exception, bool> PostgresDatabaseTransientErrorDetectionStrategy()
{
return (ex) =>
{
//if it is not a postgres exception we must assume it will be transient
if (ex.GetType() != typeof(PostgresException))
return true;
var pgex = ex as PostgresException;
switch (pgex.SqlState)
{
case "53000": //insufficient_resources
case "53100": //disk_full
case "53200": //out_of_memory
case "53300": //too_many_connections
case "53400": //configuration_limit_exceeded
case "57P03": //cannot_connect_now
case "58000": //system_error
case "58030": //io_error
//These next few I am not sure whether they should be treated as transient or not, but I am guessing so
case "55P03": //lock_not_available
case "55006": //object_in_use
case "55000": //object_not_in_prerequisite_state
case "08000": //connection_exception
case "08003": //connection_does_not_exist
case "08006": //connection_failure
case "08001": //sqlclient_unable_to_establish_sqlconnection
case "08004": //sqlserver_rejected_establishment_of_sqlconnection
case "08007": //transaction_resolution_unknown
return true;
}
return false;
};
}
귀하의 도움과 제안에 감사드립니다. 몇 가지 구체적인 오류 코드를 찾기 위해 정책을 업데이트 한 다음 여기에 수정 된 정책을 게시합니다. –
좋아요. https://github.com/npgsql/npgsql에서 문제를 열어주세요. Google 정책을 Npgsql 자체에 통합 할 수 있습니다. –
제 경험상 가장 중요한 일시적인 오류는 코드 40001 (트랜잭션 직렬화 실패)이있는 오류입니다. –