かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Reactive Extensions再入門 その35「駄目ならやり直す!を実現するRetryメソッド」

過去記事インデックス

はじめに

前回のOnErrorResumeNextメソッドでは、エラーの場合は別の方法で再開するという処理をやりました。今回は、同じことを愚直にやり続けるRetryメソッドです!

Retryメソッド

ここではRetryメソッドについて説明します。Retryメソッドは名前の通りエラーが起きた場合に、繰り返し同じ処理を行うメソッドです。メソッドのオーバーロードには、成功するまで無限に繰り返しリトライをするものと、引数でリトライ回数を指定するものがあります。メソッドのシグネチャを下記に示します。

public static IObservable<T> Retry<T>(this IObservable<T> source);
public static IObservable<T> Retry<T>(this IObservable<T> source, int retryCount);

まず、無限に繰り返すメソッドのオーバーロードを使ったコード例を下記に示します。

var retryCount = 0;
Observable
    // retryCountが3になるまでエラーになる
    .Create<string>(o =>
    {
        Console.WriteLine("Create method called: {0}", retryCount);
        if (retryCount == 3)
        {
            o.OnNext(retryCount.ToString());
            o.OnCompleted();
            return Disposable.Empty;
        }

        retryCount++;
        o.OnError(new InvalidOperationException(retryCount.ToString()));
        return Disposable.Empty;
    })
    // 成功するまでリトライする
    .Retry()
    // 購読
    .Subscribe(
        s => Console.WriteLine("OnNext: {0}", s),
        ex => Console.WriteLine("OnError: {0}", ex),
        () => Console.WriteLine("OnCompleted"));

Createメソッドで3回目までエラーになるIObservableを作成しています。それに対してRetryメソッドを呼び出して、結果を表示しています。実行結果を以下に示します。

Create method called: 0
Create method called: 1
Create method called: 2
Create method called: 3
OnNext: 3
OnCompleted

Createメソッドが4回呼ばれてエラーではない値が発行されるとSubscribeまで値が渡っていることが確認できます。このようにエラーが起きても何度もリトライします。
次にリトライ回数を指定した場合の動作を示すコード例を下記に示します。

var retryCount = 0;
Observable
    // retryCountが3になるまでエラーになる
    .Create<string>(o =>
    {
        Console.WriteLine("Create method called: {0}", retryCount);
        if (retryCount == 3)
        {
            o.OnNext(retryCount.ToString());
            o.OnCompleted();
            return Disposable.Empty;
        }

        retryCount++;
        o.OnError(new InvalidOperationException(retryCount.ToString()));
        return Disposable.Empty;
    })
    // 2回リトライする
    .Retry(2)
    // 購読
    .Subscribe(
        s => Console.WriteLine("OnNext: {0}", s),
        ex => Console.WriteLine("OnError: {0}", ex),
        () => Console.WriteLine("OnCompleted"));

先ほどと、ほぼ同じコードですがRetryメソッドの引数で2回までしかリトライをしないように指定しています。このコードの実行結果を以下に示します。

Create method called: 0
Create method called: 1
OnError: System.InvalidOperationException: 2

実行結果からわかるように2回目のリトライでも例外が発生しているため、SubscribeのOnErrorが呼び出されています。おそらく、現実的にはリトライ回数を指定するオーバーロードを使うことが多くなると思います。