2017-11-29 17 views
0

私はポリシーを作成しようとしています。実行ブロックの最大時間...リトライと中間ディレイを使用して

実行コードを最大時間(この例では10秒)実行します。 しかし、私はまたx回数(サンプルで3回)をやり直したいと思います。そして、失敗の一時停止(サンプルの2秒間)を中断します。

私は私の動作をテストするために人為的に遅延するために私のストアドプロシージャを組み立てました。

私のデータセットはコード化されているので、30秒後にデータセットが読み込まれます(fyi:30秒はストアドプロシージャのハードコード値)。だから私の実行コードは10秒後に救済されません....

理想的には、最初の2回の試行で10秒後に救済されるコードを見て、人為的に遅延する)。明らかにこれは実際のコードではありませんが、不思議なストアドプロシージャは私にその動作をテストする方法を与えます。

マイストアドプロシージャ:

USE [Northwind] 
GO 


/* CREATE */ ALTER PROCEDURE [dbo].[uspWaitAndReturn] 
(
    @InvokeDelay bit 
) 

AS 

SET NOCOUNT ON; 

if (@InvokeDelay > 0) 
BEGIN 
    WAITFOR DELAY '00:00:30'; 
END 

select top 1 * from dbo.Customers c order by newid() 

GO 

マイC#/ポリー/データベース・コード:

public DataSet GetGenericDataSet() 
    { 
     DataSet returnDs = null; 

     int maxRetryAttempts = 3; /* retry attempts */ 
     TimeSpan pauseBetweenFailuresTimeSpan = TimeSpan.FromSeconds(2); /* pause in between failures */ 
     Policy timeoutAfter10SecondsPolicy = Policy.Timeout(TimeSpan.FromSeconds(10)); /* MAGIC SETTING here, my code inside the below .Execute block below would bail out after 10 seconds */ 
     Policy retryThreeTimesWith2SecondsInBetweenPolicy = Policy.Handle<Exception>().WaitAndRetry(maxRetryAttempts, i => pauseBetweenFailuresTimeSpan); 
     Policy aggregatePolicy = timeoutAfter10SecondsPolicy.Wrap(retryThreeTimesWith2SecondsInBetweenPolicy); 

     int attemptCounter = 0; /* used to track the attempt and conditionally set the @InvokeDelay value for the stored procedure */ 

     aggregatePolicy.Execute(() => 
     { 
      try 
      { 
       attemptCounter++; 

       /* Microsoft.Practices.EnterpriseLibrary.Data code */ 
       ////////DatabaseProviderFactory factory = new DatabaseProviderFactory(); 
       ////////Database db = factory.CreateDefault(); 
       ////////DbCommand dbc = db.GetStoredProcCommand("dbo.uspWaitAndReturn"); 
       ////////dbc.CommandTimeout = 120; 
       ////////db.AddInParameter(dbc, "@InvokeDelay", DbType.Boolean, attemptCounter < maxRetryAttempts ? true : false); /* if i'm not on my last attempt, then pass in true to cause the artificial delay */ 
       ////////DataSet ds; 
       ////////ds = db.ExecuteDataSet(dbc); 
       ////////returnDs = ds; 

       using (SqlConnection conn = new SqlConnection(@"MyConnectionStringValueHere")) 
       { 
        SqlCommand sqlComm = new SqlCommand("[dbo].[uspWaitAndReturn]", conn); 
        sqlComm.Parameters.AddWithValue("@InvokeDelay", attemptCounter < maxRetryAttempts ? true : false); 
        sqlComm.CommandType = CommandType.StoredProcedure; 

        SqlDataAdapter da = new SqlDataAdapter(); 
        da.SelectCommand = sqlComm; 
        DataSet ds = new DataSet(); 
        da.Fill(ds); 
        returnDs = ds; 
       } 

      } 
      catch (Exception ex) 
      { 
       string temp = ex.Message; 
       throw; 
      } 
     }); 

     return returnDs; 
    } 

用い文:

using System; 
using System.Data; 
using System.Data.Common; 
using System.Data.SqlClient; 
//// using Microsoft.Practices.EnterpriseLibrary.Data; 
using Polly; 

バージョン(packages.config)

<?xml version="1.0" encoding="utf-8"?> 
<packages> 
    <package id="CommonServiceLocator" version="1.0" targetFramework="net40" /> 
    <package id="EnterpriseLibrary.Common" version="6.0.1304.0" targetFramework="net45" /> 
    <package id="EnterpriseLibrary.Data" version="6.0.1304.0" targetFramework="net45" /> 


    <package id="Polly" version="5.6.1" targetFramework="net45" /> 


/> 
</packages> 

APPEND:

@mountain旅行者からの素晴らしい答えた後、私は作業例を持っている:

追加TimeoutStrategy.Pessimistic

とされたDbCommandを追加しました:

重要なポイントでした。 Cancel()コール(エンタープライズライブラリを使用しない場合はSqlCommand.Cancel())を使用して(以前の)コマンドを強制終了します。

また、私は "ポリシーaggregatePolicy"を "逆転"しなければなりませんでした。

