3
\$\begingroup\$

I'm trying to implement a batch API where requests are associated with an id. In the batch response, the individual success/failure responses should be associated to their corresponding request id.

The code looks like this

class Request {};
class Response {};
class BatchResponse {};
folly::SemiFuture<Response> handle(Request foo) {
  return folly::makeSemiFuture(Response{});
}

folly::SemiFuture<BatchResponse> batchAPI() { 
  // input batched requests
  std::map<int, Request> requests = {{1, Request{}}, {2, Request{}}};

  // a collection of handled requests with their ids
  std::vector<folly::SemiFuture<std::pair<int, folly::Try<Response>>>> responses;
  responses.reserve(requests.size());

  // loop and handle requests
  for (auto& entry : requests) {
    int requestId = entry.first;
    // associate response with request id
    responses.push_back(
        handle(entry.second)
            .via(folly::getCPUExecutor().get())
            .thenTry([requestId](const folly::Try<Response>&& tryBar) {
              return std::make_pair(requestId, tryBar);
            }));
  }

  // this is the returned SemiFuture<BatchResponse>
  return folly::collectAllSemiFuture(responses)
      .via(folly::getCPUExecutor().get())
      .thenValue([](const auto& tryResponses) {
        BatchResponse response;
        for (const auto& tryPair : tryResponses) {
          // guaranteed to be successful
          std::pair<int, folly::Try<Response>> pair = tryPair.value();
          int requestId = pair.first;
          folly::Try<Response> handleResult = pair.second;

          if (handleResult.hasValue()) {
            // add value and requestId to response
          } else {
            // add exception and requestId to response
          }
        }
        return response;
      });
}

I don't like that I have to capture the requestId in the response future and transform into a pair of the two. As result, I have to unwrap the std::pair with tryPair.value(); since I know it'll be successful.

An alternative I've considered is maintaining a parallel std::vector of the request ids and capturing that in the SemiFuture returned by collectAllSemiFuture, since they would be in the same order.

Are there cleaner designs?

\$\endgroup\$
  • \$\begingroup\$ If it can't fail, why not simply use SemiFuture<Response> with .thenValue()? \$\endgroup\$ – Yuushi Sep 17 '18 at 7:42
  • \$\begingroup\$ @Yuushi In which place? In the SemiFutures returned by handle, I need to use thenTry to preserve the possible exception. I do use thenValue for the future returned by collectAllSemiFuture. \$\endgroup\$ – Savior Sep 17 '18 at 15:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.