in runtime/impl/src/main/java/com/google/apphosting/runtime/ApiProxyImpl.java [392:476]
private Future<byte[]> doAsyncCall(
EnvironmentImpl environment,
String packageName,
String methodName,
byte[] requestBytes,
Double requestDeadlineInSeconds) {
TraceWriter traceWriter = environment.getTraceWriter();
CloudTraceContext currentContext = null;
if (traceWriter != null) {
CloudTraceContext parentContext = CloudTrace.getCurrentContext(environment);
currentContext = traceWriter.startApiSpan(parentContext, packageName, methodName);
// Collects stack trace if required.
if (TraceContextHelper.isStackTraceEnabled(currentContext)
&& environment.getTraceExceptionGenerator() != null) {
StackTraceElement[] stackTrace =
environment
.getTraceExceptionGenerator()
.getExceptionWithRequestId(new Exception(), environment.getRequestId())
.getStackTrace();
traceWriter.addStackTrace(currentContext, stackTrace);
}
}
// Browserchannel messages are actually sent via XMPP, so this cheap hack
// translates the packageName in production. If these two services are
// ever separated, this should be removed.
if (packageName.equals("channel")) {
packageName = "xmpp";
}
double deadlineInSeconds =
deadlineOracle.getDeadline(
packageName, environment.isOfflineRequest(), requestDeadlineInSeconds);
APIRequest.Builder apiRequest =
APIRequest.newBuilder()
.setApiPackage(packageName)
.setCall(methodName)
.setSecurityTicket(environment.getSecurityTicket())
.setPb(ByteString.copyFrom(requestBytes));
if (currentContext != null) {
apiRequest.setTraceContext(TraceContextHelper.toProto2(currentContext));
}
AnyRpcClientContext rpc = apiHost.newClientContext();
long apiSlotWaitTime;
try {
// Get an API slot, waiting if there are already too many threads doing API calls.
// If we do wait for t milliseconds then our deadline is decreased by t.
apiSlotWaitTime = environment.apiRpcStarting(deadlineInSeconds);
deadlineInSeconds -= apiSlotWaitTime / 1000.0;
if (deadlineInSeconds < 0) {
throw new InterruptedException("Deadline was used up while waiting for API RPC slot");
}
} catch (InterruptedException ex) {
long remainingMillis = environment.getRemainingMillis();
String msg = String.format(
"Interrupted waiting for an API RPC slot with %d millis %s soft deadline",
Math.abs(remainingMillis), remainingMillis > 0 ? "until" : "since");
logger.atWarning().withCause(ex).log("%s", msg);
if (remainingMillis <= ATTRIBUTE_TO_DEADLINE_MILLIS) {
return createCancelledFuture(packageName, methodName, DEADLINE_REACHED_SLOT_REASON);
} else {
return createCancelledFuture(packageName, methodName, INTERRUPTED_SLOT_REASON);
}
}
// At this point we have counted the API call against the concurrent limit, so if we get an
// exception starting the asynchronous RPC then we must uncount the API call.
try {
return finishAsyncApiCallSetup(
rpc,
apiRequest.build(),
currentContext,
environment,
packageName,
methodName,
deadlineInSeconds,
apiSlotWaitTime);
} catch (RuntimeException | Error e) {
environment.apiRpcFinished();
logger.atWarning().withCause(e).log("Exception in API call setup");
return Futures.immediateFailedFuture(e);
}
}