2017-12-30 5 views
1

Azureサービスバスのキューで長時間実行されているメッセージを処理しているときに、奇妙な動作が見られます。私たちはかなり大きなデータセットを処理するので、ロックを更新しています。このメッセージには通常、完了に約1時間かかります。このプロセスはうまく動作し、完了まで実行されます。 がUTCの深夜に実行されている場合を除きます。真夜中UTCにAzureサービスバスのキューメッセージのロックがUTCの深夜0時に有効になる

バンは、私たちのプロセスは例外をスロー:

Microsoft.ServiceBus.Messaging.MessageLockLostException:供給ロックが無効です。ロックの有効期限が切れているか、メッセージがすでにキューから削除されています。

私たちはこの夜の夜を再現することができ、深夜にプロセスを実行しないと、決して起こらない。

メッセージロック "ExpiresAtUtc"タイムスタンプの計算では、ある日から別の日に非常に上手く行き来することはできませんか?

---- UPDATE ----

助けるかもしれないいくつかのより多くの情報:

長時間実行されるプロセスはRenewLockを呼び出すことによって、キューに再び見えるようになるからメッセージを保持します。この処理が深夜0時を過ぎると、メッセージがキューに表示され、処理が再開されます。メッセージはドロップされず、デッドレターキューには移動しません。ロックは単に期限切れとなり、メッセージは再びキューに表示され、プロセッサによってピックアップされ、プロセスが再開されます。プロセスがUTCの深夜の境界を越えない限り、正常に完了します。キューに

接続:

private QueueClient GetQueue<TMessage>() => QueueClient.CreateFromConnectionString(this.configSection.Value.ConnectionString, typeof(TMessage).Name, ReceiveMode.PeekLock); 

エンキューメッセージ:

using (var brokeredMessage = new BrokeredMessage(message) {ContentType = "application/json"}) 
{ 
    await GetQueue<TMessage>().SendAsync(brokeredMessage).ConfigureAwait(false); 
} 

デキューメッセージを

は、ここでは、コードのスニペットは、我々は、キューから/エンキュー/デキューを接続するために使用しているされています:

GetQueue<TMessage>().OnMessageAsync(
    async msg => 
    { 
     TMessage body = null; 
     try 
     { 
      body = msg.GetBody<TMessage>(); 

      await handler.HandleMessageAsync(body, msg.RenewLockAsync).ConfigureAwait(false); 
      await msg.CompleteAsync().ConfigureAwait(false); 
     } 
     catch (Exception ex) 
     { 
      await msg.AbandonAsync().ConfigureAwait(false); 
     } 
    }, 
    new OnMessageOptions 
    { 
     AutoComplete = false 
    } 
); 

以下は、Azureサービスバスのメトリクス - 成功したリクエストのスクリーンショットです。UTCの深夜(私はUTC + 1のタイムゾーンにありますので、私はどこにいるのですか)に、深夜の何かを示す方法を示します。は、 :

enter image description here以下

は、私たちの内部ログのスクリーンショットでは、処理がちょうど停止し、後に再び起動するだけ分を超える - ロックが期限切れになったとのメッセージが選択されますので、再びキューに表示されたときにプロセッサーによるアップ:

enter image description here

+0

_メッセージロック "ExpiresAtUtc"タイムスタンプの計算では、ある日から別の日に渡って非常に上手く行き渡ることはありませんか?_そうは思わないでください。シンプルなレプロを分かち合うことができればいいですね。 –

+0

こんにちは@SeanFeldman - 私はもう少し情報を追加しました。これは役に立ちますか? – codefrenzy

+0

興味深い。チャートを見ると、2分間の切断があるようです。ロックの更新はクライアント操作であるため、クライアントがサーバーに接続できない場合はロックが失われ、メッセージは他のインスタンスで使用できるようになります。ロック期間が最大5分に設定されている場合、ロック更新はそれより早く実行されます。キューに設定されているロック期間は何ですか?また、ASBクライアントのどのバージョンを使用していますか? –

答えて

0

私はASB(巨大規模)でトピック/サブスクリプションを扱うのに慣れていますが、キューとトピックが同じ内部インフラストラクチャを共有している限り、

基本的に、私たちのソフトウェアでは、長時間実行されているプロセスに対してメッセージをロックする方法として、BrokeredMessage.RenewLock()を使用しています。

プロセスを終了するまでメッセージをロックしたままにしておくと、30秒ごとに(キューのロック期間によって異なります)、別のTaskが開始されます。これはbrokeredMessage.RenewLock()つまり、ロックをpingし続けるので、最終的には失われません。

報告しているエラーはローカルではなく、メッセージを「完了」、デッドレターしたり、メッセージをロックする必要がある他の操作をしようとするとASBによってスローされます。タイムアウトによるロック

我々はロックを維持するために使用するコードは、生きている、これに非常に似ています:

private void EngageAutoRenewLock(BrokeredMessage message, CancellationTokenSource renewLockCancellationTokenSource) 
     { 
      var renewalThread = Task.Factory.StartNew(() => 
      { 
       Trace(string.Format("Starting lock renewal thread for MsgID {0}", message.MessageId)); 

       while (!renewLockCancellationTokenSource.IsCancellationRequested) 
       { 
        // we use Sleep instead of Task.Delay to avoid get an exception when token is cancelled 
        System.Threading.Thread.Sleep(30000); 
        // check again after sleeping 
        if (!renewLockCancellationTokenSource.IsCancellationRequested) 
        { 
         try 
         { 
          // renewlock wraps a RenewLockAsync() call 
          message.RenewLock(); 
         } 
         catch (Exception ex) 
         { 
          /* Do Nothing. 

           This exception can happen due the async nature of the RenewLock Operation (async Begin/End), it is possible we 
           completed/abandoned/deadlettered the message in the main thread in the middle of the RenewLock operation. 

           Additionally, we should keep retrying to renew lock until renewLockCancellationTokenSource has ben signaled. 
           This will allow to handle other transient renewal issues. */ 

          Trace(string.Format("Lock renewal failed due an exception for MsgID {0} - {1}", message.MessageId, ex.Message)); 
         } 
         Trace(string.Format("Lock renewed MsgID {0}", message.MessageId)); 
        } 
        else 
        { 
         Trace(string.Format("Lock renewed MsgID {0} not happened due IsCancellationRequested == true", message.MessageId)); 
        } 
       } 
       Trace(string.Format("Lock renewal has been cancelled by cancellationToken (ok) for MsgID {0}", message.MessageId)); 
      }, renewLockCancellationTokenSource.Token); 
     } 
+0

あなたのメッセージポンプをはるかに単純化し、メッセージロックの拡張子2をサポートすることができるOnMessageAPIを調べることをお勧めします。 'Thread.Sleep()'と非同期コードを混在させないでください。 –

+0

私たちの場合、Task.Delayの代わりにThreadスリープに依存する技術的な理由があります。他の人たちは、実装のコンテキストの方がより便利で正確な方法で遅延を処理することは自由です。ヒント:多くの標準ライブラリ/他のライブラリはTPLの前に書かれていたので、System.Threading.ThreadのものをTPLと効果的に混ぜるので、コードがTPLの責任を妨げない限り、両方を自由に混在させることができます。 –

1

私はAzureのサービスバスのチームから来ました。 LockDurationは時間の単なるものであり、内部ではタイムスタンプとして表されません。メッセージは、受信された瞬間からLockDurationのために技術的にロックされ、将来のある瞬間までロックされません。たとえば、受信した時刻から30秒間メッセージがロックされます。だから深夜UTCは決して特別な事件ではありません。私たちの自動化されたテストは毎晩実行されています。 しかし、毎晩それを複製しているので、面白いことが起こっているに違いありません。私はそれが何であるかはまだ分かりません。あなたがこれを再現している地域を知りたい。私たちのチームメンバーの1人がすぐにあなたに連絡します。

+0

ありがとうございます - あなたが想像しているように、実際に実行中のシステムに影響を与えているので、この問題を解決したいと思っています。私たちの列は「西アメリカ」にあります。キュープロセッサ(Windowsサービス)を実行しているVMも 'West US'にありますが、UTCタイムゾーンを使用しています。 – codefrenzy

関連する問題