具有可实现未来的Dynamo DB结果分页

时间:2019-09-04 11:37:07

标签: amazon-web-services amazon-dynamodb completable-future

我正在查询Dynamo DB以获取给定的主键。主键包含两个UUID字段(fieldUUID1,fieldUUID2)。 对于上面带有值列表的主键组合,我有很多查询要执行。为此,我将ExecutorService与Asynchronous CompleteableFuture配合使用,线程池的大小为4。

在所有查询返回结果CompletableFuture<Object>之后,我使用allOf可完成未来的方法将其加入,该方法可确保所有查询执行均完成,并且给我CompletableFuture<void> ,使用该流我收到CompletableFuture<List<Object>>

如果某些查询导致结果分页,即返回lastEvaluatedKey,则我无法知道哪个查询请求返回了此结果。

如果我在收到`CompletableFuture的同时进行.get()调用,这将是一个阻塞操作,无法实现使用异步的目的。我有办法解决这种情况吗?

示例:

我可以尝试thenCompose方法,但是我怎么知道在没有lastEvaluatedKey的情况下需要停在什么地方。

for (final QueryRequest queryRequest : queryRequests) {
    final CompletableFuture<QueryResult> futureResult =
        CompletableFuture.supplyAsync(() ->
            dynamoDBClient.query(queryRequest), executorService));

    if (futureResult == null) {
        continue;
    }

    futures.add(futureResult);
}

// Wait for completion of all of the Futures provided
final CompletableFuture<Void> allfuture = CompletableFuture
    .allOf(futures.toArray(new CompletableFuture[futures.size()]));

// The return type of the CompletableFuture.allOf() is a
// CompletableFuture<Void>. The limitation of this method is that it does not
// return the combined results of all Futures. Instead we have to manually get
// results from Futures. CompletableFuture.join() method and Java 8 Streams API
// makes it simple:
final CompletableFuture<List<QueryResult>> allFutureList = allfuture.thenApply(val -> {
    return futures.stream().map(f -> f.join()).collect(Collectors.toList());
});


final List<QueryOutcome> completableResults = new ArrayList<>();
try {
    try {
        // at this point all the Futures should be done, because we already executed
        // CompletableFuture.allOf method.
        final List<QueryResult> returnedResult = allFutureList.get();
        for (final QueryResult queryResult : returnedResult) {
            if (MapUtils.isNotEmpty(queryResult.getLastEvaluatedKey()) {
                // how to get hold of original request  and include last evaluated key ?
            }
        }
    } finally {

    }
} finally {

}

我可以依赖.get()方法,但这将是一个阻塞调用。

1 个答案:

答案 0 :(得分:0)

您需要的快速解决方案是更改public function createRecipe(Request $request ) { $data = ([ 'recipe_name' => 'Fried Chicken', 'ingredient_id' => ['1', '3', '4'], ]) Recipe::create($data); return redirect()->route('recipe.index')->withStatus(__('Recipe has been added.')); } } 列表。除了可以存储futures之外,您还可以更改为存储CompletableFuture<QueryResult>,其中CompletableFuture<RequestAndResult>是包含RequestAndResultQueryRequest的简单数据类。为此,您需要更改第一个循环。

然后,QueryResult完成后,您可以遍历allfuture并获得对请求和结果的访问权限。

但是,这里有一个更深层次的问题。一旦可以访问原始版本futures,您打算做什么?我的猜测是,您想发出一个QueryRequest设置为响应exclusiveStartKey所设置的后续请求。这意味着您将等待所有原始查询完成,然后才发出下一个查询。这效率低下:如果查询返回的是lastEvaluatedKey,则您希望尽快发出其后续查询。

为实现这一目标,我建议您引入一种新方法,该方法采用单个lastEvaluatedKey对象并返回一个QueryRequest。其实现大致如下:

  • 使用给定的请求发出查询
  • 结果到达后对其进行检查。如果其CompletableFuture<QueryResult>为空,则返回该方法的结果
  • 否则,请更新request.exclusiveStartKey并返回第一步。

是的,用lastEvaluatedKey(相比于阻塞代码)很难做到这一点,但完全可行。

一旦有了该方法,您的代码就需要针对CompletableFutures中的每个请求调用一次此方法,将返回的queryRequests放入列表中,然后对CompletableFuture进行操作那个清单。一旦allOf future完成,您就可以使用结果-无需发出后续查询。