use of com.google.apphosting.datastore.DatastoreV3Pb.GetResponse in project appengine-java-standard by GoogleCloudPlatform.
the class RemoteApiServlet method executeTx.
private byte[] executeTx(Request request) {
TransactionRequest txRequest = new TransactionRequest();
parseFromBytes(txRequest, request.getRequestAsBytes());
byte[] tx = beginTransaction(txRequest.isAllowMultipleEg());
List<Precondition> preconditions = txRequest.preconditions();
// Check transaction preconditions
if (!preconditions.isEmpty()) {
GetRequest getRequest = new GetRequest();
for (Precondition precondition : preconditions) {
OnestoreEntity.Reference key = precondition.getKey();
OnestoreEntity.Reference requestKey = getRequest.addKey();
requestKey.mergeFrom(key);
}
GetResponse getResponse = txGet(tx, getRequest);
List<GetResponse.Entity> entities = getResponse.entitys();
// TODO: Consider supporting deferred gets here.
assert (entities.size() == preconditions.size());
for (int i = 0; i < entities.size(); i++) {
// Throw an exception if any of the Entities don't match the Precondition specification.
assertEntityResultMatchesPrecondition(entities.get(i), preconditions.get(i));
}
}
// Preconditions OK.
// Perform puts.
// a serialized VoidProto
byte[] res = new byte[0];
if (txRequest.hasPuts()) {
PutRequest putRequest = txRequest.getPuts();
parseFromBytes(putRequest.getMutableTransaction(), tx);
res = ApiProxy.makeSyncCall("datastore_v3", "Put", putRequest.toByteArray());
}
// Perform deletes.
if (txRequest.hasDeletes()) {
DeleteRequest deleteRequest = txRequest.getDeletes();
parseFromBytes(deleteRequest.getMutableTransaction(), tx);
ApiProxy.makeSyncCall("datastore_v3", "Delete", deleteRequest.toByteArray());
}
// Commit transaction.
ApiProxy.makeSyncCall("datastore_v3", "Commit", tx);
return res;
}
use of com.google.apphosting.datastore.DatastoreV3Pb.GetResponse in project appengine-java-standard by GoogleCloudPlatform.
the class RemoteApiServlet method txGet.
private static GetResponse txGet(byte[] tx, GetRequest request) {
parseFromBytes(request.getMutableTransaction(), tx);
GetResponse response = new GetResponse();
byte[] resultBytes = ApiProxy.makeSyncCall("datastore_v3", "Get", request.toByteArray());
parseFromBytes(response, resultBytes);
return response;
}
use of com.google.apphosting.datastore.DatastoreV3Pb.GetResponse in project appengine-java-standard by GoogleCloudPlatform.
the class AsyncDatastoreServiceImpl method doBatchGet.
@Override
protected final Future<Map<Key, Entity>> doBatchGet(@Nullable Transaction txn, final Set<Key> keysToGet, final Map<Key, Entity> resultMap) {
// Initializing base request.
final GetRequest baseReq = new GetRequest();
baseReq.setAllowDeferred(true);
if (txn != null) {
TransactionImpl.ensureTxnActive(txn);
baseReq.setTransaction(InternalTransactionV3.toProto(txn));
}
if (datastoreServiceConfig.getReadPolicy().getConsistency() == EVENTUAL) {
baseReq.setFailoverMs(ARBITRARY_FAILOVER_READ_MS);
// Allows the datastore to always use READ_CONSISTENT.
baseReq.setStrong(false);
}
final boolean shouldUseMultipleBatches = txn == null && datastoreServiceConfig.getReadPolicy().getConsistency() != EVENTUAL;
// Batch and issue the request(s).
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) {
// Should never happen because we are passing null for the timeout params.
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, @Nullable Long timeout, @Nullable TimeUnit timeoutUnit) throws ExecutionException, InterruptedException, TimeoutException {
// Use a while (true) loop so that we can issue followup requests for any deferred keys.
while (true) {
List<Reference> deferredRefs = Lists.newLinkedList();
// roundtrips)
for (Future<GetResponse> currentFuture : currentFutures) {
GetResponse resp = getFutureWithOptionalTimeout(currentFuture, timeout, timeoutUnit);
addEntitiesToResultMap(resp);
deferredRefs.addAll(resp.deferreds());
}
if (deferredRefs.isEmpty()) {
// Done.
break;
}
// Some keys were deferred. Issue followup requests, and loop again.
Iterator<GetRequest> followupBatches = getByReferenceBatcher.getBatches(deferredRefs, baseReq, baseReq.getSerializedSize(), shouldUseMultipleBatches);
currentFutures = getByReferenceBatcher.makeCalls(followupBatches);
}
}
/**
* Convenience method to get the result of a Future and optionally specify a timeout.
*
* @param future the Future to get.
* @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.
* @return the result of the Future.
* @throws TimeoutException will only ever be thrown if a timeout is provided.
*/
private GetResponse getFutureWithOptionalTimeout(Future<GetResponse> future, @Nullable Long timeout, @Nullable TimeUnit timeoutUnit) throws ExecutionException, InterruptedException, TimeoutException {
if (timeout == null) {
return future.get();
} else {
return future.get(timeout, timeoutUnit);
}
}
/**
* Adds the Entities from the GetResponse to the resultMap. Will omit Keys that were
* missing. Handles Keys with different App Ids from the Entity.Key. See {@link
* #findKeyFromRequestIgnoringAppId(Reference)}
*/
private void addEntitiesToResultMap(GetResponse response) {
for (GetResponse.Entity entityResult : response.entitys()) {
if (entityResult.hasEntity()) {
Entity responseEntity = EntityTranslator.createFromPb(entityResult.getEntity());
Key responseKey = responseEntity.getKey();
// Hack for Remote API which rewrites App Ids on Keys.
if (!keysToGet.contains(responseKey)) {
responseKey = findKeyFromRequestIgnoringAppId(entityResult.getEntity().getKey());
}
resultMap.put(responseKey, responseEntity);
}
// Else, the Key was missing.
}
}
/**
* This is a hack to support calls going through the Remote API. The problem is:
*
* <p>The requested Key may have a local app id. The returned Entity may have a remote app
* id.
*
* <p>In this case, we want to return a Map.Entry with {LocalKey, RemoteEntity}. This way,
* the user can always do map.get(keyFromRequest).
*
* <p>This method will find the corresponding requested LocalKey for a RemoteKey by
* ignoring the AppId field.
*
* <p>Note that we used to be able to rely on the order of the Response Entities matching
* the order of Request Keys. We can no longer do so with the addition of Deferred
* results.
*
* @param referenceFromResponse the reference from the Response that did not match any of
* the requested Keys. (May be mutated.)
* @return the Key from the request that corresponds to the given Reference from the
* Response (ignoring AppId.)
*/
private Key findKeyFromRequestIgnoringAppId(Reference referenceFromResponse) {
// We'll create this Map lazily the first time, then cache it for future calls.
if (keyMapIgnoringAppId == null) {
keyMapIgnoringAppId = Maps.newHashMap();
for (Key requestKey : keysToGet) {
Reference requestKeyAsRefWithoutApp = KeyTranslator.convertToPb(requestKey).clearApp();
keyMapIgnoringAppId.put(requestKeyAsRefWithoutApp, requestKey);
}
}
// Note: mutating the input ref, but that's ok.
Key result = keyMapIgnoringAppId.get(referenceFromResponse.clearApp());
if (result == null) {
// TODO: What should we do here?
throw new DatastoreFailureException("Internal error");
}
return result;
}
});
}
use of com.google.apphosting.datastore.DatastoreV3Pb.GetResponse in project appengine-java-standard by GoogleCloudPlatform.
the class LocalDatastoreService method get.
public GetResponse get(@SuppressWarnings("unused") Status status, GetRequest request) {
GetResponse response = new GetResponse();
LiveTxn liveTxn = null;
for (Reference key : request.keys()) {
validatePathComplete(key);
String app = key.getApp();
Path groupPath = getGroup(key);
GetResponse.Entity responseEntity = response.addEntity();
Profile profile = getOrCreateProfile(app);
synchronized (profile) {
Profile.EntityGroup eg = profile.getGroup(groupPath);
if (request.hasTransaction()) {
if (liveTxn == null) {
liveTxn = profile.getTxn(request.getTransaction().getHandle());
}
// this will throw an exception if we attempt to read from
// the wrong entity group
eg.addTransaction(liveTxn);
}
boolean eventualConsistency = request.hasFailoverMs() && liveTxn == null;
EntityProto entity = pseudoKinds.get(liveTxn, eg, key, eventualConsistency);
if (entity == PseudoKinds.NOT_A_PSEUDO_KIND) {
VersionedEntity versionedEntity = eg.get(liveTxn, key, eventualConsistency);
if (versionedEntity == null) {
entity = null;
if (!eventualConsistency) {
responseEntity.setVersion(profile.getReadTimestamp());
}
} else {
entity = versionedEntity.entityProto();
responseEntity.setVersion(versionedEntity.version());
}
}
if (entity != null) {
responseEntity.getMutableEntity().copyFrom(entity);
postprocessEntity(responseEntity.getMutableEntity());
} else {
responseEntity.getMutableKey().copyFrom(key);
}
// Give all entity groups with unapplied jobs the opportunity to catch
// up. Note that this will not impact the result we're about to return.
profile.groom();
}
}
return response;
}
use of com.google.apphosting.datastore.DatastoreV3Pb.GetResponse in project appengine-java-standard by GoogleCloudPlatform.
the class RemoteApiServlet method executeTxQuery.
private byte[] executeTxQuery(Request request) {
RemoteApiPb.TransactionQueryResult result = new RemoteApiPb.TransactionQueryResult();
Query query = new Query();
parseFromBytes(query, request.getRequestAsBytes());
if (!query.hasAncestor()) {
throw new ApiProxy.ApplicationException(BAD_REQUEST.getValue(), "No ancestor in transactional query.");
}
// Make __entity_group__ key
OnestoreEntity.Reference egKey = result.getMutableEntityGroupKey().mergeFrom(query.getAncestor());
OnestoreEntity.Path.Element root = egKey.getPath().getElement(0);
egKey.getMutablePath().clearElement().addElement(root);
OnestoreEntity.Path.Element egElement = new OnestoreEntity.Path.Element();
egElement.setType("__entity_group__").setId(1);
egKey.getMutablePath().addElement(egElement);
// And then perform the transaction with the ancestor query and __entity_group__ fetch.
byte[] tx = beginTransaction(false);
parseFromBytes(query.getMutableTransaction(), tx);
byte[] queryBytes = ApiProxy.makeSyncCall("datastore_v3", "RunQuery", query.toByteArray());
parseFromBytes(result.getMutableResult(), queryBytes);
GetRequest egRequest = new GetRequest();
egRequest.addKey(egKey);
GetResponse egResponse = txGet(tx, egRequest);
if (egResponse.getEntity(0).hasEntity()) {
result.setEntityGroup(egResponse.getEntity(0).getEntity());
}
rollback(tx);
return result.toByteArray();
}
Aggregations