在Rx.Net中,如何在反馈耗尽之前实现可观察的反馈循环?

时间:2015-11-06 20:17:43

标签: c# system.reactive

我有以下API:

IObservable<IList<SqlDataRecord>> WriteToDBAndGetFailedSource(SqlConnection conn, IList<SqlDataRecord> batch)

它尝试将批处理写入数据库。如果失败,则返回整个批处理,否则返回的observable为空。

我还有一个产生批次的来源:

IObservable<IList<SqlDataRecord>> GetDataSource(string filePath, int bufferThreshold)

现在,我可以将它们组合起来:

var failedBatchesSource = GetDataSource(filePath, 1048576)
  .Select(batch => WriteToDBAndGetFailedSource(conn, batch))
  .Merge(100);

这将编写所有批次(最多100个并发)并返回可观察到的失败批次。

我真正想要的是将失败的批次反馈到批次的来源在一段暂停后,可能是在原始来源仍在生成批次时。当然,我可以这样写:

var failedBatchesSource = GetDataSource(filePath, 1048576)
  .Select(batch => WriteToDBAndGetFailedSource(conn, batch))
  .Merge(100)
  .Select(batch => WriteToDBAndGetFailedSource(conn, batch))
  .Merge(100);

但这当然是错误的,因为:

  1. 这打破了在再次处理失败的批次之前暂停的要求。
  2. 它可能会为数据库生成100多个并发写入请求。
  3. 就像展开一个未知迭代次数的for循环一样 - 没有效果。
  4. 一旦我收集了所有的失败并在循环中重新开始,我也可以打破可观察的monad:

                var src = GetDataSource(filePath, 1048576);
    
                for (;;)
                {
                    var failed = await src
                        .Select(batch => WriteToDBAndGetFailedSource(conn, batch))
                        .Merge(100)
                        .ToList();
                    if (failed.Count == 0)
                    {
                        break;
                    }
                    src = failed.ToObservable();
                }
    

    但是我想知道如果我能留在可观察的monad中我能做得更好。

2 个答案:

答案 0 :(得分:1)

认为这可能会成功

public static IObservable<T> ProcessAll<T>(this IObservable<T> source, Func<T, IObservable<T>> processor, int mergeCount, TimeSpan failureDelay)
{
    return Observable.Create<T>(
        observer =>
            {
                var failed = new Subject<T>();

                return source.Merge(failed)
                        .Select(processor)
                        .Merge(mergeCount)
                        .Delay(failureDelay)
                        .Subscribe(failed.OnNext, observer.OnError, observer.OnCompleted);
            });
}

并像这样使用它:

GetDataSource(filePath, 1048576)
  .ProcessAll(batch => WriteToDBAndGetFailedSource(conn, batch), 100, TimeSpan.FromMilliseconds(500))
  .Subscribe();

ProcessAll是一个可怕的名字,但它是星期五晚上,我无法想到一个更好的名字。

答案 1 :(得分:0)

使用Observable.Buffer.允许您缓冲,直到您有100条记录要发送,或者直到X时间已经过去。

或者,Observable.Interval只会触发每个X时间跨度。您可以在处理发布事件时添加并发限制。

只要有要发布的对象,其中任何一个都应该重复发射。