summaryrefslogtreecommitdiffstats
path: root/configserver/src
diff options
context:
space:
mode:
Diffstat (limited to 'configserver/src')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ConfigActivationListener.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java)8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelController.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/FileDistributionStatus.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java41
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java33
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java13
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionImpl.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java128
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponses.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java40
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java71
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java80
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java149
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java46
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java14
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplication.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java9
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java15
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java14
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java33
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java34
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java26
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java10
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java11
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java15
36 files changed, 538 insertions, 366 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index ca56a200c2c..2a15f724b29 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -96,6 +96,7 @@ import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -631,15 +632,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
.stream()
.filter(fileReference -> ! fileReferencesInUse.contains(fileReference))
.filter(fileReference -> isLastFileAccessBefore(new File(fileReferencesPath, fileReference), instant))
- .sorted((a, b) -> {
- if (a.equals(b))
- return 0;
- else if (lastAccessed(new File(fileReferencesPath, a))
- .isBefore(lastAccessed(new File(fileReferencesPath, b))))
- return -1;
- else
- return 1;
- })
+ .sorted(Comparator.comparing(a -> lastAccessed(new File(fileReferencesPath, a))))
.collect(Collectors.toList());
}
@@ -804,7 +797,8 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
.map(lock -> new ApplicationTransaction(lock, transaction));
try (var sessionLock = tenant.getApplicationRepo().lock(applicationId)) {
Optional<Session> activeSession = getActiveSession(applicationId);
- CompletionWaiter waiter = session.getSessionZooKeeperClient().createActiveWaiter();
+ var sessionZooKeeperClient = tenant.getSessionRepository().createSessionZooKeeperClient(session.getSessionId());
+ CompletionWaiter waiter = sessionZooKeeperClient.createActiveWaiter();
transaction.add(deactivateCurrentActivateNew(activeSession, session, force));
if (applicationTransaction.isPresent()) {
@@ -911,14 +905,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
sessionsPerTenant.keySet().forEach(tenant -> tenant.getSessionRepository().deleteExpiredSessions(activeSessions));
}
- public int deleteExpiredRemoteSessions(Duration expiryTime) {
- return deleteExpiredRemoteSessions(clock, expiryTime);
- }
-
- public int deleteExpiredRemoteSessions(Clock clock, Duration expiryTime) {
+ public int deleteExpiredRemoteSessions(Clock clock) {
return tenantRepository.getAllTenants()
.stream()
- .map(tenant -> tenant.getSessionRepository().deleteExpiredRemoteSessions(clock, expiryTime))
+ .map(tenant -> tenant.getSessionRepository().deleteExpiredRemoteSessions(clock))
.mapToInt(i -> i)
.sum();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigActivationListener.java
index b41f31d9dcb..f7e9e270b9c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ReloadListener.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigActivationListener.java
@@ -7,13 +7,13 @@ import com.yahoo.vespa.config.server.application.ApplicationSet;
import java.util.Collection;
/**
- * A ReloadListener is used to signal to a component that config has been
- * reloaded. It only exists because the RpcServer cannot distinguish between a
- * successful reload of a new application and a reload of the same application.
+ * A ConfigActivationListener is used to signal to a component that config has been
+ * activated. It only exists because the RpcServer cannot distinguish between a
+ * successful activation of a new application and an activation of the same application.
*
* @author Ulf Lilleengen
*/
-public interface ReloadListener {
+public interface ConfigActivationListener {
/**
* Signals the listener that hosts used by a particular tenant.
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelController.java b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelController.java
index f6ed98d904b..fd939b91388 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelController.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelController.java
@@ -16,7 +16,7 @@ import java.io.StringReader;
/**
* Handler for global configs that must be resolved using the global SuperModel instance. Deals with
- * reloading of config as well.
+ * activation of config as well.
*
* @author Ulf Lilleengen
*/
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
index aa7dceba95c..93bb44e25d3 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
@@ -43,12 +43,12 @@ public class SuperModelRequestHandler implements RequestHandler {
}
/**
- * Signals that config has been reloaded for an {@link com.yahoo.vespa.config.server.application.Application}
+ * Signals that config has been activated for an {@link com.yahoo.vespa.config.server.application.Application}
* belonging to a tenant.
*
- * @param applicationSet The reloaded set of {@link com.yahoo.vespa.config.server.application.Application}.
+ * @param applicationSet The activated set of {@link com.yahoo.vespa.config.server.application.Application}.
*/
- public synchronized void reloadConfig(ApplicationSet applicationSet) {
+ public synchronized void activateConfig(ApplicationSet applicationSet) {
superModelManager.configActivated(applicationSet);
updateHandler();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/FileDistributionStatus.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/FileDistributionStatus.java
index 23df938e0b7..c80faa2375a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/FileDistributionStatus.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/FileDistributionStatus.java
@@ -65,7 +65,7 @@ public class FileDistributionStatus extends AbstractComponent {
HostStatus getHostStatus(String hostname, int port, Duration timeout) {
Target target = supervisor.connect(new Spec(hostname, port));
Request request = new Request("filedistribution.getActiveFileReferencesStatus");
- target.invokeSync(request, timeout.toMillis() / 1000);
+ target.invokeSync(request, timeout);
HostStatus hostStatus = createHostStatusFromResponse(hostname, request);
target.close();
return hostStatus;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
index 28487106268..2a0d62a1c9a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
@@ -14,7 +14,7 @@ import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.GetConfigRequest;
import com.yahoo.vespa.config.protocol.ConfigResponse;
import com.yahoo.vespa.config.server.NotFoundException;
-import com.yahoo.vespa.config.server.ReloadListener;
+import com.yahoo.vespa.config.server.ConfigActivationListener;
import com.yahoo.vespa.config.server.RequestHandler;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.host.HostRegistry;
@@ -27,13 +27,11 @@ import com.yahoo.vespa.curator.CompletionTimeoutException;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.curator.transaction.CuratorTransaction;
-import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.ListFlag;
import com.yahoo.vespa.flags.PermanentFlags;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
-
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Clock;
@@ -69,7 +67,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
private final Executor zkWatcherExecutor;
private final Metrics metrics;
private final TenantName tenant;
- private final ReloadListener reloadListener;
+ private final ConfigActivationListener configActivationListener;
private final ConfigResponseFactory responseFactory;
private final HostRegistry hostRegistry;
private final ApplicationMapper applicationMapper = new ApplicationMapper();
@@ -80,7 +78,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
private final ListFlag<String> incompatibleVersions;
public TenantApplications(TenantName tenant, Curator curator, StripedExecutor<TenantName> zkWatcherExecutor,
- ExecutorService zkCacheExecutor, Metrics metrics, ReloadListener reloadListener,
+ ExecutorService zkCacheExecutor, Metrics metrics, ConfigActivationListener configActivationListener,
ConfigserverConfig configserverConfig, HostRegistry hostRegistry,
TenantFileSystemDirs tenantFileSystemDirs, Clock clock, FlagSource flagSource) {
this.curator = curator;
@@ -91,7 +89,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
this.directoryCache.addListener(this::childEvent);
this.directoryCache.start();
this.metrics = metrics;
- this.reloadListener = reloadListener;
+ this.configActivationListener = configActivationListener;
this.responseFactory = ConfigResponseFactory.create(configserverConfig);
this.tenantMetricUpdater = metrics.getOrCreateMetricUpdater(Metrics.createDimensions(tenant));
this.hostRegistry = hostRegistry;
@@ -213,23 +211,22 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
@Override
public ConfigResponse resolveConfig(ApplicationId appId, GetConfigRequest req, Optional<Version> vespaVersion) {
Application application = getApplication(appId, vespaVersion);
- log.log(Level.FINE, () -> TenantRepository.logPre(appId) + "Resolving for tenant '" + tenant +
- "' with handler for application '" + application + "'");
+ log.log(Level.FINE, () -> TenantRepository.logPre(appId) + "Resolving config");
return application.resolveConfig(req, responseFactory);
}
- private void notifyReloadListeners(ApplicationSet applicationSet) {
+ private void notifyConfigActivationListeners(ApplicationSet applicationSet) {
if (applicationSet.getAllApplications().isEmpty()) throw new IllegalArgumentException("application set cannot be empty");
- reloadListener.hostsUpdated(applicationSet.getAllApplications().get(0).toApplicationInfo().getApplicationId(),
- applicationSet.getAllHosts());
- reloadListener.configActivated(applicationSet);
+ configActivationListener.hostsUpdated(applicationSet.getAllApplications().get(0).toApplicationInfo().getApplicationId(),
+ applicationSet.getAllHosts());
+ configActivationListener.configActivated(applicationSet);
}
/**
* Activates the config of the given app. Notifies listeners
*
- * @param applicationSet the {@link ApplicationSet} to be reloaded
+ * @param applicationSet the {@link ApplicationSet} to be activated
*/
public void activateApplication(ApplicationSet applicationSet, long activeSessionId) {
ApplicationId id = applicationSet.getId();
@@ -239,8 +236,8 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
if (applicationSet.getApplicationGeneration() != activeSessionId)
return; // Application activated a new session before we got here.
- setLiveApp(applicationSet);
- notifyReloadListeners(applicationSet);
+ setActiveApp(applicationSet);
+ notifyConfigActivationListeners(applicationSet);
}
}
@@ -257,7 +254,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
if (hasApplication(applicationId)) {
applicationMapper.remove(applicationId);
hostRegistry.removeHostsForKey(applicationId);
- reloadListenersOnRemove(applicationId);
+ configActivationListenersOnRemove(applicationId);
tenantMetricUpdater.setApplications(applicationMapper.numApplications());
metrics.removeMetricUpdater(Metrics.createDimensions(applicationId));
getRemoveApplicationWaiter(applicationId).notifyCompletion();
@@ -279,12 +276,12 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
}
}
- private void reloadListenersOnRemove(ApplicationId applicationId) {
- reloadListener.hostsUpdated(applicationId, hostRegistry.getHostsForKey(applicationId));
- reloadListener.applicationRemoved(applicationId);
+ private void configActivationListenersOnRemove(ApplicationId applicationId) {
+ configActivationListener.hostsUpdated(applicationId, hostRegistry.getHostsForKey(applicationId));
+ configActivationListener.applicationRemoved(applicationId);
}
- private void setLiveApp(ApplicationSet applicationSet) {
+ private void setActiveApp(ApplicationSet applicationSet) {
ApplicationId id = applicationSet.getId();
Collection<String> hostsForApp = applicationSet.getAllHosts();
hostRegistry.update(id, hostsForApp);
@@ -337,7 +334,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
}
/**
- * Given baseIdSegment search/ and id search/qrservers/default.0, return search/qrservers
+ * Given baseIdSegment search/ and id search/container/default.0, return search/container
* @return id segment with one extra level from the id appended
*/
String appendOneLevelOfId(String baseIdSegment, String id) {
@@ -402,7 +399,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
@Override
public void verifyHosts(ApplicationId applicationId, Collection<String> newHosts) {
hostRegistry.verifyHosts(applicationId, newHosts);
- reloadListener.verifyHostsAreAvailable(applicationId, newHosts);
+ configActivationListener.verifyHostsAreAvailable(applicationId, newHosts);
}
public HostValidator<ApplicationId> getHostValidator() {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index c31d4603353..068323f7784 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -197,7 +197,6 @@ public class ModelContextImpl implements ModelContext {
private final boolean useV8GeoPositions;
private final int maxCompactBuffers;
private final List<String> ignoredHttpUserAgents;
- private final boolean enableServerOcspStapling;
private final String mergeThrottlingPolicy;
private final double persistenceThrottlingWsDecrementFactor;
private final double persistenceThrottlingWsBackoff;
@@ -211,6 +210,16 @@ public class ModelContextImpl implements ModelContext {
private final boolean enableProxyProtocolMixedMode;
private final boolean sharedStringRepoNoReclaim;
private final String logFileCompressionAlgorithm;
+ private final boolean mbus_dispatch_on_decode;
+ private final boolean mbus_dispatch_on_encode;
+ private final int mbus_threads;
+ private final int mbus_network_threads;
+ private int mbus_java_num_targets;
+ private int mbus_java_events_before_wakeup;
+ private int mbus_cpp_num_targets;
+ private int mbus_cpp_events_before_wakeup;
+ private int rpc_num_targets;
+ private int rpc_events_before_wakeup;
public FeatureFlags(FlagSource source, ApplicationId appId, Version version) {
this.defaultTermwiseLimit = flagValue(source, appId, version, Flags.DEFAULT_TERM_WISE_LIMIT);
@@ -224,6 +233,10 @@ public class ModelContextImpl implements ModelContext {
this.useAsyncMessageHandlingOnSchedule = flagValue(source, appId, version, Flags.USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE);
this.feedConcurrency = flagValue(source, appId, version, Flags.FEED_CONCURRENCY);
this.feedNiceness = flagValue(source, appId, version, Flags.FEED_NICENESS);
+ this.mbus_dispatch_on_decode = flagValue(source, appId, version, Flags.MBUS_DISPATCH_ON_DECODE);
+ this.mbus_dispatch_on_encode = flagValue(source, appId, version, Flags.MBUS_DISPATCH_ON_ENCODE);
+ this.mbus_threads = flagValue(source, appId, version, Flags.MBUS_NUM_THREADS);
+ this.mbus_network_threads = flagValue(source, appId, version, Flags.MBUS_NUM_NETWORK_THREADS);
this.allowedAthenzProxyIdentities = flagValue(source, appId, version, Flags.ALLOWED_ATHENZ_PROXY_IDENTITIES);
this.maxActivationInhibitedOutOfSyncGroups = flagValue(source, appId, version, Flags.MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS);
this.jvmOmitStackTraceInFastThrow = type -> flagValueAsInt(source, appId, version, type, PermanentFlags.JVM_OMIT_STACK_TRACE_IN_FAST_THROW);
@@ -244,7 +257,6 @@ public class ModelContextImpl implements ModelContext {
this.useV8GeoPositions = flagValue(source, appId, version, Flags.USE_V8_GEO_POSITIONS);
this.maxCompactBuffers = flagValue(source, appId, version, Flags.MAX_COMPACT_BUFFERS);
this.ignoredHttpUserAgents = flagValue(source, appId, version, PermanentFlags.IGNORED_HTTP_USER_AGENTS);
- this.enableServerOcspStapling = flagValue(source, appId, version, Flags.ENABLE_SERVER_OCSP_STAPLING);
this.mergeThrottlingPolicy = flagValue(source, appId, version, Flags.MERGE_THROTTLING_POLICY);
this.persistenceThrottlingWsDecrementFactor = flagValue(source, appId, version, Flags.PERSISTENCE_THROTTLING_WS_DECREMENT_FACTOR);
this.persistenceThrottlingWsBackoff = flagValue(source, appId, version, Flags.PERSISTENCE_THROTTLING_WS_BACKOFF);
@@ -258,6 +270,12 @@ public class ModelContextImpl implements ModelContext {
this.enableProxyProtocolMixedMode = flagValue(source, appId, version, Flags.ENABLE_PROXY_PROTOCOL_MIXED_MODE);
this.sharedStringRepoNoReclaim = flagValue(source, appId, version, Flags.SHARED_STRING_REPO_NO_RECLAIM);
this.logFileCompressionAlgorithm = flagValue(source, appId, version, Flags.LOG_FILE_COMPRESSION_ALGORITHM);
+ this.mbus_java_num_targets = flagValue(source, appId, version, Flags.MBUS_JAVA_NUM_TARGETS);
+ this.mbus_java_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_JAVA_EVENTS_BEFORE_WAKEUP);
+ this.mbus_cpp_num_targets = flagValue(source, appId, version, Flags.MBUS_CPP_NUM_TARGETS);
+ this.mbus_cpp_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_CPP_EVENTS_BEFORE_WAKEUP);
+ this.rpc_num_targets = flagValue(source, appId, version, Flags.RPC_NUM_TARGETS);
+ this.rpc_events_before_wakeup = flagValue(source, appId, version, Flags.RPC_EVENTS_BEFORE_WAKEUP);
}
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@@ -271,6 +289,10 @@ public class ModelContextImpl implements ModelContext {
@Override public boolean useAsyncMessageHandlingOnSchedule() { return useAsyncMessageHandlingOnSchedule; }
@Override public double feedConcurrency() { return feedConcurrency; }
@Override public double feedNiceness() { return feedNiceness; }
+ @Override public boolean mbusDispatchOnDecode() { return mbus_dispatch_on_decode; }
+ @Override public boolean mbusDispatchOnEncode() { return mbus_dispatch_on_encode; }
+ @Override public int mbusNetworkThreads() { return mbus_network_threads; }
+ @Override public int mbusThreads() { return mbus_threads; }
@Override public List<String> allowedAthenzProxyIdentities() { return allowedAthenzProxyIdentities; }
@Override public int maxActivationInhibitedOutOfSyncGroups() { return maxActivationInhibitedOutOfSyncGroups; }
@Override public String jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type type) {
@@ -293,7 +315,6 @@ public class ModelContextImpl implements ModelContext {
@Override public boolean useV8GeoPositions() { return useV8GeoPositions; }
@Override public int maxCompactBuffers() { return maxCompactBuffers; }
@Override public List<String> ignoredHttpUserAgents() { return ignoredHttpUserAgents; }
- @Override public boolean enableServerOcspStapling() { return enableServerOcspStapling; }
@Override public String mergeThrottlingPolicy() { return mergeThrottlingPolicy; }
@Override public double persistenceThrottlingWsDecrementFactor() { return persistenceThrottlingWsDecrementFactor; }
@Override public double persistenceThrottlingWsBackoff() { return persistenceThrottlingWsBackoff; }
@@ -306,6 +327,12 @@ public class ModelContextImpl implements ModelContext {
@Override public Architecture adminClusterArchitecture() { return adminClusterArchitecture; }
@Override public boolean enableProxyProtocolMixedMode() { return enableProxyProtocolMixedMode; }
@Override public boolean sharedStringRepoNoReclaim() { return sharedStringRepoNoReclaim; }
+ @Override public int mbusJavaRpcNumTargets() { return mbus_java_num_targets; }
+ @Override public int mbusJavaEventsBeforeWakeup() { return mbus_java_events_before_wakeup; }
+ @Override public int mbusCppRpcNumTargets() { return mbus_cpp_num_targets; }
+ @Override public int mbusCppEventsBeforeWakeup() { return mbus_cpp_events_before_wakeup; }
+ @Override public int rpcNumTargets() { return rpc_num_targets; }
+ @Override public int rpcEventsBeforeWakeup() { return rpc_events_before_wakeup; }
@Override public String logFileCompressionAlgorithm(String defVal) {
var fflag = this.logFileCompressionAlgorithm;
if (fflag != null && ! fflag.equals("")) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
index 46ae2fd15d5..7f120a88a05 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
@@ -60,16 +60,13 @@ public class FileDirectory {
public File getFile(FileReference reference) {
ensureRootExist();
File dir = new File(getPath(reference));
- if (!dir.exists()) {
+ if (!dir.exists())
throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + "' does not exist.");
- }
- if (!dir.isDirectory()) {
+ if (!dir.isDirectory())
throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + "' is not a directory.");
- }
File [] files = dir.listFiles(new Filter());
- if (files == null || files.length == 0) {
+ if (files == null || files.length == 0)
throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + " does not contain any files");
- }
return files[0];
}
@@ -82,7 +79,7 @@ public class FileDirectory {
if (file.isDirectory()) {
return Files.walk(file.toPath(), 100).map(path -> {
try {
- log.log(Level.FINE, () -> "Calculating hash for '" + path + "'");
+ log.log(Level.FINEST, () -> "Calculating hash for '" + path + "'");
return hash(path.toFile(), hasher);
} catch (IOException e) {
log.log(Level.WARNING, "Failed getting hash from '" + path + "'");
@@ -144,7 +141,7 @@ public class FileDirectory {
File destination = new File(tempDestinationDir.toFile(), source.getName());
if (!destinationDir.exists()) {
destinationDir.mkdir();
- log.log(Level.FINE, () -> "file reference ' " + reference.value() + "', source: " + source.getAbsolutePath() );
+ log.log(Level.FINE, () -> "file reference '" + reference.value() + "', source: " + source.getAbsolutePath() );
if (source.isDirectory()) {
log.log(Level.FINE, () -> "Copying source " + source.getAbsolutePath() + " to " + destination.getAbsolutePath());
IOUtils.copyDirectory(source, destination, -1);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionImpl.java
index abb8a3e8487..7d7d4aa1d7d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionImpl.java
@@ -10,6 +10,7 @@ import com.yahoo.jrt.StringArray;
import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Target;
+import java.time.Duration;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -20,7 +21,7 @@ import java.util.logging.Logger;
public class FileDistributionImpl implements FileDistribution, RequestWaiter {
private final static Logger log = Logger.getLogger(FileDistributionImpl.class.getName());
- private final static double rpcTimeout = 1.0;
+ private final static Duration rpcTimeout = Duration.ofSeconds(1);
private final Supervisor supervisor;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
index 88405b3eef9..770352e6bfc 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
@@ -13,14 +13,16 @@ import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Transport;
import com.yahoo.vespa.config.ConnectionPool;
import com.yahoo.vespa.defaults.Defaults;
-import com.yahoo.vespa.filedistribution.FileReferenceCompressor;
import com.yahoo.vespa.filedistribution.EmptyFileReferenceData;
import com.yahoo.vespa.filedistribution.FileDistributionConnectionPool;
import com.yahoo.vespa.filedistribution.FileDownloader;
+import com.yahoo.vespa.filedistribution.FileReferenceCompressor;
import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
import com.yahoo.vespa.filedistribution.LazyFileReferenceData;
import com.yahoo.vespa.filedistribution.LazyTemporaryStorageFileReferenceData;
+import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.yolean.Exceptions;
import java.io.File;
import java.io.IOException;
@@ -28,15 +30,21 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getOtherConfigServersInCluster;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
import static com.yahoo.vespa.filedistribution.FileReferenceData.Type.compressed;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type;
public class FileServer {
@@ -48,10 +56,13 @@ public class FileServer {
private final FileDirectory root;
private final ExecutorService executor;
private final FileDownloader downloader;
+ private final List<CompressionType> compressionTypes; // compression types to use, in preferred order
+ // TODO: Move to filedistribution module, so that it can be used by both clients and servers
private enum FileApiErrorCodes {
OK(0, "OK"),
- NOT_FOUND(1, "Filereference not found");
+ NOT_FOUND(1, "File reference not found"),
+ TIMEOUT(2, "Timeout");
private final int code;
private final String description;
FileApiErrorCodes(int code, String description) {
@@ -80,21 +91,24 @@ public class FileServer {
@SuppressWarnings("WeakerAccess") // Created by dependency injection
@Inject
- public FileServer(ConfigserverConfig configserverConfig) {
+ public FileServer(ConfigserverConfig configserverConfig, FlagSource flagSource) {
this(new File(Defaults.getDefaults().underVespaHome(configserverConfig.fileReferencesDir())),
- createFileDownloader(getOtherConfigServersInCluster(configserverConfig)));
+ createFileDownloader(getOtherConfigServersInCluster(configserverConfig),
+ compressionTypes(Flags.FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES.bindTo(flagSource).value())),
+ compressionTypesAsList(Flags.FILE_DISTRIBUTION_COMPRESSION_TYPES_TO_SERVE.bindTo(flagSource).value()));
}
// For testing only
public FileServer(File rootDir) {
- this(rootDir, createFileDownloader(List.of()));
+ this(rootDir, createFileDownloader(List.of(), Set.of(gzip)), List.of(gzip));
}
- public FileServer(File rootDir, FileDownloader fileDownloader) {
+ FileServer(File rootDir, FileDownloader fileDownloader, List<CompressionType> compressionTypes) {
this.downloader = fileDownloader;
this.root = new FileDirectory(rootDir);
this.executor = Executors.newFixedThreadPool(Math.max(8, Runtime.getRuntime().availableProcessors()),
new DaemonThreadFactory("file-server-"));
+ this.compressionTypes = compressionTypes;
}
boolean hasFile(String fileReference) {
@@ -112,21 +126,14 @@ public class FileServer {
FileDirectory getRootDir() { return root; }
- void startFileServing(String fileName, Receiver target) {
- FileReference reference = new FileReference(fileName);
- File file = root.getFile(reference);
+ void startFileServing(FileReference reference, Receiver target, Set<CompressionType> acceptedCompressionTypes) {
+ if ( ! root.getFile(reference).exists()) return;
- if (file.exists()) {
- serveFile(reference, target);
- }
- }
-
- private void serveFile(FileReference reference, Receiver target) {
File file = root.getFile(reference);
log.log(Level.FINE, () -> "Start serving " + reference + " with file '" + file.getAbsolutePath() + "'");
FileReferenceData fileData = EmptyFileReferenceData.empty(reference, file.getName());
try {
- fileData = readFileReferenceData(reference);
+ fileData = readFileReferenceData(reference, acceptedCompressionTypes);
target.receive(fileData, new ReplayStatus(0, "OK"));
log.log(Level.FINE, () -> "Done serving " + reference.value() + " with file '" + file.getAbsolutePath() + "'");
} catch (IOException e) {
@@ -140,56 +147,71 @@ public class FileServer {
}
}
- private FileReferenceData readFileReferenceData(FileReference reference) throws IOException {
+ private FileReferenceData readFileReferenceData(FileReference reference, Set<CompressionType> acceptedCompressionTypes) throws IOException {
File file = root.getFile(reference);
if (file.isDirectory()) {
Path tempFile = Files.createTempFile("filereferencedata", reference.value());
- File compressedFile = new FileReferenceCompressor(compressed).compress(file.getParentFile(), tempFile.toFile());
- return new LazyTemporaryStorageFileReferenceData(reference, file.getName(), compressed, compressedFile);
+ CompressionType compressionType = chooseCompressionType(acceptedCompressionTypes);
+ log.log(Level.FINE, () -> "accepted compression types=" + acceptedCompressionTypes + ", compression type to use=" + compressionType);
+ File compressedFile = new FileReferenceCompressor(compressed, compressionType).compress(file.getParentFile(), tempFile.toFile());
+ return new LazyTemporaryStorageFileReferenceData(reference, file.getName(), compressed, compressedFile, compressionType);
} else {
- return new LazyFileReferenceData(reference, file.getName(), FileReferenceData.Type.file, file);
+ return new LazyFileReferenceData(reference, file.getName(), Type.file, file, gzip);
}
}
- public void serveFile(String fileReference, boolean downloadFromOtherSourceIfNotFound, Request request, Receiver receiver) {
+ public void serveFile(FileReference fileReference,
+ boolean downloadFromOtherSourceIfNotFound,
+ Set<CompressionType> acceptedCompressionTypes,
+ Request request, Receiver receiver) {
if (executor instanceof ThreadPoolExecutor)
log.log(Level.FINE, () -> "Active threads: " + ((ThreadPoolExecutor) executor).getActiveCount());
log.log(Level.FINE, () -> "Received request for file reference '" + fileReference + "' from " + request.target());
Instant deadline = Instant.now().plus(timeout);
- executor.execute(() -> serveFileInternal(fileReference, downloadFromOtherSourceIfNotFound, request, receiver, deadline));
+ String client = request.target().toString();
+ executor.execute(() -> {
+ var result = serveFileInternal(fileReference, downloadFromOtherSourceIfNotFound, client, receiver, deadline, acceptedCompressionTypes);
+ request.returnValues()
+ .add(new Int32Value(result.getCode()))
+ .add(new StringValue(result.getDescription()));
+ request.returnRequest();
+ });
}
- private void serveFileInternal(String fileReference,
- boolean downloadFromOtherSourceIfNotFound,
- Request request,
- Receiver receiver,
- Instant deadline) {
+ private FileApiErrorCodes serveFileInternal(FileReference fileReference,
+ boolean downloadFromOtherSourceIfNotFound,
+ String client,
+ Receiver receiver,
+ Instant deadline,
+ Set<CompressionType> acceptedCompressionTypes) {
if (Instant.now().isAfter(deadline)) {
- log.log(Level.INFO, () -> "Deadline exceeded for request for file reference '" + fileReference + "' from " + request.target() +
- " , giving up");
- return;
+ log.log(Level.INFO, () -> "Deadline exceeded for request for file reference '" + fileReference + "' from " + client);
+ return FileApiErrorCodes.TIMEOUT;
}
boolean fileExists;
try {
- String client = request.target().toString();
- FileReferenceDownload fileReferenceDownload = new FileReferenceDownload(new FileReference(fileReference),
- client,
- downloadFromOtherSourceIfNotFound);
+ var fileReferenceDownload = new FileReferenceDownload(fileReference, client, downloadFromOtherSourceIfNotFound);
fileExists = hasFileDownloadIfNeeded(fileReferenceDownload);
- if (fileExists) startFileServing(fileReference, receiver);
+ if (fileExists) startFileServing(fileReference, receiver, acceptedCompressionTypes);
} catch (IllegalArgumentException e) {
fileExists = false;
- log.warning("Failed serving file reference '" + fileReference + "', request was from " + request.target() + ", with error " + e.toString());
+ log.warning("Failed serving file reference '" + fileReference + "', request from " + client + " failed with: " + e.getMessage());
}
- FileApiErrorCodes result = fileExists ? FileApiErrorCodes.OK : FileApiErrorCodes.NOT_FOUND;
- request.returnValues()
- .add(new Int32Value(result.getCode()))
- .add(new StringValue(result.getDescription()));
- request.returnRequest();
+ return (fileExists ? FileApiErrorCodes.OK : FileApiErrorCodes.NOT_FOUND);
+ }
+
+ /* Choose the first compression type (list is in preferred order) that matches an accepted compression type, or fail */
+ private CompressionType chooseCompressionType(Set<CompressionType> acceptedCompressionTypes) {
+ for (CompressionType compressionType : compressionTypes) {
+ if (acceptedCompressionTypes.contains(compressionType))
+ return compressionType;
+ }
+ throw new RuntimeException("Could not find a compression type that can be used. Accepted compression types: " +
+ acceptedCompressionTypes + ", compression types server can use: " + compressionTypes);
}
boolean hasFileDownloadIfNeeded(FileReferenceDownload fileReferenceDownload) {
@@ -199,17 +221,16 @@ public class FileServer {
if (fileReferenceDownload.downloadFromOtherSourceIfNotFound()) {
log.log(Level.FINE, "File not found, downloading from another source");
// Create new FileReferenceDownload with downloadFromOtherSourceIfNotFound set to false
- // to avoid config servers requesting a file reference perpetually, e.g. for a file that
- // does not exist anymore
+ // to avoid requesting a file reference perpetually, e.g. for a file that does not exist anymore
FileReferenceDownload newDownload = new FileReferenceDownload(fileReference,
fileReferenceDownload.client(),
false);
boolean fileExists = downloader.getFile(newDownload).isPresent();
if ( ! fileExists)
- log.log(Level.WARNING, "Failed downloading '" + fileReferenceDownload + "'");
+ log.log(Level.INFO, "Failed downloading '" + fileReferenceDownload + "'");
return fileExists;
} else {
- log.log(Level.FINE, "File not found, will not download from another source, since request came from another config server");
+ log.log(Level.FINE, "File not found, will not download from another source");
return false;
}
}
@@ -221,14 +242,27 @@ public class FileServer {
executor.shutdown();
}
- private static FileDownloader createFileDownloader(List<String> configServers) {
+ private static FileDownloader createFileDownloader(List<String> configServers, Set<CompressionType> acceptedCompressionTypes) {
Supervisor supervisor = new Supervisor(new Transport("filedistribution-pool")).setDropEmptyBuffers(true);
return new FileDownloader(configServers.isEmpty()
? FileDownloader.emptyConnectionPool()
: createConnectionPool(configServers, supervisor),
supervisor,
- timeout);
+ timeout,
+ acceptedCompressionTypes);
+ }
+
+ private static LinkedHashSet<CompressionType> compressionTypes(List<String> compressionTypes) {
+ return compressionTypes.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+ private static List<CompressionType> compressionTypesAsList(List<String> compressionTypes) {
+ return compressionTypes.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toList());
}
private static ConnectionPool createConnectionPool(List<String> configServers, Supervisor supervisor) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
index f7042b49c3f..0d4baa7dc56 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
@@ -38,4 +38,9 @@ class ProxyResponse extends HttpResponse {
}
}
+ @Override
+ public long maxPendingBytes() {
+ return 1 << 25; // 32MB
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
index ae4b205c06e..12972e5c465 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
@@ -18,20 +18,23 @@ import com.yahoo.vespa.filedistribution.FileDistributionConnectionPool;
import com.yahoo.vespa.filedistribution.FileDownloader;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
import com.yahoo.vespa.flags.FlagSource;
-
+import com.yahoo.vespa.flags.Flags;
import java.io.File;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.fileReferenceExistsOnDisk;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getOtherConfigServersInCluster;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
/**
* Verifies that all active sessions has an application package on local disk.
* If not, the package is downloaded with file distribution. This can happen e.g.
- * if a configserver is down when the application is deployed.
+ * if a config server is down when the application is deployed.
*
* @author gjoranv
*/
@@ -53,7 +56,10 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
this.applicationRepository = applicationRepository;
this.configserverConfig = applicationRepository.configserverConfig();
this.downloadDirectory = new File(Defaults.getDefaults().underVespaHome(configserverConfig.fileReferencesDir()));
- this.fileDownloader = createFileDownloader(configserverConfig, downloadDirectory, supervisor);
+ this.fileDownloader = createFileDownloader(configserverConfig,
+ downloadDirectory,
+ supervisor,
+ Flags.FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES.bindTo(flagSource).value());
}
@Override
@@ -94,14 +100,18 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
private static FileDownloader createFileDownloader(ConfigserverConfig configserverConfig,
File downloadDirectory,
- Supervisor supervisor) {
+ Supervisor supervisor,
+ List<String> flagValues) {
List<String> otherConfigServersInCluster = getOtherConfigServersInCluster(configserverConfig);
ConfigSourceSet configSourceSet = new ConfigSourceSet(otherConfigServersInCluster);
ConnectionPool connectionPool = (otherConfigServersInCluster.isEmpty())
? FileDownloader.emptyConnectionPool()
: new FileDistributionConnectionPool(configSourceSet, supervisor);
- return new FileDownloader(connectionPool, supervisor, downloadDirectory, Duration.ofSeconds(300));
+ Set<CompressionType> acceptedCompressionTypes = flagValues.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toSet());
+ return new FileDownloader(connectionPool, supervisor, downloadDirectory, Duration.ofSeconds(300), acceptedCompressionTypes);
}
@Override
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
index d980fb079e7..38ac41d0eb9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
@@ -16,22 +16,17 @@ import java.util.logging.Level;
* @author hmusum
*/
public class SessionsMaintainer extends ConfigServerMaintainer {
- private final boolean hostedVespa;
SessionsMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval, FlagSource flagSource) {
super(applicationRepository, curator, flagSource, applicationRepository.clock().instant(), interval, true);
- this.hostedVespa = applicationRepository.configserverConfig().hostedVespa();
}
@Override
protected double maintain() {
applicationRepository.deleteExpiredLocalSessions();
- if (hostedVespa) {
- Duration expiryTime = Duration.ofMinutes(90);
- int deleted = applicationRepository.deleteExpiredRemoteSessions(expiryTime);
- log.log(Level.FINE, () -> "Deleted " + deleted + " expired remote sessions older than " + expiryTime);
- }
+ int deleted = applicationRepository.deleteExpiredRemoteSessions(applicationRepository.clock());
+ log.log(Level.FINE, () -> "Deleted " + deleted + " expired remote sessions");
return 1.0;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
index cce6429f84a..8a541abf4ae 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
@@ -130,7 +130,7 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
}
catch (RuntimeException e) {
if (shouldSkipCreatingMajorVersionOnError(majorVersions, majorVersion, wantedNodeVespaVersion, allocatedHosts)) {
- log.log(Level.INFO, applicationId + ": Skipping major version " + majorVersion, e);
+ log.log(Level.FINE, applicationId + ": Skipping major version " + majorVersion, e);
}
else {
if (e instanceof IllegalArgumentException) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponses.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponses.java
index 7e6fccb6d2f..0b54a09d963 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponses.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/DelayedConfigResponses.java
@@ -2,16 +2,14 @@
package com.yahoo.vespa.config.server.rpc;
import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.jrt.Target;
import com.yahoo.jrt.TargetWatcher;
-import java.util.logging.Level;
import com.yahoo.vespa.config.protocol.JRTServerConfigRequest;
import com.yahoo.vespa.config.server.GetConfigContext;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.monitoring.Metrics;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -22,6 +20,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -40,7 +39,7 @@ public class DelayedConfigResponses {
private final Map<ApplicationId, MetricUpdater> metrics = new ConcurrentHashMap<>();
- /* Requests that resolve to config that has not changed are put on this queue. When reloading
+ /* Requests that resolve to config that has not changed are put on this queue. When activating
config, all requests on this queue are reprocessed as if they were a new request */
private final Map<ApplicationId, BlockingQueue<DelayedConfigResponse>> delayedResponses =
new ConcurrentHashMap<>();
@@ -183,7 +182,7 @@ public class DelayedConfigResponses {
response.getRequest().getShortDescription());
}
// Config will be resolved in the run() method of DelayedConfigResponse,
- // when the timer expires or config is updated/reloaded.
+ // when the timer expires or config is updated/activated.
response.schedule(Math.max(0, request.getTimeout()));
metricDelayedResponses(context.applicationId(), delayedResponsesQueue.size());
} catch (InterruptedException e) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
index c31015b533a..b7327ef3aa7 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
@@ -6,13 +6,11 @@ import com.yahoo.collections.Pair;
import com.yahoo.component.Version;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.di.config.ApplicationBundlesConfig;
-import com.yahoo.net.HostName;
-import com.yahoo.vespa.config.PayloadChecksum;
-import com.yahoo.vespa.config.PayloadChecksum.Type;
-import com.yahoo.vespa.config.PayloadChecksums;
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;
@@ -22,11 +20,13 @@ 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;
/**
@@ -56,7 +56,7 @@ class GetConfigProcessor implements Runnable {
private void respond(JRTServerConfigRequest request) {
Request req = request.getRequest();
if (req.isError()) {
- Level logLevel = (req.errorCode() == ErrorCode.APPLICATION_NOT_LOADED) ? Level.FINE : Level.INFO;
+ 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);
@@ -83,9 +83,7 @@ class GetConfigProcessor implements Runnable {
return null;
}
Trace trace = request.getRequestTrace();
- if (logDebug(trace)) {
- debugLog(trace, "GetConfigProcessor.run() on " + localHostName);
- }
+ debugLog(trace, "GetConfigProcessor.run() on " + localHostName);
Optional<TenantName> tenant = rpcServer.resolveTenant(request, trace);
@@ -98,23 +96,21 @@ class GetConfigProcessor implements Runnable {
GetConfigContext context = rpcServer.createGetConfigContext(tenant, request, trace);
if (context == null || ! context.requestHandler().hasApplication(context.applicationId(), Optional.empty())) {
- handleError(request, ErrorCode.APPLICATION_NOT_LOADED, "No application exists");
+ handleError(request, APPLICATION_NOT_LOADED, "No application exists");
return null;
}
+ logPre = TenantRepository.logPre(context.applicationId());
Optional<Version> vespaVersion = rpcServer.useRequestVersion() ?
request.getVespaVersion().map(VespaVersion::toString).map(Version::fromString) :
Optional.empty();
- if (logDebug(trace)) {
- debugLog(trace, "Using version " + printableVespaVersion(vespaVersion));
- }
+ 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 null;
}
- this.logPre = TenantRepository.logPre(context.applicationId());
ConfigResponse config;
try {
config = rpcServer.resolveConfig(request, context, vespaVersion);
@@ -143,14 +139,10 @@ class GetConfigProcessor implements Runnable {
// debugLog(trace, "config response before encoding:" + config.toString());
request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.applyOnRestart(), config.getPayloadChecksums());
- if (logDebug(trace)) {
- debugLog(trace, "return response: " + request.getShortDescription());
- }
+ debugLog(trace, "return response: " + request.getShortDescription());
respond(request);
} else {
- if (logDebug(trace)) {
- debugLog(trace, "delaying response " + request.getShortDescription());
- }
+ debugLog(trace, "delaying response " + request.getShortDescription());
return new Pair<>(context, config != null ? config.getGeneration() : 0);
}
return null;
@@ -164,9 +156,9 @@ class GetConfigProcessor implements Runnable {
if (delayed != null) {
rpcServer.delayResponse(request, delayed.getFirst());
if (rpcServer.hasNewerGeneration(delayed.getFirst().applicationId(), delayed.getSecond())) {
- // This will ensure that if the reload train left the station while I was boarding, another train will
- // immediately be scheduled.
- rpcServer.configReloaded(delayed.getFirst().applicationId());
+ // This will ensure that if the config activation train left the station while I was boarding,
+ // another train will immediately be scheduled.
+ rpcServer.configActivated(delayed.getFirst().applicationId());
}
}
}
@@ -177,7 +169,7 @@ class GetConfigProcessor implements Runnable {
}
private static String printableVespaVersion(Optional<Version> vespaVersion) {
- return (vespaVersion.isPresent() ? vespaVersion.get().toFullString() : "LATEST");
+ return vespaVersion.map(Version::toFullString).orElse("LATEST");
}
private void returnEmpty(JRTServerConfigRequest request) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
index 6a8141032df..b36967d76a4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
@@ -1,9 +1,9 @@
// Copyright Yahoo. 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.component.annotation.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
+import com.yahoo.component.annotation.Inject;
import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.config.FileReference;
import com.yahoo.config.provision.ApplicationId;
@@ -28,7 +28,7 @@ import com.yahoo.vespa.config.protocol.JRTServerConfigRequest;
import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3;
import com.yahoo.vespa.config.protocol.Trace;
import com.yahoo.vespa.config.server.GetConfigContext;
-import com.yahoo.vespa.config.server.ReloadListener;
+import com.yahoo.vespa.config.server.ConfigActivationListener;
import com.yahoo.vespa.config.server.RequestHandler;
import com.yahoo.vespa.config.server.SuperModelRequestHandler;
import com.yahoo.vespa.config.server.application.ApplicationSet;
@@ -44,13 +44,15 @@ import com.yahoo.vespa.filedistribution.FileDownloader;
import com.yahoo.vespa.filedistribution.FileReceiver;
import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
-
import java.nio.ByteBuffer;
+import java.time.Duration;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ConcurrentHashMap;
@@ -62,8 +64,11 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
+
/**
* An RPC server class that handles the config protocol RPC method "getConfigV3".
* Mandatory hooks need to be implemented by subclasses.
@@ -71,7 +76,7 @@ import java.util.stream.Stream;
* @author hmusum
*/
// TODO: Split business logic out of this
-public class RpcServer implements Runnable, ReloadListener, TenantListener {
+public class RpcServer implements Runnable, ConfigActivationListener, TenantListener {
static final String getConfigMethodName = "getConfigV3";
@@ -221,7 +226,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
getSupervisor().addMethod(new Method("printStatistics", "", "s", this::printStatistics)
.methodDesc("printStatistics")
.returnDesc(0, "statistics", "Statistics for server"));
- getSupervisor().addMethod(new Method("filedistribution.serveFile", "si", "is", this::serveFile));
+ getSupervisor().addMethod(new Method("filedistribution.serveFile", "si*", "is", this::serveFile));
getSupervisor().addMethod(new Method("filedistribution.setFileReferencesToDownload", "S", "i", this::setFileReferencesToDownload)
.methodDesc("set which file references to download")
.paramDesc(0, "file references", "file reference to download")
@@ -252,7 +257,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
/**
* Checks all delayed responses for config changes and waits until all has been answered.
- * This method should be called when config is reloaded in the server.
+ * This method should be called when config is activated in the server.
*/
@Override
public void configActivated(ApplicationSet applicationSet) {
@@ -260,19 +265,19 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
ApplicationState state = getState(applicationId);
state.setActiveGeneration(applicationSet.getApplicationGeneration());
reloadSuperModel(applicationSet);
- configReloaded(applicationId);
+ configActivated(applicationId);
}
private void reloadSuperModel(ApplicationSet applicationSet) {
- superModelRequestHandler.reloadConfig(applicationSet);
- configReloaded(ApplicationId.global());
+ superModelRequestHandler.activateConfig(applicationSet);
+ configActivated(ApplicationId.global());
}
- void configReloaded(ApplicationId applicationId) {
+ void configActivated(ApplicationId applicationId) {
List<DelayedConfigResponses.DelayedConfigResponse> responses = delayedConfigResponses.drainQueue(applicationId);
String logPre = TenantRepository.logPre(applicationId);
if (log.isLoggable(Level.FINE)) {
- log.log(Level.FINE, logPre + "Start of configReload: " + responses.size() + " requests on delayed requests queue");
+ log.log(Level.FINE, logPre + "Start of configActivated: " + responses.size() + " requests on delayed requests queue");
}
int responsesSent = 0;
CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executorService);
@@ -303,7 +308,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
}
if (log.isLoggable(Level.FINE))
- log.log(Level.FINE, logPre + "Finished reloading " + responsesSent + " requests");
+ log.log(Level.FINE, logPre + "Finished activating " + responsesSent + " requests");
}
private void logRequestDebug(Level level, String message, JRTServerConfigRequest request) {
@@ -326,8 +331,8 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
@Override
public void applicationRemoved(ApplicationId applicationId) {
superModelRequestHandler.removeApplication(applicationId);
- configReloaded(applicationId);
- configReloaded(ApplicationId.global());
+ configActivated(applicationId);
+ configActivated(ApplicationId.global());
}
public void respond(JRTServerConfigRequest request) {
@@ -487,6 +492,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
sendParts(session, fileData);
sendEof(session, fileData, status);
}
+
private void sendParts(int session, FileReferenceData fileData) {
ByteBuffer bb = ByteBuffer.allocate(0x100000);
for (int partId = 0, read = fileData.nextContent(bb); read >= 0; partId++, read = fileData.nextContent(bb)) {
@@ -500,12 +506,9 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
bb.clear();
}
}
+
private int sendMeta(FileReferenceData fileData) {
- Request request = new Request(FileReceiver.RECEIVE_META_METHOD);
- request.parameters().add(new StringValue(fileData.fileReference().value()));
- request.parameters().add(new StringValue(fileData.filename()));
- request.parameters().add(new StringValue(fileData.type().name()));
- request.parameters().add(new Int64Value(fileData.size()));
+ Request request = createMetaRequest(fileData);
invokeRpcIfValidConnection(request);
if (request.isError()) {
log.warning("Failed delivering meta for reference '" + fileData.fileReference().value() + "' with file '" + fileData.filename() + "' to " +
@@ -518,6 +521,20 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
return request.returnValues().get(1).asInt32();
}
}
+
+ // non-private for testing
+ static Request createMetaRequest(FileReferenceData fileData) {
+ Request request = new Request(FileReceiver.RECEIVE_META_METHOD);
+ request.parameters().add(new StringValue(fileData.fileReference().value()));
+ request.parameters().add(new StringValue(fileData.filename()));
+ request.parameters().add(new StringValue(fileData.type().name()));
+ request.parameters().add(new Int64Value(fileData.size()));
+ // Only add paramter if not gzip, this is default and old clients will not handle the extra parameter
+ if (fileData.compressionType() != CompressionType.gzip)
+ request.parameters().add(new StringValue(fileData.compressionType().name()));
+ return request;
+ }
+
private void sendPart(int session, FileReference ref, int partId, byte [] buf) {
Request request = new Request(FileReceiver.RECEIVE_PART_METHOD);
request.parameters().add(new StringValue(ref.value()));
@@ -534,6 +551,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
}
}
}
+
private void sendEof(int session, FileReferenceData fileData, FileServer.ReplayStatus status) {
Request request = new Request(FileReceiver.RECEIVE_EOF_METHOD);
request.parameters().add(new StringValue(fileData.fileReference().value()));
@@ -554,7 +572,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
private void invokeRpcIfValidConnection(Request request) {
if (target.isValid()) {
- target.invokeSync(request, 600);
+ target.invokeSync(request, Duration.ofMinutes(10));
} else {
throw new RuntimeException("Connection to " + target + " is invalid", target.getConnectionLostReason());
}
@@ -566,7 +584,18 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
rpcAuthorizer.authorizeFileRequest(request)
.thenRun(() -> { // okay to do in authorizer thread as serveFile is async
FileServer.Receiver receiver = new ChunkedFileReceiver(request.target());
- fileServer.serveFile(request.parameters().get(0).asString(), request.parameters().get(1).asInt32() == 0, request, receiver);
+
+ FileReference reference = new FileReference(request.parameters().get(0).asString());
+ boolean downloadFromOtherSourceIfNotFound = request.parameters().get(1).asInt32() == 0;
+ Set<FileReferenceData.CompressionType> acceptedCompressionTypes = Set.of(CompressionType.gzip);
+ // Newer clients specify accepted compression types in request
+ if (request.parameters().size() > 2)
+ acceptedCompressionTypes = Arrays.stream(request.parameters().get(2).asStringArray())
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toSet());
+ log.log(Level.FINE, "acceptedCompressionTypes=" + acceptedCompressionTypes);
+
+ fileServer.serveFile(reference, downloadFromOtherSourceIfNotFound, acceptedCompressionTypes, request, receiver);
});
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
index f5b570fed40..536a446df2f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
@@ -10,9 +10,9 @@ import com.yahoo.config.provision.security.NodeIdentifier;
import com.yahoo.config.provision.security.NodeIdentifierException;
import com.yahoo.config.provision.security.NodeIdentity;
import com.yahoo.jrt.Request;
-import com.yahoo.jrt.SecurityContext;
import com.yahoo.security.tls.MixedMode;
import com.yahoo.security.tls.TransportSecurityUtils;
+import com.yahoo.security.tls.ConnectionAuthContext;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3;
import com.yahoo.vespa.config.server.RequestHandler;
@@ -166,14 +166,14 @@ public class MultiTenantRpcAuthorizer implements RpcAuthorizer {
// TODO Make peer identity mandatory once TLS mixed mode is removed
private Optional<NodeIdentity> getPeerIdentity(Request request) {
- Optional<SecurityContext> securityContext = request.target().getSecurityContext();
- if (securityContext.isEmpty()) {
+ ConnectionAuthContext authCtx = request.target().connectionAuthContext();
+ if (authCtx.peerCertificate().isEmpty()) {
if (TransportSecurityUtils.getInsecureMixedMode() == MixedMode.DISABLED) {
throw new IllegalStateException("Security context missing"); // security context should always be present
}
return Optional.empty(); // client choose to communicate over insecure channel
}
- List<X509Certificate> certChain = securityContext.get().peerCertificateChain();
+ List<X509Certificate> certChain = authCtx.peerCertificateChain();
if (certChain.isEmpty()) {
throw new IllegalStateException("Client authentication is not enforced!"); // clients should be required to authenticate when TLS is enabled
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
index edc166d0989..82faeae01e8 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
@@ -61,10 +61,6 @@ public abstract class Session implements Comparable<Session> {
return sessionZooKeeperClient.readStatus();
}
- public SessionZooKeeperClient getSessionZooKeeperClient() {
- return sessionZooKeeperClient;
- }
-
@Override
public String toString() {
return "Session,id=" + sessionId + ",status=" + getStatus();
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index 1e073ac3458..63ba8197960 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -6,6 +6,9 @@ import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
import com.yahoo.concurrent.UncheckedTimeoutException;
import com.yahoo.config.FileReference;
+import com.yahoo.config.application.ValidationProcessor;
+import com.yahoo.config.application.XmlPreProcessor;
+import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.application.api.FileRegistry;
@@ -26,6 +29,7 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.net.HostName;
import com.yahoo.path.Path;
+import com.yahoo.text.XML;
import com.yahoo.vespa.config.server.ConfigServerSpec;
import com.yahoo.vespa.config.server.TimeoutBudget;
import com.yahoo.vespa.config.server.application.ApplicationSet;
@@ -47,9 +51,14 @@ import com.yahoo.vespa.config.server.tenant.EndpointCertificateRetriever;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.model.application.validation.BundleValidator;
+import org.xml.sax.SAXException;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Collection;
@@ -58,9 +67,11 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
+import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+import java.util.zip.ZipException;
import static com.yahoo.vespa.config.server.ConfigServerSpec.fromConfig;
@@ -238,6 +249,7 @@ public class SessionPreparer {
void preprocess() {
try {
+ validateXmlFeatures(applicationPackage, logger);
this.preprocessedApplicationPackage = applicationPackage.preprocess(zone, logger);
} catch (IOException | RuntimeException e) {
throw new IllegalArgumentException("Error preprocessing application package for " + applicationId +
@@ -246,6 +258,74 @@ public class SessionPreparer {
checkTimeout("preprocess");
}
+ /**
+ * Warn on use of deprecated XML features
+ */
+ private void validateXmlFeatures(ApplicationPackage applicationPackage, DeployLogger logger) {
+ // TODO: Validate no use of XInclude, datatype definitions or external entities
+ // in any xml file we parse, such as services.xml, deployment.xml, hosts.xml,
+ // validation-overrides.xml and any pom.xml files in OSGi bundles
+ // services.xml and hosts.xml will need to be preprocessed by our own processors first
+
+ File applicationPackageDir = applicationPackage.getFileReference(Path.fromString("."));
+ File servicesXml = applicationPackage.getFileReference(Path.fromString("services.xml"));
+ File hostsXml = applicationPackage.getFileReference(Path.fromString("hosts.xml"));
+
+ // Validate after doing our own preprocessing on these two files
+ if(servicesXml.exists()) {
+ vespaPreprocess(applicationPackageDir.getAbsoluteFile(), servicesXml, applicationPackage.getMetaData());
+ }
+ if(hostsXml.exists()) {
+ vespaPreprocess(applicationPackageDir.getAbsoluteFile(), hostsXml, applicationPackage.getMetaData());
+ }
+
+ if (zone.system().isPublic()) {
+ // Validate all other XML files
+ try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE,
+ (path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Xx][Mm][Ll]"))) {
+ paths.filter(p -> !(p.equals(servicesXml.getAbsoluteFile().toPath()) || p.equals(hostsXml.getAbsoluteFile().toPath())))
+ .forEach(xmlPath -> {
+ try {
+ new ValidationProcessor().process(XML.getDocument(xmlPath.toFile()));
+ } catch (IOException | TransformerException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Validate pom.xml files in OSGi bundles
+ try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE,
+ (path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Jj][Aa][Rr]"))) {
+ paths.forEach(jarPath -> {
+ try {
+ new BundleValidator().getPomXmlContent(logger, new JarFile(jarPath.toFile()));
+ } catch (ZipException e) {
+ // ignore for tests
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void vespaPreprocess(File appDir, File inputXml, ApplicationMetaData metaData) {
+ try {
+ new XmlPreProcessor(appDir,
+ inputXml,
+ metaData.getApplicationId().instance(),
+ zone.environment(),
+ zone.region())
+ .run();
+ } catch (ParserConfigurationException | IOException | SAXException | TransformerException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
AllocatedHosts buildModels(Instant now) {
var allocatedHosts = new AllocatedHostsFromAllModels();
this.modelResultList = preparedModelsBuilder.buildModels(applicationId, dockerImageRepository, vespaVersion,
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index 059d192e7d2..a6bbd6c20a2 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -44,13 +44,12 @@ import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.StringFlag;
+import com.yahoo.vespa.flags.UnboundStringFlag;
import com.yahoo.yolean.Exceptions;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.zookeeper.KeeperException;
-
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
@@ -83,6 +82,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import static com.yahoo.vespa.curator.Curator.CompletionWaiter;
+import static com.yahoo.vespa.flags.FetchVector.Dimension.APPLICATION_ID;
import static java.nio.file.Files.readAttributes;
/**
@@ -128,7 +128,6 @@ public class SessionRepository {
private final ModelFactoryRegistry modelFactoryRegistry;
private final ConfigDefinitionRepo configDefinitionRepo;
private final int maxNodeSize;
- private final StringFlag failDeploymentForFilesWithUnknownExtension;
public SessionRepository(TenantName tenantName,
TenantApplications applicationRepo,
@@ -172,7 +171,6 @@ public class SessionRepository {
this.modelFactoryRegistry = modelFactoryRegistry;
this.configDefinitionRepo = configDefinitionRepo;
this.maxNodeSize = maxNodeSize;
- this.failDeploymentForFilesWithUnknownExtension = Flags.APPLICATION_FILES_WITH_UNKNOWN_EXTENSION.bindTo(flagSource);
loadSessions(); // Needs to be done before creating cache below
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, zkCacheExecutor);
@@ -347,39 +345,44 @@ public class SessionRepository {
public RemoteSession createRemoteSession(long sessionId) {
SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionId);
RemoteSession session = new RemoteSession(tenantName, sessionId, sessionZKClient);
- RemoteSession newSession = loadSessionIfActive(session).orElse(session);
- remoteSessionCache.put(sessionId, newSession);
- updateSessionStateWatcher(sessionId, newSession);
- return newSession;
+ loadSessionIfActive(session);
+ remoteSessionCache.put(sessionId, session);
+ updateSessionStateWatcher(sessionId);
+ return session;
}
- public int deleteExpiredRemoteSessions(Clock clock, Duration expiryTime) {
+ public int deleteExpiredRemoteSessions(Clock clock) {
+ Duration expiryTime = configserverConfig.hostedVespa()
+ ? sessionLifetime.multipliedBy(2)
+ : sessionLifetime.multipliedBy(24); // TODO: Remove when tested more (Sep. 2022 at the latest)
+
List<Long> remoteSessionsFromZooKeeper = getRemoteSessionsFromZooKeeper();
log.log(Level.FINE, () -> "Remote sessions for tenant " + tenantName + ": " + remoteSessionsFromZooKeeper);
int deleted = 0;
+ // Avoid deleting too many in one run
+ int deleteMax = (int) Math.min(1000, Math.max(10, remoteSessionsFromZooKeeper.size() * 0.01));
for (long sessionId : remoteSessionsFromZooKeeper) {
Session session = remoteSessionCache.get(sessionId);
- if (session == null) {
- log.log(Level.FINE, () -> "Remote session " + sessionId + " is null, creating a new one");
+ if (session == null)
session = new RemoteSession(tenantName, sessionId, createSessionZooKeeperClient(sessionId));
- }
if (session.getStatus() == Session.Status.ACTIVATE) continue;
if (sessionHasExpired(session.getCreateTime(), expiryTime, clock)) {
log.log(Level.FINE, () -> "Remote session " + sessionId + " for " + tenantName + " has expired, deleting it");
deleteRemoteSessionFromZooKeeper(session);
deleted++;
}
- // Avoid deleting too many in one run
- if (deleted >= 2)
+ if (deleted >= deleteMax)
break;
}
return deleted;
}
- public void deactivateAndUpdateCache(RemoteSession remoteSession) {
- RemoteSession session = remoteSession.deactivated();
- remoteSessionCache.put(session.getSessionId(), session);
+ public void deactivateSession(long sessionId) {
+ var s = remoteSessionCache.get(sessionId);
+ if (s == null) return;
+
+ remoteSessionCache.put(sessionId, s.deactivated());
}
public void deleteRemoteSessionFromZooKeeper(Session session) {
@@ -390,7 +393,7 @@ public class SessionRepository {
}
private boolean sessionHasExpired(Instant created, Duration expiryTime, Clock clock) {
- return (created.plus(expiryTime).isBefore(clock.instant()));
+ return created.plus(expiryTime).isBefore(clock.instant());
}
private List<Long> getSessionListFromDirectoryCache(List<ChildData> children) {
@@ -441,8 +444,11 @@ public class SessionRepository {
return session.getStatus() == Session.Status.DELETE;
}
- void activate(RemoteSession session) {
- long sessionId = session.getSessionId();
+ void activate(long sessionId) {
+ createLocalSessionFromDistributedApplicationPackage(sessionId);
+ RemoteSession session = remoteSessionCache.get(sessionId);
+ if (session == null) return;
+
CompletionWaiter waiter = createSessionZooKeeperClient(sessionId).getActiveWaiter();
log.log(Level.FINE, () -> session.logPre() + "Activating " + sessionId);
applicationRepo.activateApplication(ensureApplicationLoaded(session), sessionId);
@@ -451,21 +457,25 @@ public class SessionRepository {
log.log(Level.INFO, session.logPre() + "Session activated: " + sessionId);
}
- private Optional<RemoteSession> loadSessionIfActive(RemoteSession session) {
+ private void loadSessionIfActive(RemoteSession session) {
for (ApplicationId applicationId : applicationRepo.activeApplications()) {
Optional<Long> activeSession = applicationRepo.activeSessionOf(applicationId);
if (activeSession.isPresent() && activeSession.get() == session.getSessionId()) {
log.log(Level.FINE, () -> "Found active application for session " + session.getSessionId() + " , loading it");
applicationRepo.activateApplication(ensureApplicationLoaded(session), session.getSessionId());
log.log(Level.INFO, session.logPre() + "Application activated successfully: " + applicationId + " (generation " + session.getSessionId() + ")");
- return Optional.ofNullable(remoteSessionCache.get(session.getSessionId()));
+ return;
}
}
- return Optional.empty();
}
- void prepareRemoteSession(RemoteSession session) {
- SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(session.getSessionId());
+ void prepareRemoteSession(long sessionId) {
+ // Might need to create local session first
+ createLocalSessionFromDistributedApplicationPackage(sessionId);
+ RemoteSession session = remoteSessionCache.get(sessionId);
+ if (session == null) return;
+
+ SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(sessionId);
CompletionWaiter waiter = sessionZooKeeperClient.getPrepareWaiter();
ensureApplicationLoaded(session);
notifyCompletion(waiter);
@@ -482,13 +492,13 @@ public class SessionRepository {
RemoteSession activated = session.activated(applicationSet);
long sessionId = activated.getSessionId();
remoteSessionCache.put(sessionId, activated);
- updateSessionStateWatcher(sessionId, activated);
+ updateSessionStateWatcher(sessionId);
return applicationSet;
}
void confirmUpload(Session session) {
- CompletionWaiter waiter = session.getSessionZooKeeperClient().getUploadWaiter();
+ CompletionWaiter waiter = createSessionZooKeeperClient(session.getSessionId()).getUploadWaiter();
long sessionId = session.getSessionId();
log.log(Level.FINE, () -> "Notifying upload waiter for session " + sessionId);
notifyCompletion(waiter);
@@ -575,32 +585,31 @@ public class SessionRepository {
// ---------------- Common stuff ----------------------------------------------------------------
public void deleteExpiredSessions(Map<ApplicationId, Long> activeSessions) {
- log.log(Level.FINE, () -> "Purging old sessions for tenant '" + tenantName + "'");
+ log.log(Level.FINE, () -> "Deleting expired local sessions for tenant '" + tenantName + "'");
Set<LocalSession> toDelete = new HashSet<>();
Set<Long> newSessions = findNewSessionsInFileSystem();
try {
for (LocalSession candidate : getLocalSessionsFromFileSystem()) {
// Skip sessions newly added (we might have a session in the file system, but not in ZooKeeper,
// we don't want to touch any of them)
- if (newSessions.contains(candidate.getSessionId())) {
- log.log(Level.FINE, () -> "Skipping expiring newly created session " + candidate.getSessionId());
+ if (newSessions.contains(candidate.getSessionId()))
continue;
- }
Instant createTime = candidate.getCreateTime();
- log.log(Level.FINE, () -> "Candidate session for deletion: " + candidate.getSessionId() + ", created: " + createTime);
+ log.log(Level.FINE, () -> "Candidate local session for deletion: " + candidate.getSessionId() +
+ ", created: " + createTime + ", state " + candidate.getStatus() + ", can be deleted: " + canBeDeleted(candidate));
- if (hasExpired(candidate) && canBeDeleted(candidate)) {
+ if (hasExpired(createTime) && canBeDeleted(candidate)) {
toDelete.add(candidate);
} else if (createTime.plus(Duration.ofDays(1)).isBefore(clock.instant())) {
- // Sessions with state ACTIVATE, but which are not actually active
Optional<ApplicationId> applicationId = candidate.getOptionalApplicationId();
if (applicationId.isEmpty()) continue;
+
Long activeSession = activeSessions.get(applicationId.get());
if (activeSession == null || activeSession != candidate.getSessionId()) {
toDelete.add(candidate);
- log.log(Level.INFO, "Deleted inactive session " + candidate.getSessionId() + " created " +
- createTime + " for '" + applicationId + "'");
+ log.log(Level.FINE, () -> "Will delete inactive session " + candidate.getSessionId() + " created " +
+ createTime + " for '" + applicationId + "'");
}
}
}
@@ -614,21 +623,22 @@ public class SessionRepository {
log.log(Level.FINE, () -> "Done purging old sessions");
}
- private boolean hasExpired(LocalSession candidate) {
- return candidate.getCreateTime().plus(sessionLifetime).isBefore(clock.instant());
+ private boolean hasExpired(Instant created) {
+ return created.plus(sessionLifetime).isBefore(clock.instant());
}
// Sessions with state other than UNKNOWN or ACTIVATE or old sessions in UNKNOWN state
private boolean canBeDeleted(LocalSession candidate) {
- return ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(candidate.getStatus())
- || oldSessionDirWithNonExistingSession(candidate);
+ return ( ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(candidate.getStatus()))
+ || oldSessionDirWithUnknownStatus(candidate);
}
- private boolean oldSessionDirWithNonExistingSession(LocalSession session) {
+ private boolean oldSessionDirWithUnknownStatus(LocalSession session) {
+ Duration expiryTime = Duration.ofHours(configserverConfig.keepSessionsWithUnknownStatusHours());
File sessionDir = tenantFileSystemDirs.getUserApplicationDir(session.getSessionId());
return sessionDir.exists()
&& session.getStatus() == Session.Status.UNKNOWN
- && created(sessionDir).plus(Duration.ofDays(30)).isBefore(clock.instant());
+ && created(sessionDir).plus(expiryTime).isBefore(clock.instant());
}
private Set<Long> findNewSessionsInFileSystem() {
@@ -673,29 +683,30 @@ public class SessionRepository {
boolean internalRedeploy,
Optional<DeployLogger> deployLogger) {
long deployTimestamp = System.currentTimeMillis();
- String user = System.getenv("USER");
- if (user == null) {
- user = "unknown";
- }
- DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationId, deployTimestamp,
- internalRedeploy, sessionId, currentlyActiveSessionId.orElse(nonExistingActiveSessionId));
+ DeployData deployData = new DeployData(userDir.getAbsolutePath(), applicationId, deployTimestamp, internalRedeploy,
+ sessionId, currentlyActiveSessionId.orElse(nonExistingActiveSessionId));
FilesApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(configApplicationDir, deployData);
+ validateFileExtensions(applicationId, deployLogger, app);
+
+ return app;
+ }
+
+ private void validateFileExtensions(ApplicationId applicationId, Optional<DeployLogger> deployLogger, FilesApplicationPackage app) {
try {
app.validateFileExtensions();
} catch (IllegalArgumentException e) {
- switch (failDeploymentForFilesWithUnknownExtension.value()) {
- case "FAIL":
- throw e;
- case "LOG":
- deployLogger.ifPresent(logger -> logger.logApplicationPackage(Level.WARNING, e.getMessage()));
- break;
- case "NOOP":
- default:
- break;
+ if (configserverConfig.hostedVespa()) {
+ UnboundStringFlag flag = Flags.APPLICATION_FILES_WITH_UNKNOWN_EXTENSION;
+ String value = flag.bindTo(flagSource).with(APPLICATION_ID, applicationId.serializedForm()).value();
+ switch (value) {
+ case "FAIL" -> throw e;
+ case "LOG" -> deployLogger.ifPresent(logger -> logger.logApplicationPackage(Level.WARNING, e.getMessage()));
+ default -> log.log(Level.WARNING, "Unknown value for flag " + flag.id() + ": " + value);
+ }
+ } else {
+ deployLogger.ifPresent(logger -> logger.logApplicationPackage(Level.WARNING, e.getMessage()));
}
}
-
- return app;
}
private LocalSession createSessionFromApplication(File applicationDirectory,
@@ -816,8 +827,9 @@ public class SessionRepository {
}
/**
- * Returns a new local session for the given session id if it does not already exist.
- * Will also add the session to the local session cache if necessary
+ * Create a new local session for the given session id if it does not already exist.
+ * Will also add the session to the local session cache if necessary. If there is no
+ * remote session matching the session it will also be created.
*/
public void createLocalSessionFromDistributedApplicationPackage(long sessionId) {
if (applicationRepo.sessionExistsInFileSystem(sessionId)) {
@@ -864,7 +876,7 @@ public class SessionRepository {
return getSessionPath(sessionId).append(ZKApplication.SESSIONSTATE_ZK_SUBPATH);
}
- private SessionZooKeeperClient createSessionZooKeeperClient(long sessionId) {
+ public SessionZooKeeperClient createSessionZooKeeperClient(long sessionId) {
return new SessionZooKeeperClient(curator,
tenantName,
sessionId,
@@ -885,15 +897,12 @@ public class SessionRepository {
return new TenantFileSystemDirs(configServerDB, tenantName).getUserApplicationDir(sessionId);
}
- private void updateSessionStateWatcher(long sessionId, RemoteSession remoteSession) {
- SessionStateWatcher sessionStateWatcher = sessionStateWatchers.get(sessionId);
- if (sessionStateWatcher == null) {
- Curator.FileCache fileCache = curator.createFileCache(getSessionStatePath(sessionId).getAbsolute(), false);
+ private void updateSessionStateWatcher(long sessionId) {
+ sessionStateWatchers.computeIfAbsent(sessionId, (id) -> {
+ Curator.FileCache fileCache = curator.createFileCache(getSessionStatePath(id).getAbsolute(), false);
fileCache.addListener(this::nodeChanged);
- sessionStateWatchers.put(sessionId, new SessionStateWatcher(fileCache, remoteSession, metricUpdater, zkWatcherExecutor, this));
- } else {
- sessionStateWatcher.updateRemoteSession(remoteSession);
- }
+ return new SessionStateWatcher(fileCache, id, metricUpdater, zkWatcherExecutor, this);
+ });
}
@Override
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java
index 6c6f60426fe..082743e48f9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java
@@ -13,7 +13,7 @@ import java.util.logging.Logger;
import static com.yahoo.vespa.config.server.session.Session.Status;
/**
- * Watches one particular session (/config/v2/tenants/&lt;tenantName&gt;/sessions/&lt;n&gt;/sessionState in ZooKeeper)
+ * Watches session state for a session (/config/v2/tenants/&lt;tenantName&gt;/sessions/&lt;n&gt;/sessionState in ZooKeeper)
* The session must be in the session repo.
*
* @author Vegard Havdal
@@ -24,18 +24,18 @@ public class SessionStateWatcher {
private static final Logger log = Logger.getLogger(SessionStateWatcher.class.getName());
private final Curator.FileCache fileCache;
- private volatile RemoteSession session;
+ private final long sessionId;
private final MetricUpdater metrics;
private final Executor zkWatcherExecutor;
private final SessionRepository sessionRepository;
SessionStateWatcher(Curator.FileCache fileCache,
- RemoteSession session,
+ long sessionId,
MetricUpdater metrics,
Executor zkWatcherExecutor,
SessionRepository sessionRepository) {
this.fileCache = fileCache;
- this.session = session;
+ this.sessionId = sessionId;
this.metrics = metrics;
this.fileCache.addListener(this::nodeChanged);
this.fileCache.start();
@@ -44,39 +44,25 @@ public class SessionStateWatcher {
}
private synchronized void sessionStatusChanged(Status newStatus) {
- long sessionId = session.getSessionId();
-
switch (newStatus) {
case NEW:
case UNKNOWN:
break;
case DELETE:
- sessionRepository.deactivateAndUpdateCache(session);
+ case DEACTIVATE:
+ sessionRepository.deactivateSession(sessionId);
break;
case PREPARE:
- createLocalSession(sessionId);
- sessionRepository.prepareRemoteSession(session);
+ sessionRepository.prepareRemoteSession(sessionId);
break;
case ACTIVATE:
- createLocalSession(sessionId);
- sessionRepository.activate(session);
- break;
- case DEACTIVATE:
- sessionRepository.deactivateAndUpdateCache(session);
+ sessionRepository.activate(sessionId);
break;
default:
throw new IllegalStateException("Unknown status " + newStatus);
}
}
- private void createLocalSession(long sessionId) {
- sessionRepository.createLocalSessionFromDistributedApplicationPackage(sessionId);
- }
-
- public long getSessionId() {
- return session.getSessionId();
- }
-
public void close() {
try {
fileCache.close();
@@ -92,27 +78,13 @@ public class SessionStateWatcher {
ChildData node = fileCache.getCurrentData();
if (node != null) {
newStatus = Status.parse(Utf8.toString(node.getData()));
-
- String debugMessage = log.isLoggable(Level.FINE) ?
- session.logPre() + "Session " + session.getSessionId()
- + " changed status to " + newStatus.name() :
- null;
- if (debugMessage != null) log.fine(debugMessage);
-
sessionStatusChanged(newStatus);
-
- if (debugMessage != null) log.fine(debugMessage + ": Done");
}
} catch (Exception e) {
- log.log(Level.WARNING, session.logPre() + "Error handling session change to " +
- newStatus.name() + " for session " + getSessionId(), e);
+ log.log(Level.WARNING, "Error handling session change to " + newStatus.name() + " for session " + sessionId, e);
metrics.incSessionChangeErrors();
}
});
}
- public synchronized void updateRemoteSession(RemoteSession session) {
- this.session = session;
- }
-
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java
index a3ddae8f7aa..4a1e81b9058 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java
@@ -19,13 +19,7 @@ import java.util.logging.Logger;
*
* @author andreer
*/
-public class EndpointCertificateRetriever {
-
- private final SecretStore secretStore;
-
- public EndpointCertificateRetriever(SecretStore secretStore) {
- this.secretStore = secretStore;
- }
+public record EndpointCertificateRetriever(SecretStore secretStore) {
private static final Logger log = Logger.getLogger(EndpointCertificateRetriever.class.getName());
@@ -40,11 +34,11 @@ public class EndpointCertificateRetriever {
verifyKeyMatchesCertificate(endpointCertificateMetadata, cert, key);
- return new EndpointCertificateSecrets(cert, key);
+ return new EndpointCertificateSecrets(cert, key, endpointCertificateMetadata.version());
} catch (RuntimeException e) {
log.log(Level.WARNING, "Exception thrown during certificate retrieval", e);
// Assume not ready yet
- return EndpointCertificateSecrets.MISSING;
+ return EndpointCertificateSecrets.missing(endpointCertificateMetadata.version());
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
index 1325f065ebd..9cb90d7f50c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
@@ -19,7 +19,7 @@ import com.yahoo.path.Path;
import com.yahoo.text.Utf8;
import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.ConfigServerDB;
-import com.yahoo.vespa.config.server.ReloadListener;
+import com.yahoo.vespa.config.server.ConfigActivationListener;
import com.yahoo.vespa.config.server.application.PermanentApplicationPackage;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
@@ -115,7 +115,7 @@ public class TenantRepository {
private final Clock clock;
private final ModelFactoryRegistry modelFactoryRegistry;
private final ConfigDefinitionRepo configDefinitionRepo;
- private final ReloadListener reloadListener;
+ private final ConfigActivationListener configActivationListener;
private final ScheduledExecutorService checkForRemovedApplicationsService =
new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("check for removed applications"));
private final Curator.DirectoryCache directoryCache;
@@ -136,7 +136,7 @@ public class TenantRepository {
Zone zone,
ModelFactoryRegistry modelFactoryRegistry,
ConfigDefinitionRepo configDefinitionRepo,
- ReloadListener reloadListener,
+ ConfigActivationListener configActivationListener,
TenantListener tenantListener,
ZookeeperServerConfig zookeeperServerConfig) {
this(hostRegistry,
@@ -155,7 +155,7 @@ public class TenantRepository {
Clock.systemUTC(),
modelFactoryRegistry,
configDefinitionRepo,
- reloadListener,
+ configActivationListener,
tenantListener,
zookeeperServerConfig);
}
@@ -176,7 +176,7 @@ public class TenantRepository {
Clock clock,
ModelFactoryRegistry modelFactoryRegistry,
ConfigDefinitionRepo configDefinitionRepo,
- ReloadListener reloadListener,
+ ConfigActivationListener configActivationListener,
TenantListener tenantListener,
ZookeeperServerConfig zookeeperServerConfig) {
this.hostRegistry = hostRegistry;
@@ -196,7 +196,7 @@ public class TenantRepository {
this.clock = clock;
this.modelFactoryRegistry = modelFactoryRegistry;
this.configDefinitionRepo = configDefinitionRepo;
- this.reloadListener = reloadListener;
+ this.configActivationListener = configActivationListener;
this.tenantListener = tenantListener;
this.zookeeperServerConfig = zookeeperServerConfig;
// This we should control with a feature flag.
@@ -338,7 +338,7 @@ public class TenantRepository {
zkApplicationWatcherExecutor,
zkCacheExecutor,
metrics,
- reloadListener,
+ configActivationListener,
configserverConfig,
hostRegistry,
new TenantFileSystemDirs(configServerDB, tenantName),
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplication.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplication.java
index 107bd8be367..4c262379c35 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplication.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplication.java
@@ -84,7 +84,7 @@ public class ZKApplication {
}
/**
- * Retrieves a node relative to the node of the live application
+ * Retrieves a node relative to the node of the active application
*
* @param path a path relative to the currently active application
* @return a Reader that can be used to get the data
@@ -129,7 +129,7 @@ public class ZKApplication {
}
/**
- * Checks if the given node exists under path under this live app
+ * Checks if the given node exists under path under this active app
*
* @param path a zookeeper path
* @return true if the node exists in the path, false otherwise
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
index 0fa6e9a0704..9b23037ed19 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
@@ -5,7 +5,6 @@ import com.google.common.base.Joiner;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.application.api.ApplicationMetaData;
-import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.ComponentInfo;
import com.yahoo.config.application.api.FileRegistry;
import com.yahoo.config.application.api.UnparsedConfigDefinition;
@@ -59,7 +58,7 @@ public class ZKApplicationPackage extends AbstractApplicationPackage {
public ZKApplicationPackage(AddFileInterface fileManager, Curator curator, Path sessionPath, int maxNodeSize) {
verifyAppPath(curator, sessionPath);
zkApplication = new ZKApplication(curator, sessionPath, maxNodeSize);
- metaData = readMetaDataFromLiveApp(zkApplication);
+ metaData = readMetaDataFromActiveApp(zkApplication);
importFileRegistries(fileManager);
allocatedHosts = importAllocatedHosts();
}
@@ -102,13 +101,13 @@ public class ZKApplicationPackage extends AbstractApplicationPackage {
}
}
- private ApplicationMetaData readMetaDataFromLiveApp(ZKApplication liveApp) {
+ private ApplicationMetaData readMetaDataFromActiveApp(ZKApplication activeApp) {
Path metaPath = Path.fromString(ZKApplication.META_ZK_PATH);
- String metaDataString = liveApp.getData(metaPath);
+ String metaDataString = activeApp.getData(metaPath);
if (metaDataString == null || metaDataString.isEmpty()) {
return null;
}
- return ApplicationMetaData.fromJsonString(liveApp.getData(metaPath));
+ return ApplicationMetaData.fromJsonString(activeApp.getData(metaPath));
}
@Override
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index ee2a979be7a..d1d8c165124 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -233,7 +233,6 @@ public class ApplicationRepositoryTest {
assertEquals(originalApplicationMetaData.getApplicationId(), applicationMetaData.getApplicationId());
assertEquals(originalApplicationMetaData.getGeneration().longValue(), applicationMetaData.getPreviousActiveGeneration());
assertNotEquals(originalApplicationMetaData.getGeneration(), applicationMetaData.getGeneration());
- assertEquals(originalApplicationMetaData.getDeployedByUser(), applicationMetaData.getDeployedByUser());
}
@Test
@@ -446,7 +445,7 @@ public class ApplicationRepositoryTest {
assertEquals(3, localSession.getSessionId());
// All sessions except 3 should be removed after the call to deleteExpiredRemoteSessions
- assertEquals(2, tester.applicationRepository().deleteExpiredRemoteSessions(clock, Duration.ofSeconds(0)));
+ assertEquals(2, tester.applicationRepository().deleteExpiredRemoteSessions(clock));
ArrayList<Long> remoteSessions = new ArrayList<>(sessionRepository.getRemoteSessionsFromZooKeeper());
Session remoteSession = sessionRepository.getRemoteSession(remoteSessions.get(0));
assertEquals(3, remoteSession.getSessionId());
@@ -464,7 +463,8 @@ public class ApplicationRepositoryTest {
// and check that expiring local sessions still works
int sessionId = 6;
TenantName tenantName = tester.tenant().getName();
- java.nio.file.Path dir = Files.createDirectory(new TenantFileSystemDirs(serverdb, tenantName).getUserApplicationDir(sessionId).toPath());
+ Instant session6CreateTime = clock.instant();
+ Files.createDirectory(new TenantFileSystemDirs(serverdb, tenantName).getUserApplicationDir(sessionId).toPath());
LocalSession localSession2 = new LocalSession(tenant1,
sessionId,
FilesApplicationPackage.fromFile(testApp),
@@ -479,7 +479,7 @@ public class ApplicationRepositoryTest {
// so will be candidate for expiry)
Session session = sessionRepository.createRemoteSession(7);
sessionRepository.createSetStatusTransaction(session, Session.Status.UNKNOWN);
- assertEquals(2, sessionRepository.getLocalSessions().size()); // Still 2, no new local session
+ assertEquals(2, sessionRepository.getLocalSessions().size()); // Still 2, no new local session
// Check that trying to expire local session when there exists a local session without any data in zookeeper
// should not delete session if this is a new file ...
@@ -489,8 +489,11 @@ public class ApplicationRepositoryTest {
clock.advance(Duration.ofSeconds(60));
deleteExpiredLocalSessionsAndAssertNumberOfSessions(1, tester, sessionRepository);
- // Set older created timestamp for session dir for local session without any data in zookeeper, should be deleted
- setCreatedTime(dir, Instant.now().minus(Duration.ofDays(31)));
+ // Reset clock to the time session was created, session should NOT be deleted
+ clock.setInstant(session6CreateTime);
+ deleteExpiredLocalSessionsAndAssertNumberOfSessions(1, tester, sessionRepository);
+ // Advance time, session SHOULD be deleted
+ clock.advance(Duration.ofHours(configserverConfig.keepSessionsWithUnknownStatusHours()).plus(Duration.ofMinutes(1)));
deleteExpiredLocalSessionsAndAssertNumberOfSessions(0, tester, sessionRepository);
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
index 3f329894cef..c3766ad9b83 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java
@@ -58,14 +58,14 @@ public class SuperModelRequestHandlerTest {
assertNotNull(controller.getHandler());
long gen = counter.get();
- controller.reloadConfig(createApp(foo, 3L));
+ controller.activateConfig(createApp(foo, 3L));
assertNotNull(controller.getHandler());
assertEquals(gen + 1, controller.getHandler().getGeneration());
- controller.reloadConfig(createApp(foo, 4L));
+ controller.activateConfig(createApp(foo, 4L));
assertEquals(gen + 2, controller.getHandler().getGeneration());
// Test that a new app is used when there already exist an application with the same id
assertEquals(4, controller.getHandler().getSuperModel().applicationModels().get(foo).getGeneration());
- controller.reloadConfig(createApp(bar, 2L));
+ controller.activateConfig(createApp(bar, 2L));
assertEquals(gen + 3, controller.getHandler().getGeneration());
}
@@ -76,9 +76,9 @@ public class SuperModelRequestHandlerTest {
ApplicationId baz = applicationId("b", "baz");
long gen = counter.get();
- controller.reloadConfig(createApp(foo, 3L));
- controller.reloadConfig(createApp(bar, 30L));
- controller.reloadConfig(createApp(baz, 9L));
+ controller.activateConfig(createApp(foo, 3L));
+ controller.activateConfig(createApp(bar, 30L));
+ controller.activateConfig(createApp(baz, 9L));
assertEquals(gen + 3, controller.getHandler().getGeneration());
assertEquals(3, controller.getHandler().getSuperModel().applicationModels().size());
assertTrue(controller.getHandler().getSuperModel().applicationModels().keySet().containsAll(List.of(foo, bar, baz)));
@@ -101,7 +101,7 @@ public class SuperModelRequestHandlerTest {
controller = new SuperModelRequestHandler(new TestConfigDefinitionRepo(), configserverConfig, manager);
long gen = counter.get();
- controller.reloadConfig(createApp(foo, 3L));
+ controller.activateConfig(createApp(foo, 3L));
assertEquals(masterGen + gen + 1, controller.getHandler().getGeneration());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
index abb8f9a9df3..85e64b4a32d 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.config.server.application;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
-import com.yahoo.component.Vtag;
import com.yahoo.concurrent.InThreadExecutorService;
import com.yahoo.concurrent.StripedExecutor;
import com.yahoo.config.model.NullConfigModelRegistry;
@@ -14,7 +13,7 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.server.ConfigServerDB;
-import com.yahoo.vespa.config.server.ReloadListener;
+import com.yahoo.vespa.config.server.ConfigActivationListener;
import com.yahoo.vespa.config.server.ServerCache;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.host.HostRegistry;
@@ -51,8 +50,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
-import java.util.function.Supplier;
import java.util.stream.IntStream;
import static com.yahoo.vespa.config.server.application.TenantApplications.RemoveApplicationWaiter;
@@ -195,14 +192,14 @@ public class TenantApplicationsTest {
assertTrue("Node is compatible after upgrading", applications.compatibleWith(Optional.of(nodeVersion1), app1));
}
- public static class MockReloadListener implements ReloadListener {
- public final AtomicInteger reloaded = new AtomicInteger(0);
+ public static class MockConfigActivationListener implements ConfigActivationListener {
+ public final AtomicInteger activated = new AtomicInteger(0);
final AtomicInteger removed = new AtomicInteger(0);
final Map<String, Collection<String>> tenantHosts = new LinkedHashMap<>();
@Override
public void configActivated(ApplicationSet application) {
- reloaded.incrementAndGet();
+ activated.incrementAndGet();
}
@Override
@@ -222,7 +219,7 @@ public class TenantApplicationsTest {
@Test
public void testListConfigs() throws IOException, SAXException {
- TenantApplications applications = createTenantApplications(TenantName.defaultName(), new MockCurator(), configserverConfig, new MockReloadListener(), new InMemoryFlagSource());
+ TenantApplications applications = createTenantApplications(TenantName.defaultName(), new MockCurator(), configserverConfig, new MockConfigActivationListener(), new InMemoryFlagSource());
assertFalse(applications.hasApplication(ApplicationId.defaultId(), Optional.of(vespaVersion)));
VespaModel model = new VespaModel(FilesApplicationPackage.fromFile(new File("src/test/apps/app")));
@@ -250,11 +247,11 @@ public class TenantApplicationsTest {
@Test
public void testAppendIdsInNonRecursiveListing() {
- TenantApplications applications = createTenantApplications(tenantName, curator, configserverConfig, new MockReloadListener(), new InMemoryFlagSource());
- assertEquals(applications.appendOneLevelOfId("search/music", "search/music/qrservers/default/qr.0"), "search/music/qrservers");
- assertEquals(applications.appendOneLevelOfId("search", "search/music/qrservers/default/qr.0"), "search/music");
- assertEquals(applications.appendOneLevelOfId("search/music/qrservers/default/qr.0", "search/music/qrservers/default/qr.0"), "search/music/qrservers/default/qr.0");
- assertEquals(applications.appendOneLevelOfId("", "search/music/qrservers/default/qr.0"), "search");
+ TenantApplications applications = createTenantApplications(tenantName, curator, configserverConfig, new MockConfigActivationListener(), new InMemoryFlagSource());
+ assertEquals(applications.appendOneLevelOfId("search/music", "search/music/container/default/qr.0"), "search/music/container");
+ assertEquals(applications.appendOneLevelOfId("search", "search/music/container/default/qr.0"), "search/music");
+ assertEquals(applications.appendOneLevelOfId("search/music/container/default/qr.0", "search/music/container/default/qr.0"), "search/music/container/default/qr.0");
+ assertEquals(applications.appendOneLevelOfId("", "search/music/container/default/qr.0"), "search");
}
@Test
@@ -297,7 +294,7 @@ public class TenantApplicationsTest {
}
private TenantApplications createZKAppRepo(InMemoryFlagSource flagSource) {
- return createTenantApplications(tenantName, curator, configserverConfig, new MockReloadListener(), flagSource);
+ return createTenantApplications(tenantName, curator, configserverConfig, new MockConfigActivationListener(), flagSource);
}
private static ApplicationId createApplicationId() {
@@ -328,15 +325,15 @@ public class TenantApplicationsTest {
// For testing only
private TenantApplications createTenantApplications(TenantName tenantName,
- Curator curator,
- ConfigserverConfig configserverConfig,
- ReloadListener reloadListener, InMemoryFlagSource flagSource) {
+ Curator curator,
+ ConfigserverConfig configserverConfig,
+ ConfigActivationListener configActivationListener, InMemoryFlagSource flagSource) {
return new TenantApplications(tenantName,
curator,
new StripedExecutor<>(new InThreadExecutorService()),
new InThreadExecutorService(),
Metrics.createTestMetrics(),
- reloadListener,
+ configActivationListener,
configserverConfig,
new HostRegistry(),
new TenantFileSystemDirs(new ConfigServerDB(configserverConfig), tenantName),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
index 41ac081f68b..fd6440a9632 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
@@ -57,8 +57,7 @@ public class ZooKeeperClientTest {
zk = new MockCurator();
ZooKeeperClient zkc = new ZooKeeperClient(zk, new BaseDeployLogger(), appPath);
ApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(new File("src/test/apps/zkfeed"),
- new DeployData("foo",
- "/bar/baz",
+ new DeployData("/bar/baz",
ApplicationId.from("default", "appName", "default"),
1345L,
true,
@@ -121,7 +120,6 @@ public class ZooKeeperClientTest {
Utf8.toString(zk.getData(appPath.append(META_ZK_PATH)).get()));
assertTrue(metaData.getChecksum().length() > 0);
assertTrue(metaData.isInternalRedeploy());
- assertEquals("foo", metaData.getDeployedByUser());
assertEquals("/bar/baz", metaData.getDeployPath());
assertEquals(1345, metaData.getDeployTimestamp().longValue());
assertEquals(3, metaData.getGeneration().longValue());
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
index 67c40f94b6a..39219471bb1 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
@@ -8,21 +8,25 @@ import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Transport;
import com.yahoo.net.HostName;
import com.yahoo.vespa.filedistribution.FileDownloader;
+import com.yahoo.vespa.filedistribution.FileReferenceCompressor;
import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.lz4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -37,7 +41,7 @@ public class FileServerTest {
@Before
public void setup() throws IOException {
File rootDir = new File(temporaryFolder.newFolder("fileserver-root").getAbsolutePath());
- fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir));
+ fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir), List.of(gzip, lz4));
}
@Test
@@ -75,11 +79,30 @@ public class FileServerTest {
File dir = getFileServerRootDir();
IOUtils.writeFile(dir + "/12y/f1", "dummy-data", true);
CompletableFuture<byte []> content = new CompletableFuture<>();
- fileServer.startFileServing("12y", new FileReceiver(content));
+ fileServer.startFileServing(new FileReference("12y"), new FileReceiver(content), Set.of(gzip));
assertEquals(new String(content.get()), "dummy-data");
}
@Test
+ public void requireThatWeCanReplayDirWithLz4() throws IOException, InterruptedException, ExecutionException {
+ File rootDir = new File(temporaryFolder.newFolder("fileserver-root-3").getAbsolutePath());
+ fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir), List.of(lz4, gzip)); // prefer lz4
+ File dir = getFileServerRootDir();
+ IOUtils.writeFile(dir + "/subdir/12z/f1", "dummy-data-2", true);
+ CompletableFuture<byte []> content = new CompletableFuture<>();
+ fileServer.startFileServing(new FileReference("subdir"), new FileReceiver(content), Set.of(gzip, lz4));
+
+ // Decompress with lz4 and check contents
+ var compressor = new FileReferenceCompressor(FileReferenceData.Type.compressed, lz4);
+ File downloadedFileCompressed = new File(dir + "/downloaded-file-compressed");
+ IOUtils.writeFile(downloadedFileCompressed, content.get());
+ File downloadedFileUncompressed = new File(dir + "/downloaded-file-uncompressed");
+ compressor.decompress(downloadedFileCompressed, downloadedFileUncompressed);
+ assertTrue(downloadedFileUncompressed.isDirectory());
+ assertEquals("dummy-data-2", IOUtils.readFile(new File(downloadedFileUncompressed, "12z/f1")));
+ }
+
+ @Test
public void requireThatDifferentNumberOfConfigServersWork() throws IOException {
// Empty connection pool in tests etc.
ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder()
@@ -117,7 +140,7 @@ public class FileServerTest {
private FileServer createFileServer(ConfigserverConfig.Builder configBuilder) throws IOException {
File fileReferencesDir = temporaryFolder.newFolder();
configBuilder.fileReferencesDir(fileReferencesDir.getAbsolutePath());
- return new FileServer(new ConfigserverConfig(configBuilder));
+ return new FileServer(new ConfigserverConfig(configBuilder), new InMemoryFlagSource());
}
private static class FileReceiver implements FileServer.Receiver {
@@ -142,7 +165,8 @@ public class FileServerTest {
new Supervisor(new Transport("mock")).setDropEmptyBuffers(true),
downloadDirectory,
Duration.ofMillis(100),
- Duration.ofMillis(100));
+ Duration.ofMillis(100),
+ Set.of(gzip));
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
index 7ad237e45ed..8607fc0e2dc 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
@@ -5,6 +5,7 @@ import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.LbServicesConfig;
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.component.Version;
+import com.yahoo.config.FileReference;
import com.yahoo.config.SimpletypesConfig;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
@@ -27,16 +28,20 @@ import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.session.PrepareParams;
+import com.yahoo.vespa.filedistribution.LazyFileReferenceData;
import com.yahoo.vespa.model.VespaModel;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.xml.sax.SAXException;
-
import java.io.File;
import java.io.IOException;
import java.util.Optional;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.lz4;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type.compressed;
+import static com.yahoo.vespa.config.server.rpc.RpcServer.ChunkedFileReceiver.createMetaRequest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -97,6 +102,25 @@ public class RpcServerTest {
}
}
+ @Test
+ public void testFileReceiverMetaRequest() throws IOException {
+ File file = temporaryFolder.newFile();
+ Request request = createMetaRequest(new LazyFileReferenceData(new FileReference("foo"), "fileA", compressed, file, gzip));
+ assertEquals(4, request.parameters().size());
+ assertEquals("foo", request.parameters().get(0).asString());
+ assertEquals("fileA", request.parameters().get(1).asString());
+ assertEquals("compressed", request.parameters().get(2).asString());
+ assertEquals(0, request.parameters().get(3).asInt64());
+
+ request = createMetaRequest(new LazyFileReferenceData(new FileReference("foo"), "fileA", compressed, file, lz4));
+ assertEquals(5, request.parameters().size());
+ assertEquals("foo", request.parameters().get(0).asString());
+ assertEquals("fileA", request.parameters().get(1).asString());
+ assertEquals("compressed", request.parameters().get(2).asString());
+ assertEquals(0, request.parameters().get(3).asInt64());
+ assertEquals("lz4", request.parameters().get(4).asString());
+ }
+
private JRTClientConfigRequest createSimpleRequest() {
ConfigKey<?> key = new ConfigKey<>(SimpletypesConfig.class, "");
JRTClientConfigRequest clientReq = createRequest(new RawConfig(key, SimpletypesConfig.getDefMd5()));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java
index 40ed20b7969..441f6c3a6ce 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java
@@ -166,7 +166,7 @@ public class RpcTester implements AutoCloseable {
void performRequest(Request req) {
clock.advance(Duration.ofMillis(10));
- sup.connect(spec).invokeSync(req, 10.0);
+ sup.connect(spec).invokeSync(req, Duration.ofSeconds(10));
if (req.methodName().equals(RpcServer.getConfigMethodName))
assertEquals(clock.instant(), hostLivenessTracker.lastRequestFrom(myHostname).get());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
index 2650b23a38e..2ab959fcaa0 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
@@ -11,7 +11,6 @@ import com.yahoo.config.provision.security.NodeIdentifier;
import com.yahoo.config.provision.security.NodeIdentifierException;
import com.yahoo.config.provision.security.NodeIdentity;
import com.yahoo.jrt.Request;
-import com.yahoo.jrt.SecurityContext;
import com.yahoo.jrt.StringValue;
import com.yahoo.jrt.Target;
import com.yahoo.jrt.Values;
@@ -19,6 +18,9 @@ import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.SignatureAlgorithm;
import com.yahoo.security.X509CertificateBuilder;
+import com.yahoo.security.tls.CapabilityMode;
+import com.yahoo.security.tls.CapabilitySet;
+import com.yahoo.security.tls.ConnectionAuthContext;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
@@ -248,10 +250,10 @@ public class MultiTenantRpcAuthorizerTest {
}
private static Request mockJrtRpcRequest(String payload) {
- SecurityContext securityContext = mock(SecurityContext.class);
- when(securityContext.peerCertificateChain()).thenReturn(PEER_CERTIFICATE_CHAIN);
+ ConnectionAuthContext authContext =
+ new ConnectionAuthContext(PEER_CERTIFICATE_CHAIN, CapabilitySet.all(), Set.of(), CapabilityMode.ENFORCE);
Target target = mock(Target.class);
- when(target.getSecurityContext()).thenReturn(Optional.of(securityContext));
+ when(target.connectionAuthContext()).thenReturn(authContext);
Request request = mock(Request.class);
when(request.target()).thenReturn(target);
Values values = new Values();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
index 86f8dc03af3..1960ba43747 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
@@ -6,7 +6,6 @@ import com.yahoo.cloud.config.ZookeeperServerConfig;
import com.yahoo.component.Version;
import com.yahoo.concurrent.InThreadExecutorService;
import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
@@ -59,7 +58,7 @@ public class TenantRepositoryTest {
private static final TenantName tenant3 = TenantName.from("tenant3");
private TenantRepository tenantRepository;
- private TenantApplicationsTest.MockReloadListener listener;
+ private TenantApplicationsTest.MockConfigActivationListener listener;
private MockTenantListener tenantListener;
private Curator curator;
private ConfigserverConfig configserverConfig;
@@ -74,7 +73,7 @@ public class TenantRepositoryTest {
@Before
public void setupSessions() throws IOException {
curator = new MockCurator();
- listener = new TenantApplicationsTest.MockReloadListener();
+ listener = new TenantApplicationsTest.MockConfigActivationListener();
tenantListener = new MockTenantListener();
assertFalse(tenantListener.tenantsLoaded);
configserverConfig = new ConfigserverConfig.Builder()
@@ -83,7 +82,7 @@ public class TenantRepositoryTest {
.build();
tenantRepository = new TestTenantRepository.Builder().withConfigserverConfig(configserverConfig)
.withCurator(curator)
- .withReloadListener(listener)
+ .withConfigActivationListener(listener)
.withTenantListener(tenantListener)
.build();
assertTrue(tenantListener.tenantsLoaded);
@@ -115,7 +114,7 @@ public class TenantRepositoryTest {
MetricUpdater.createTestUpdater(),
id)),
4);
- assertEquals(1, listener.reloaded.get());
+ assertEquals(1, listener.activated.get());
}
@Test
@@ -224,7 +223,7 @@ public class TenantRepositoryTest {
Clock.systemUTC(),
new ModelFactoryRegistry(List.of(VespaModelFactory.createTestFactory())),
new TestConfigDefinitionRepo(),
- new TenantApplicationsTest.MockReloadListener(),
+ new TenantApplicationsTest.MockConfigActivationListener(),
new MockTenantListener(),
new ZookeeperServerConfig.Builder().myid(0).build());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
index 42bb62b06c2..b8c46c2bdc4 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
@@ -5,12 +5,11 @@ import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.ZookeeperServerConfig;
import com.yahoo.concurrent.InThreadExecutorService;
import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.config.server.ConfigServerDB;
import com.yahoo.vespa.config.server.MockSecretStore;
-import com.yahoo.vespa.config.server.ReloadListener;
+import com.yahoo.vespa.config.server.ConfigActivationListener;
import com.yahoo.vespa.config.server.TestConfigDefinitionRepo;
import com.yahoo.vespa.config.server.application.TenantApplicationsTest;
import com.yahoo.vespa.config.server.filedistribution.FileDistributionFactory;
@@ -44,7 +43,7 @@ public class TestTenantRepository extends TenantRepository {
Clock clock,
ModelFactoryRegistry modelFactoryRegistry,
ConfigDefinitionRepo configDefinitionRepo,
- ReloadListener reloadListener,
+ ConfigActivationListener configActivationListener,
TenantListener tenantListener) {
super(hostRegistry,
curator,
@@ -62,7 +61,7 @@ public class TestTenantRepository extends TenantRepository {
clock,
modelFactoryRegistry,
configDefinitionRepo,
- reloadListener,
+ configActivationListener,
tenantListener,
new ZookeeperServerConfig.Builder().myid(0).build());
}
@@ -78,7 +77,7 @@ public class TestTenantRepository extends TenantRepository {
HostProvisionerProvider hostProvisionerProvider = HostProvisionerProvider.empty();
ModelFactoryRegistry modelFactoryRegistry = new ModelFactoryRegistry(List.of(VespaModelFactory.createTestFactory()));
ConfigserverConfig configserverConfig = new ConfigserverConfig.Builder().build();
- ReloadListener reloadListener = new TenantApplicationsTest.MockReloadListener();
+ ConfigActivationListener configActivationListener = new TenantApplicationsTest.MockConfigActivationListener();
TenantListener tenantListener = new MockTenantListener();
Zone zone = Zone.defaultZone();
@@ -127,8 +126,8 @@ public class TestTenantRepository extends TenantRepository {
return this;
}
- public Builder withReloadListener(ReloadListener reloadListener) {
- this.reloadListener = reloadListener;
+ public Builder withConfigActivationListener(ConfigActivationListener configActivationListener) {
+ this.configActivationListener = configActivationListener;
return this;
}
@@ -156,7 +155,7 @@ public class TestTenantRepository extends TenantRepository {
clock,
modelFactoryRegistry,
configDefinitionRepo,
- reloadListener,
+ configActivationListener,
tenantListener);
}