リモート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で問題を開いてください。あなたのポリシーをNpgsql自体に組み込むことができます。 –
私の経験から、最も重要な一時的なエラーは、コード40001(トランザクションのシリアル化の失敗)のエラーです。 –