Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.yahoo.vespa.config.server.rpc.GetConfigProcessor Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.rpc;
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.component.Version;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.di.config.ApplicationBundlesConfig;
import com.yahoo.jrt.Request;
import com.yahoo.net.HostName;
import com.yahoo.vespa.config.ConfigPayload;
import com.yahoo.vespa.config.ErrorCode;
import com.yahoo.vespa.config.PayloadChecksums;
import com.yahoo.vespa.config.UnknownConfigIdException;
import com.yahoo.vespa.config.protocol.ConfigResponse;
import com.yahoo.vespa.config.protocol.JRTServerConfigRequest;
import com.yahoo.vespa.config.protocol.Payload;
import com.yahoo.vespa.config.protocol.Trace;
import com.yahoo.vespa.config.protocol.VespaVersion;
import com.yahoo.vespa.config.server.GetConfigContext;
import com.yahoo.vespa.config.server.UnknownConfigDefinitionException;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.yahoo.vespa.config.ErrorCode.APPLICATION_NOT_LOADED;
import static com.yahoo.vespa.config.ErrorCode.UNKNOWN_VESPA_VERSION;
import static com.yahoo.vespa.config.protocol.SlimeConfigResponse.fromConfigPayload;
/**
* @author hmusum
*/
class GetConfigProcessor implements Runnable {
private static final Logger log = Logger.getLogger(GetConfigProcessor.class.getName());
private static final String localHostName = HostName.getLocalhost();
private static final PayloadChecksums emptyApplicationBundlesConfigChecksums =
PayloadChecksums.fromPayload(Payload.from(ConfigPayload.fromInstance(new ApplicationBundlesConfig.Builder().build())));
private final JRTServerConfigRequest request;
/* True only when this request has expired its server timeout and we need to respond to the client */
private final boolean forceResponse;
private final RpcServer rpcServer;
private String logPre = "";
GetConfigProcessor(RpcServer rpcServer, JRTServerConfigRequest request, boolean forceResponse) {
this.rpcServer = rpcServer;
this.request = request;
this.forceResponse = forceResponse;
}
private void respond(JRTServerConfigRequest request) {
Request req = request.getRequest();
if (req.isError()) {
Level logLevel = Set.of(APPLICATION_NOT_LOADED, UNKNOWN_VESPA_VERSION).contains(req.errorCode()) ? Level.FINE : Level.INFO;
log.log(logLevel, () -> logPre + req.errorMessage());
}
rpcServer.respond(request);
}
private void handleError(JRTServerConfigRequest request, int errorCode, String message) {
String target = "(unknown)";
try {
target = request.getRequest().target().toString();
} catch (IllegalStateException e) {
//ignore when no target
}
request.addErrorResponse(errorCode, logPre + "Failed request (" + message + ") from " + target);
respond(request);
}
// TODO: Increment statistics (Metrics) failed counters when requests fail
public Optional resolveConfig(JRTServerConfigRequest request) {
// Request has already been detached
if ( ! request.validateParameters()) {
// Error code is set in verifyParameters if parameters are not OK.
log.log(Level.WARNING, "Parameters for request " + request + " did not validate: " + request.errorCode() + " : " + request.errorMessage());
respond(request);
return Optional.empty();
}
Trace trace = request.getRequestTrace();
debugLog(trace, "GetConfigProcessor.run() on " + localHostName);
Optional tenant = rpcServer.resolveTenant(request, trace);
// If we are certain that this request is from a node that no longer belongs to this application,
// fabricate an empty request to cause the sentinel to stop all running services
if (rpcServer.canReturnEmptySentinelConfig() && rpcServer.allTenantsLoaded() && tenant.isEmpty() && isSentinelConfigRequest(request)) {
returnEmpty(request);
return Optional.empty();
}
GetConfigContext context = rpcServer.createGetConfigContext(tenant, request, trace);
if (context.isEmpty() || ! context.requestHandler().hasApplication(context.applicationId(), Optional.empty())) {
handleError(request, APPLICATION_NOT_LOADED, "No application exists");
return Optional.empty();
}
logPre = TenantRepository.logPre(context.applicationId());
Optional vespaVersion = rpcServer.useRequestVersion() ?
request.getVespaVersion().map(VespaVersion::toString).map(Version::fromString) :
Optional.empty();
debugLog(trace, "Using version " + printableVespaVersion(vespaVersion));
if ( ! context.requestHandler().hasApplication(context.applicationId(), vespaVersion)) {
handleError(request, ErrorCode.UNKNOWN_VESPA_VERSION, "Unknown Vespa version in request: " + printableVespaVersion(vespaVersion));
return Optional.empty();
}
ConfigResponse config;
try {
config = rpcServer.resolveConfig(request, context, vespaVersion);
} catch (UnknownConfigDefinitionException e) {
handleError(request, ErrorCode.UNKNOWN_DEFINITION, "Unknown config definition " + request.getConfigKey());
return Optional.empty();
} catch (UnknownConfigIdException e) {
handleError(request, ErrorCode.ILLEGAL_CONFIGID, "Illegal config id " + request.getConfigKey().getConfigId());
return Optional.empty();
} catch (Throwable e) {
log.log(Level.SEVERE, "Unexpected error handling config request", e);
handleError(request, ErrorCode.INTERNAL_ERROR, "Internal error " + e.getMessage());
return Optional.empty();
}
// config == null is not an error, but indicates that the config will be returned later.
if ((config != null) && ( ! config.getPayloadChecksums().matches(request.getRequestConfigChecksums())
|| config.hasNewerGeneration(request)
|| forceResponse)) {
if ( ApplicationBundlesConfig.class.equals(request.getConfigKey().getConfigClass()) // If it's a Java container ...
&& ! context.requestHandler().compatibleWith(vespaVersion, context.applicationId()) // ... with a runtime version incompatible with the deploying version ...
&& ! emptyApplicationBundlesConfigChecksums.matches(config.getPayloadChecksums())) { // ... and there actually are incompatible user bundles, then return no config:
handleError(request, ErrorCode.INCOMPATIBLE_VESPA_VERSION, "Version " + printableVespaVersion(vespaVersion) + " is binary incompatible with the latest deployed version");
return Optional.empty();
}
// debugLog(trace, "config response before encoding:" + config.toString());
request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.applyOnRestart(), config.getPayloadChecksums());
debugLog(trace, "return response: " + request.getShortDescription());
respond(request);
} else {
debugLog(trace, "delaying response " + request.getShortDescription());
return Optional.of(new DelayedConfig(context, config != null ? config.getGeneration() : 0));
}
return Optional.empty();
}
@Override
public void run() {
Optional delayed = resolveConfig(request);
delayed.ifPresent(d -> {
GetConfigContext context = d.context();
if (rpcServer.hasNewerGeneration(context.applicationId(), d.generation()))
// This will ensure that if the config activation train left the station while I was boarding,
// we will resolve config again with new generation
resolveConfig(request);
else
rpcServer.delayResponse(request, context);
});
}
private boolean isSentinelConfigRequest(JRTServerConfigRequest request) {
return request.getConfigKey().getName().equals(SentinelConfig.getDefName()) &&
request.getConfigKey().getNamespace().equals(SentinelConfig.getDefNamespace());
}
private static String printableVespaVersion(Optional vespaVersion) {
return vespaVersion.map(Version::toFullString).orElse("LATEST");
}
private void returnEmpty(JRTServerConfigRequest request) {
log.log(Level.FINE, () -> "Returning empty sentinel config for request from " + request.getClientHostName());
var emptyPayload = ConfigPayload.fromInstance(new SentinelConfig.Builder().build());
ConfigResponse config = fromConfigPayload(emptyPayload,
0,
false,
PayloadChecksums.fromPayload(Payload.from(emptyPayload)));
request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), false, config.getPayloadChecksums());
respond(request);
}
static boolean logDebug(Trace trace) {
return trace.shouldTrace(RpcServer.TRACELEVEL_DEBUG) || log.isLoggable(Level.FINE);
}
private void debugLog(Trace trace, String message) {
if (logDebug(trace)) {
log.log(Level.FINE, () -> logPre + message);
trace.trace(RpcServer.TRACELEVEL_DEBUG, logPre + message);
}
}
private record DelayedConfig(GetConfigContext context, long generation) {}
}