我有以下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);
但这当然是错误的,因为:
一旦我收集了所有的失败并在循环中重新开始,我也可以打破可观察的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中我能做得更好。
答案 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时间跨度。您可以在处理发布事件时添加并发限制。
只要有要发布的对象,其中任何一个都应该重复发射。