public DataSet GetGenericDataSet() 
    { 
     DataSet returnDs = null; 

     DbCommand dbc = null; /* increase scope so it can be cancelled */ 

     int maxRetryAttempts = 3; /* retry attempts */ 
     TimeSpan pauseBetweenFailuresTimeSpan = TimeSpan.FromSeconds(2); /* pause in between failures */ 
     Policy timeoutAfter10SecondsPolicy = Policy.Timeout(
      TimeSpan.FromSeconds(10), 
      TimeoutStrategy.Pessimistic, 
      (context, timespan, task) => 
      { 
       string x = timespan.Seconds.ToString(); 
       if (null != dbc) 
       { 
        dbc.Cancel(); 
        dbc = null; 
       } 
      }); 

     Policy retryThreeTimesWith2SecondsInBetweenPolicy = Policy.Handle<Exception>().WaitAndRetry(maxRetryAttempts, i => pauseBetweenFailuresTimeSpan); 
     ////Policy aggregatePolicy = timeoutAfter10SecondsPolicy.Wrap(retryThreeTimesWith2SecondsInBetweenPolicy); 
     Policy aggregatePolicy = retryThreeTimesWith2SecondsInBetweenPolicy.Wrap(timeoutAfter10SecondsPolicy); 

     int attemptCounter = 0; /* used to track the attempt and conditionally set the @InvokeDelay value for the stored procedure */ 

     aggregatePolicy.Execute(() => 
     { 
      try 
      { 
       attemptCounter++; 

       /* Microsoft.Practices.EnterpriseLibrary.Data code */ 
       DatabaseProviderFactory factory = new DatabaseProviderFactory(); 
       Database db = factory.CreateDefault(); 
       dbc = db.GetStoredProcCommand("dbo.uspWaitAndReturn"); 
       dbc.CommandTimeout = 120; 
       db.AddInParameter(dbc, "@InvokeDelay", DbType.Boolean, attemptCounter < maxRetryAttempts ? true : false); /* if i'm not on my last attempt, then pass in true to cause the artificial delay */ 
       DataSet ds; 
       ds = db.ExecuteDataSet(dbc); 
       returnDs = ds; 

       ////////using (SqlConnection conn = new SqlConnection(@"YOUR_VALUE_HERE")) 
       ////////{ 
       //////// SqlCommand sqlComm = new SqlCommand("[dbo].[uspWaitAndReturn]", conn); 
       //////// sqlComm.Parameters.AddWithValue("@InvokeDelay", attemptCounter < maxRetryAttempts ? true : false); 
       //////// sqlComm.CommandType = CommandType.StoredProcedure; 

       //////// SqlDataAdapter da = new SqlDataAdapter(); 
       //////// da.SelectCommand = sqlComm; 
       //////// DataSet ds = new DataSet(); 
       //////// da.Fill(ds); 
       //////// returnDs = ds; 
       ////////} 
      } 
      catch (SqlException sqlex) 
      { 
       switch (sqlex.ErrorCode) 
       { 
        case -2146232060: 
         /* I couldn't find a more concrete way to find this specific exception, -2146232060 seems to represent alot of things */ 
         if (!sqlex.Message.Contains("cancelled")) 
         { 
          throw; 
         } 

         break; 
        default: 
         throw; 
       } 
      } 
     }); 

     return returnDs; 
    } 

Sql Profiler Results

答えて

1

ポリーTimeoutPolicycomes in two modes

  • Optimistic TimeoutCancellationToken介しco-operative cancellationによってタイムアウトを強制します。実行されたデリゲートがCancellationTokenに応答することが期待されます。

  • Pessimistic Timeoutは、あなたが実行されているデリゲートがどのCancellationTokenを尊重しないCancellationToken

に応答ではありません代表者のタイムアウトを強制します。だから、(元のコードのため掲載)あなたはTimeoutStrategy.Pessimisticを使用するポリシーを設定する必要があります:

Policy timeoutAfter10SecondsPolicy = Policy.Timeout(TimeSpan.FromSeconds(10), TimeoutStrategy.Pessimistic); 

(。これはデフォルトであるとして、元のコードでは、Policy timeoutAfter10SecondsPolicy = Policy.Timeout(TimeSpan.FromSeconds(10));TimeoutStrategy.Optimisticを採用し、掲載)


上記のコードでは、TimeoutPolicyが表示されない理由を説明しています。 これはです:discussion in the Polly wiki about what pessimistic timeout meansに注意してください:が実行されたデリゲートをが待つのを待つが、そのデリゲートを実行しているスレッド/ Taskを取り消すことはできません。したがって、使用中のSQLリソースはタイムアウト時に解放されません。

タイムアウト時にSQLリソースが解放されるようにするには、タイムアウト時にSQL操作をキャンセルするコードを拡張する必要があります。例えば、this StackOverflow answerに示すように、SqlCommand.Cancel() methodを使用できます。 PollyのTimeoutPolicycan be configured with an onTimeout delegateは、タイムアウトが発生したときに呼び出されます。すなわち、適切なSqlCommand.Cancel()を呼び出すようにこのデリゲートを設定します。

また、SqlCommand.ExecuteReaderAsync(CancellationToken)などのコードをCancellationTokenで駆動されるPollyのoptimistic timeoutと組み合わせて、非同期のアプローチに移行することもできます。しかし、それははるかに広い議論です。

+0

私はDbCommand.Cancel()(具体的にはSqlCommand.Cancel()です)を作成していました。ありがとう。私は私のaggregatePolicyを "後方に"持っていると思います。 – granadaCoder

+0

私は完全な作業コードとSQ​​Lプロファイラのスクリーンショットで私の元の質問を追加しました。再度、感謝します。 – granadaCoder

関連する問題