return new InternalTransactionV3(apiConfig, request.getApp(), future);
}
@Override
protected final Future<Map<Key, Entity>> doBatchGet( Transaction txn, final Set<Key> keysToGet, final Map<Key, Entity> resultMap) {
final GetRequest baseReq = new GetRequest();
baseReq.setAllowDeferred(true);
if (txn != null) {
TransactionImpl.ensureTxnActive(txn);
baseReq.setTransaction(InternalTransactionV3.localTxnToRemoteTxn(txn));
}
if (datastoreServiceConfig.getReadPolicy().getConsistency() == EVENTUAL) {
baseReq.setFailoverMs(ARBITRARY_FAILOVER_READ_MS);
baseReq.setStrong(false);
}
final boolean shouldUseMultipleBatches = getDatastoreType() != MASTER_SLAVE && txn == null
&& datastoreServiceConfig.getReadPolicy().getConsistency() != EVENTUAL;
Iterator<GetRequest> batches = getByKeyBatcher.getBatches(keysToGet, baseReq,
baseReq.getSerializedSize(), shouldUseMultipleBatches);
List<Future<GetResponse>> futures = getByKeyBatcher.makeCalls(batches);
return registerInTransaction(txn, new MultiFuture<GetResponse, Map<Key, Entity>>(futures) {
/**
* A Map from a Reference without an App Id specified to the corresponding Key that the user
* requested. This is a workaround for the Remote API to support matching requested Keys to
* Entities that may be from a different App Id .
*/
private Map<Reference, Key> keyMapIgnoringAppId;
@Override
public Map<Key, Entity> get() throws InterruptedException, ExecutionException {
try {
aggregate(futures, null, null);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
return resultMap;
}
@Override
public Map<Key, Entity> get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
aggregate(futures, timeout, unit);
return resultMap;
}
/**
* Aggregates the results of the given Futures and issues (synchronous) followup requests if
* any results were deferred.
*
* @param currentFutures the Futures corresponding to the batches of the initial GetRequests.
* @param timeout the timeout to use while waiting on the Future, or null for none.
* @param timeoutUnit the unit of the timeout, or null for none.
*/
private void aggregate(
Iterable<Future<GetResponse>> currentFutures, Long timeout, TimeUnit timeoutUnit)
throws ExecutionException, InterruptedException, TimeoutException {
while (true) {
List<Reference> deferredRefs = Lists.newLinkedList();
for (Future<GetResponse> currentFuture : currentFutures) {
GetResponse resp = getFutureWithOptionalTimeout(currentFuture, timeout, timeoutUnit);
addEntitiesToResultMap(resp);
deferredRefs.addAll(resp.deferreds());
}
if (deferredRefs.isEmpty()) {
break;
}
Iterator<GetRequest> followupBatches = getByReferenceBatcher.getBatches(deferredRefs,
baseReq, baseReq.getSerializedSize(), shouldUseMultipleBatches);
currentFutures = getByReferenceBatcher.makeCalls(followupBatches);
}
}
/**