我正在尝试以反应方式从Couchbase提取大型数据集。
我使用spring数据提供的ReactiveCouchbaseRepository
。
public interface ReactiveFooRepository extends ReactiveCouchbaseRepository<Foo, String> {
@Query("#{#n1ql.selectEntity} WHERE ... ORDER BY ...")
Flux<Foo> findAll();
}
在我的服务中,我按以下方式订阅
repository.findAll()
.subscribe(this::process,
t -> LOGGER.error("Failed to process.", t));
在我的测试数据集上,这很好用,但是在生产中,该查询返回了一个大型数据集,并运行了大约20-25秒。
我的理解是,这正是反应式存储库的确切含义:无需进行显式分页等即可消耗大量结果。
但是我得到的是IllegalStateException
Failed to process.
java.lang.IllegalStateException: The content of this Observable (queryRow.13de03e2-9271-47e2-9d56-df01038011f9) is already released. Subscribe earlier or tune the CouchbaseEnvironment#autoreleaseAfter() setting.
...
提高autoreleaseAfter
超时时间似乎不是一个可靠的解决方案。
似乎在发布第一个结果元素之前,将整个结果缓冲在Couchbase中。
我弄错了吗?关于可能的问题或解决方案有什么想法?
我发现的一个问题是订购。我认为Couchbase必须先获取整个结果集才能对其进行排序。
删除ORDER BY
子句后,我可以流式传输结果,但只能在直接使用Java SDK时进行。
以下代码可以正常工作,并立即开始流式传输数据:
public void applyToAllFooAsync(Consumer<Optional<Foo>> consumer) {
String queryString = String.format("SELECT meta().id, _class, field1, field2, ... "
+ "FROM %s "
+ "WHERE ... ",
getQuotedBucketName());
N1qlParams params = N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS).pretty(false);
N1qlQuery query = N1qlQuery.simple(queryString, params);
asyncBucket
.query(query)
.flatMap(AsyncN1qlQueryResult::rows)
.map(this::getFoo)
.forEach(consumer::accept);
}
protected Optional<Foo> getFoo(AsyncN1qlQueryRow row) {
try {
return Optional.of(objectMapper.readValue(row.byteValue(), clazz));
} catch (IOException e) {
LOGGER.warn("Could not map Foo to object.", e);
return Optional.empty();
}
}
applyToAllOperationsAsync(System.out::println));
但是,如果我在ReactiveCouchbaseRepository
中使用完全相同的查询,则需要花费几秒钟的时间,然后抛出上面显示的异常。
有人知道可能源自不同的行为吗? 有人可以指出我在Spring数据代码中使用实际Java SDK的类或方法吗?