简体   繁体   English

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

[英]In Rx.Net how can I implement an observable feedback loop until the feedback is exhausted?

I have the following API: 我有以下API:

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

It attempts to write the batch into database. 它试图将批处理写入数据库。 If fails, the entire batch is returned, otherwise the returned observable is empty. 如果失败,则返回整个批处理,否则返回的observable为空。

I also have a source producing the batches: 我也有一个产生批次的来源:

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

Now, I can combine them like this: 现在,我可以像这样组合它们:

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

This is going to write all the batches (at most 100 concurrently) and return an observable of failed batches. 这将编写所有批次(最多100个并发)并返回可观察到的失败批次。

What I really want is to feed the failed batches back into the source of the batches after a certain pause , could be while the original source is still producing batches. 我真正想要的是在一段暂停之后将失败的批次送回批次的来源,可能是在原始来源仍在生产批次时。 I could, of course, write something like this: 当然,我可以这样写:

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

But it is wrong, of course, because: 但当然,这是错误的,因为:

  1. This breaks the requirement to have a pause before failed batches are handled again. 这打破了在再次处理失败的批次之前暂停的要求。
  2. It may generate more than 100 concurrent write requests to the database. 它可能会为数据库生成100多个并发写入请求。
  3. It is like unwinding a for-loop with an unknown count of iterations - unproductive. 这就像解开一个带有未知迭代次数的for循环 - 没有效果。

I can also break out of the observable monad once I have collected all the failures and start all over again inside a loop: 一旦我收集了所有的失败并在循环中重新开始,我也可以打破可观察的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();
            }

But I wonder if I can do better while staying within the observable monad. 但是我想知道我是否可以在可观察的monad中保持更好的状态。

I think this might do the trick 认为这可能会成功

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);
            });
}

And use it like this: 并像这样使用它:

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

ProcessAll is a horrible name but it's Friday night and I can't think of a better one. ProcessAll是一个可怕的名字,但它是星期五晚上,我想不出更好的名字。

Use an Observable.Buffer. 使用Observable.Buffer。 That allows you to buffer until you have 100 records to send, or until X amount of time has passed. 这允许您缓冲,直到您有100条记录要发送,或者直到X时间已经过去。

Alternatively, an Observable.Interval will simply fire every X time span. 或者, Observable.Interval将只触发每个X时间跨度。 You can add the concurrency limit when you handle the publish event. 您可以在处理发布事件时添加并发限制。

Either of these should fire repeatedly so long as there objects to be published. 只要有要发布的对象,这些中的任何一个都应该重复发射。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM