正确终止卡住的Couchbase Observable

时间:2015-12-27 16:35:54

标签: java couchbase rx-java

我正在尝试根据某些约束以快速方式删除一批couchbase文档(或者如果不满足约束则更新文档)。根据我的术语,每个删除被称为“包裹”。

执行时,我遇到了一个非常奇怪的行为 - 负责此任务的线程开始按预期工作几次迭代(充其量)。在此“宽限期”之后,couchbase会“卡住”而Observable不会调用其Subscriber的任何方法(onNextonComplete,{{1在定义的30秒内。

onError超时发生时(参见下面的实现),该方法返回但latch仍在执行(我注意到,当它停止打印调试消息时,断点超出此范围方法)。 我怀疑沙发基地被卡住,因为几秒钟之后,许多Observable被留下某种“幽灵”状态 - 活着并向他们的Observable报告,而Subscriber又没有任何关系,因为创建它们的方法已经完成,最终导致java.lang.OutOfMemoryError: GC overhead limit exceeded

我不知道我在这里声称的是否有道理,但我想不出这种行为的另一个原因。 我应该如何在超时后正确终止Observable?我是不是该?还有其他方法吗?

public List<InfoParcel> upsertParcels(final Collection<InfoParcel> parcels) {
    final CountDownLatch latch = new CountDownLatch(parcels.size());

    final List<JsonDocument> docRetList = new LinkedList<JsonDocument>();
    Observable<JsonDocument> obs = Observable
            .from(parcels)
            .flatMap(parcel ->
                        Observable.defer(() -> 
                            {
                                return bucket.async().get(parcel.key).firstOrDefault(null);
                            })
                            .map(doc -> {
                                // In-memory manipulation of the document
                                return updateDocs(doc, parcel);
                            })
                            .flatMap(doc -> {
                                boolean shouldDelete = ... // Decide by inner logic
                                if (shouldDelete) {
                                    if (doc.cas() == 0) {
                                        return Observable.just(doc);
                                    }
                                    return bucket.async().remove(doc);
                                }
                                return (doc.cas() == 0 ? bucket.async().insert(doc) : bucket.async().replace(doc));
                            })
            );

    obs.subscribe(new Subscriber<JsonDocument>() {
                @Override
                public void onNext(JsonDocument doc) {
                    docRetList.add(doc);
                    latch.countDown();
                }

                @Override
                public void onCompleted() {
                    // Due to a bug in RxJava, onError() / retryWhen() does not intercept exceptions thrown from within the map/flatMap methods.
                    // Therefore, we need to recalculate the "conflicted" parcels and send them for update again. 
                    while(latch.getCount() > 0) {
                        latch.countDown();
                    }
                }

                @Override
                public void onError(Throwable e) {
                    // Same reason as above
                    while (latch.getCount() > 0) {
                        latch.countDown();
                    }
                }
            };
    );

    latch.await(30, TimeUnit.SECONDS);

    // Recalculating remaining failed parcels and returning them for another cycle of this method (there's a loop outside)
}

2 个答案:

答案 0 :(得分:0)

我认为这确实是因为使用倒计时锁存器并不会向数据源发出数据处理流应该停止的信号。

您可以使用toList().timeout(30, TimeUnit.SECONDS).toBlocking().single()而不是收集(未同步且因此不安全)的外部列表以及使用countdownLatch来使用更多rxjava。

这将阻止,直到返回您的文档列表。

答案 1 :(得分:0)

在代码中创建沙发床环境时,请将CalculationPoolSize设置为较大的值。当Couchbase客户端使用异步方式用尽线程时,它将停止工作,并且永远不会调用回调。