diff options
75 files changed, 543 insertions, 314 deletions
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/DeployData.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/DeployData.java index f48dffecb27..cd13305c009 100644 --- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/DeployData.java +++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/DeployData.java @@ -8,27 +8,37 @@ package com.yahoo.config.model.application.provider; */ public class DeployData { - /* Which user deployed */ + /** Which user deployed */ private final String deployedByUser; - /* Name of application given by user */ + /** Name of application given by user */ private final String applicationName; - /* The absolute path to the directory holding the application */ + /** The absolute path to the directory holding the application */ private final String deployedFromDir; - /* Timestamp when a deployment was made */ + /** Timestamp when a deployment was made */ private final long deployTimestamp; + /** Whether this is an internal redeploy, not caused by an application package change */ + private final boolean internalRedeploy; + /* Application generation. Incremented by one each time an application is deployed. */ private final long generation; private final long currentlyActiveGeneration; - public DeployData(String deployedByUser, String deployedFromDir, String applicationName, Long deployTimestamp, Long generation, long currentlyActiveGeneration) { + public DeployData(String deployedByUser, + String deployedFromDir, + String applicationName, + Long deployTimestamp, + boolean internalRedeploy, + Long generation, + long currentlyActiveGeneration) { this.deployedByUser = deployedByUser; this.deployedFromDir = deployedFromDir; this.applicationName = applicationName; this.deployTimestamp = deployTimestamp; + this.internalRedeploy = internalRedeploy; this.generation = generation; this.currentlyActiveGeneration = currentlyActiveGeneration; } @@ -45,6 +55,8 @@ public class DeployData { return deployTimestamp; } + public boolean isInternalRedeploy() { return internalRedeploy; } + public long getGeneration() { return generation; } diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java index 9bfcd4ecb6d..8fc871a1aa9 100644 --- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java +++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java @@ -113,9 +113,13 @@ public class FilesApplicationPackage implements ApplicationPackage { } private static ApplicationMetaData metaDataFromDeployData(File appDir, DeployData deployData) { - return new ApplicationMetaData(deployData.getDeployedByUser(), deployData.getDeployedFromDir(), - deployData.getDeployTimestamp(), deployData.getApplicationName(), - computeCheckSum(appDir), deployData.getGeneration(), + return new ApplicationMetaData(deployData.getDeployedByUser(), + deployData.getDeployedFromDir(), + deployData.getDeployTimestamp(), + deployData.isInternalRedeploy(), + deployData.getApplicationName(), + computeCheckSum(appDir), + deployData.getGeneration(), deployData.getCurrentlyActiveGeneration()); } @@ -566,7 +570,7 @@ public class FilesApplicationPackage implements ApplicationPackage { } public static ApplicationMetaData readMetaData(File appDir) { - ApplicationMetaData defaultMetaData = new ApplicationMetaData(appDir, "n/a", "n/a", 0l, "", 0l, 0l); + ApplicationMetaData defaultMetaData = new ApplicationMetaData(appDir, "n/a", "n/a", 0l, false, "", 0l, 0l); File metaFile = new File(appDir, META_FILE_NAME); if (!metaFile.exists()) { return defaultMetaData; diff --git a/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java index ebe93f16738..04405839a9b 100644 --- a/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java +++ b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java @@ -14,22 +14,25 @@ import java.util.Map; public abstract class ConfigInstance extends InnerNode { public interface Builder extends ConfigBuilder { + /** * Dispatches a getConfig() call if this instance's producer is of the right type * @param producer a config producer * @return true if this instance's producer was the correct type, and hence a getConfig call was dispatched */ - public boolean dispatchGetConfig(Producer producer); + boolean dispatchGetConfig(Producer producer); + + String getDefName(); + String getDefNamespace(); + String getDefMd5(); - public String getDefName(); - public String getDefNamespace(); - public String getDefMd5(); } public interface Producer {} private String configMd5 = ""; + @SuppressWarnings("unused") // Used by reflection from ConfigInstanceUtil String configId; /** diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java index 41a0feff5d4..a3769299cf8 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java @@ -11,27 +11,32 @@ import java.io.*; * Metadata about an application package. * * @author hmusum - * @since 5.0 */ public class ApplicationMetaData { + private final String deployedByUser; private final String deployedFromDir; private final long deployTimestamp; + private final boolean internalRedeploy; private final long generation; private final long previousActiveGeneration; private final String checkSum; private final String appName; public ApplicationMetaData(File appDir, String deployedByUser, String deployedFromDir, Long deployTimestamp, + boolean internalRedeploy, String checkSum, Long generation, long previousActiveGeneration) { - this(deployedByUser, deployedFromDir, deployTimestamp, appDir.getName(), checkSum, generation, previousActiveGeneration); + this(deployedByUser, deployedFromDir, deployTimestamp, internalRedeploy, + appDir.getName(), checkSum, generation, previousActiveGeneration); } - public ApplicationMetaData(String deployedByUser, String deployedFromDir, Long deployTimestamp, String applicationName, String checkSum, Long generation, long previousActiveGeneration) { + public ApplicationMetaData(String deployedByUser, String deployedFromDir, Long deployTimestamp, boolean internalRedeploy, + String applicationName, String checkSum, Long generation, long previousActiveGeneration) { this.appName = applicationName; this.deployedByUser = deployedByUser; this.deployedFromDir = deployedFromDir; this.deployTimestamp = deployTimestamp; + this.internalRedeploy = internalRedeploy; this.checkSum = checkSum; this.generation = generation; this.previousActiveGeneration = previousActiveGeneration; @@ -88,6 +93,12 @@ public class ApplicationMetaData { } /** + * Returns whether this application generation was produced by a system internal redeployment, + * not an application package change + */ + public boolean isInternalRedeploy() { return internalRedeploy; } + + /** * Returns an md5 hash of the contents of the application package * @return an md5sum of the application package */ @@ -115,7 +126,14 @@ public class ApplicationMetaData { Inspector root = data.get(); Inspector deploy = root.field("deploy"); Inspector app = root.field("application"); - return new ApplicationMetaData(deploy.field("user").asString(), deploy.field("from").asString(), deploy.field("timestamp").asLong(), app.field("name").asString(), app.field("checksum").asString(), app.field("generation").asLong(), app.field("previousActiveGeneration").asLong()); + return new ApplicationMetaData(deploy.field("user").asString(), + deploy.field("from").asString(), + deploy.field("timestamp").asLong(), + booleanField("internalRedeploy", false, deploy), + app.field("name").asString(), + app.field("checksum").asString(), + app.field("generation").asLong(), + app.field("previousActiveGeneration").asLong()); } catch (Exception e) { throw new IllegalArgumentException("Error parsing json metadata", e); } @@ -128,6 +146,7 @@ public class ApplicationMetaData { deploy.setString("user", deployedByUser); deploy.setString("from", deployedFromDir); deploy.setLong("timestamp", deployTimestamp); + deploy.setBool("internalRedeploy", internalRedeploy); Cursor app = meta.setObject("application"); app.setString("name", appName); app.setString("checksum", checkSum); @@ -136,6 +155,12 @@ public class ApplicationMetaData { return slime; } + private static boolean booleanField(String fieldName, boolean defaultValue, Inspector object) { + Inspector value = object.field(fieldName); + if ( ! value.valid()) return defaultValue; + return value.asBool(); + } + public String asJsonString() { Slime slime = getSlime(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java index fe6f7da2092..dd54fe11c39 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java @@ -224,9 +224,10 @@ public interface ApplicationPackage { /** * Gets the ApplicationMetaData instance for this application package. + * * @return an ApplicationMetaData instance */ - default ApplicationMetaData getMetaData() { return null; } + ApplicationMetaData getMetaData(); default File getFileReference(Path pathRelativeToAppDir) { throw new UnsupportedOperationException("This application package cannot return file references"); diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationInfo.java index c6a72ebb3ff..486db205a4c 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationInfo.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationInfo.java @@ -4,6 +4,7 @@ package com.yahoo.config.model.api; import com.yahoo.config.provision.ApplicationId; public class ApplicationInfo { + private final ApplicationId applicationId; private final long generation; private final Model model; // NOT immutable @@ -23,4 +24,5 @@ public class ApplicationInfo { public Model getModel() { return model; } + } diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java index 8ca0e5c501c..0cfde3c655c 100644 --- a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java +++ b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java @@ -1,6 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.model.test; +import com.yahoo.config.application.api.ApplicationMetaData; import com.yahoo.config.application.api.ComponentInfo; import com.yahoo.config.application.api.UnparsedConfigDefinition; import com.yahoo.config.application.api.ApplicationFile; @@ -39,6 +40,7 @@ public class MockApplicationPackage implements ApplicationPackage { private final Optional<String> validationOverrides; private final boolean failOnValidateXml; private final QueryProfileRegistry queryProfileRegistry; + private final ApplicationMetaData applicationMetaData; protected MockApplicationPackage(String hosts, String services, List<String> searchDefinitions, String searchDefinitionDir, String deploymentSpec, String validationOverrides, boolean failOnValidateXml, @@ -52,6 +54,7 @@ public class MockApplicationPackage implements ApplicationPackage { this.failOnValidateXml = failOnValidateXml; queryProfileRegistry = new QueryProfileXMLReader().read(asNamedReaderList(queryProfileType), asNamedReaderList(queryProfile)); + applicationMetaData = new ApplicationMetaData("user", "dir", 0L, false, "application", "checksum", 0L, 0L); } @Override @@ -133,6 +136,8 @@ public class MockApplicationPackage implements ApplicationPackage { public QueryProfileRegistry getQueryProfiles() { return queryProfileRegistry; } + public ApplicationMetaData getMetaData() { return applicationMetaData; } + @Override public Reader getRankingExpression(String name) { File expressionFile = new File(searchDefinitionDir, name); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java index 7444224258e..9801eab9f2b 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java @@ -27,7 +27,6 @@ import java.util.TreeSet; /** * @author Einar M R Rosenvinge - * @since 5.1.11 */ public class ContainerDocumentApi implements FeederConfig.Producer { diff --git a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java index f113b10f508..177e332e86b 100644 --- a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java +++ b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java @@ -20,7 +20,6 @@ import com.yahoo.vespa.config.ConfigDefinition; import com.yahoo.vespa.config.ConfigDefinitionKey; import com.yahoo.vespa.model.VespaModel; import com.yahoo.vespa.model.search.SearchDefinition; -import org.json.JSONException; import org.junit.After; import org.junit.Rule; import org.junit.Test; @@ -290,7 +289,7 @@ public class ApplicationDeployTest { } @Test - public void testConfigDefinitionsFromJars() throws IOException { + public void testConfigDefinitionsFromJars() { String appName = "src/test/cfg//application/app1"; FilesApplicationPackage app = FilesApplicationPackage.fromFile(new File(appName), false); Map<ConfigDefinitionKey, UnparsedConfigDefinition> defs = app.getAllExistingConfigDefs(); @@ -298,11 +297,11 @@ public class ApplicationDeployTest { } @Test - public void testMetaData() throws IOException, JSONException { + public void testMetaData() throws IOException { File tmp = Files.createTempDir(); String appPkg = TESTDIR + "app1"; IOUtils.copyDirectory(new File(appPkg), tmp); - final DeployData deployData = new DeployData("foo", "bar", "baz", 13l, 1337l, 3l); + final DeployData deployData = new DeployData("foo", "bar", "baz", 13l, false, 1337l, 3l); FilesApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(tmp, deployData); app.writeMetaData(); FilesApplicationPackage newApp = FilesApplicationPackage.fromFileWithDeployData(tmp, deployData); diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java index 68985bd598a..f80c2699da2 100644 --- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java +++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServer.java @@ -314,7 +314,7 @@ public class ConfigProxyRpcServer implements Runnable, TargetWatcher, RpcServer public void returnOkResponse(JRTServerConfigRequest request, RawConfig config) { request.getRequestTrace().trace(TRACELEVEL, "Config proxy returnOkResponse()"); - request.addOkResponse(config.getPayload(), config.getGeneration(), config.getConfigMd5()); + request.addOkResponse(config.getPayload(), config.getGeneration(), config.isInternalRedeploy(), config.getConfigMd5()); log.log(LogLevel.DEBUG, () -> "Return response: " + request.getShortDescription() + ",configMd5=" + config.getConfigMd5() + ",generation=" + config.getGeneration()); log.log(LogLevel.SPAM, () -> "Config payload in response for " + request.getShortDescription() + ":" + config.getPayload()); diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigTester.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigTester.java index c16d6ccbcf5..33e798da15e 100644 --- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigTester.java +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigTester.java @@ -44,11 +44,11 @@ public class ConfigTester { String defMd5 = ConfigUtils.getDefMd5(defContent); String configMd5 = ConfigUtils.getMd5(fooConfigPayload); fooConfig = new RawConfig(configKey, defMd5, fooPayload, configMd5, - generation, defContent, Optional.empty()); + generation, false, defContent, Optional.empty()); String defName2 = "bar"; barConfig = new RawConfig(new ConfigKey<>(defName2, configId, namespace), defMd5, fooPayload, configMd5, - generation, defContent, Optional.empty()); + generation, false, defContent, Optional.empty()); } JRTServerConfigRequest createRequest(RawConfig config) { diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheTest.java index 2f2aac2a6bb..16bc2c66a94 100644 --- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheTest.java +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheTest.java @@ -63,9 +63,9 @@ public class MemoryCacheTest { slime.setString("bar \"value2\""); payloadDifferentMd5 = Payload.from(new ConfigPayload(slime)); - config = new RawConfig(configKey, defMd5, payload, configMd5, generation, defContent, Optional.empty()); - config2 = new RawConfig(configKey2, defMd52, payload2, configMd5, generation, defContent, Optional.empty()); - configDifferentMd5 = new RawConfig(configKey, differentDefMd5, payloadDifferentMd5, configMd5, generation, defContent, Optional.empty()); + config = new RawConfig(configKey, defMd5, payload, configMd5, generation, false, defContent, Optional.empty()); + config2 = new RawConfig(configKey2, defMd52, payload2, configMd5, generation, false, defContent, Optional.empty()); + configDifferentMd5 = new RawConfig(configKey, differentDefMd5, payloadDifferentMd5, configMd5, generation, false, defContent, Optional.empty()); cacheKey = new ConfigCacheKey(configKey, config.getDefMd5()); cacheKey2 = new ConfigCacheKey(configKey2, config2.getDefMd5()); diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java index 3cd0f1043cc..513a5caa08d 100644 --- a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java @@ -18,7 +18,6 @@ import static org.junit.Assert.*; /** * @author hmusum - * @since 5.1.9 */ public class ProxyServerTest { @@ -35,7 +34,7 @@ public class ProxyServerTest { private static final ConfigKey<?> errorConfigKey = new ConfigKey<>("error", fooConfig.getConfigId(), fooConfig.getNamespace()); static final RawConfig errorConfig = new RawConfig(errorConfigKey, fooConfig.getDefMd5(), fooConfig.getPayload(), fooConfig.getConfigMd5(), - fooConfig.getGeneration(), ErrorCode.UNKNOWN_DEFINITION, fooConfig.getDefContent(), Optional.empty()); + fooConfig.getGeneration(), false, ErrorCode.UNKNOWN_DEFINITION, fooConfig.getDefContent(), Optional.empty()); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -223,7 +222,8 @@ public class ProxyServerTest { private static RawConfig createConfigWithNextConfigGeneration(RawConfig config, int errorCode, Payload payload) { return new RawConfig(config.getKey(), config.getDefMd5(), payload, config.getConfigMd5(), - config.getGeneration() + 1, errorCode, config.getDefContent(), Optional.empty()); + config.getGeneration() + 1, false, + errorCode, config.getDefContent(), Optional.empty()); } } diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java b/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java index 18d13aac12c..cce3ce0a0c5 100644 --- a/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java +++ b/config/src/main/java/com/yahoo/config/subscription/ConfigInstanceUtil.java @@ -14,7 +14,6 @@ import com.yahoo.vespa.config.*; /** * @author gjoranv - * @since 5.1.6 */ public class ConfigInstanceUtil { @@ -34,8 +33,8 @@ public class ConfigInstanceUtil { setter.invoke(destination, source); setter.setAccessible(false); } catch (Exception e) { - throw new ConfigurationRuntimeException("Could not set values on config builder." - + destination.getClass().getName(), e); + throw new ConfigurationRuntimeException("Could not set values on config builder." + + destination.getClass().getName(), e); } } diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java b/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java index b0af69d2dbf..9e3a08eaf2c 100644 --- a/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java +++ b/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java @@ -31,8 +31,13 @@ public class ConfigSubscriber { private State state = State.OPEN; protected List<ConfigHandle<? extends ConfigInstance>> subscriptionHandles = new ArrayList<>(); private final ConfigSource source; + + /** The last complete config generation received by this */ private long generation = -1; + /** Whether the last generation received was due to a system-internal redeploy, not an application package change */ + private boolean internalRedeploy = false; + /** * Reuse requesters for equal source sets, limit number if many subscriptions. */ @@ -219,7 +224,7 @@ public class ConfigSubscriber { * @param timeoutInMillis timeout to wait in milliseconds * @param requireChange if set, at least one config have to change * @return true, if a new config generation has been found for all configs (additionally requires - * that at lest one of them has changed if <code>requireChange</code> is true), false otherwise + * that at lest one of them has changed if <code>requireChange</code> is true), false otherwise */ private boolean acquireSnapshot(long timeoutInMillis, boolean requireChange) { if (state == State.CLOSED) return false; @@ -234,20 +239,22 @@ public class ConfigSubscriber { h.setChanged(false); // Reset this flag, if it was set, the user should have acted on it the last time this method returned true. } boolean reconfigDue; + boolean internalRedeployOnly = true; do { // Keep on polling the subscriptions until we have a new generation across the board, or it times out for (ConfigHandle<? extends ConfigInstance> h : subscriptionHandles) { ConfigSubscription<? extends ConfigInstance> subscription = h.subscription(); - if (!subscription.nextConfig(timeLeftMillis)) { + if ( ! subscription.nextConfig(timeLeftMillis)) { // This subscriber has no new state and we know it has exhausted all time return false; } throwIfExceptionSet(subscription); ConfigSubscription.ConfigState<? extends ConfigInstance> config = subscription.getConfigState(); if (currentGen == null) currentGen = config.getGeneration(); - if (!currentGen.equals(config.getGeneration())) allGenerationsTheSame = false; + if ( ! currentGen.equals(config.getGeneration())) allGenerationsTheSame = false; allGenerationsChanged = allGenerationsChanged && config.isGenerationChanged(); if (config.isConfigChanged()) anyConfigChanged = true; + internalRedeployOnly = internalRedeployOnly && config.isInternalRedeploy(); timeLeftMillis = timeLeftMillis - (System.currentTimeMillis() - started); } reconfigDue = (anyConfigChanged || !requireChange) && allGenerationsChanged && allGenerationsTheSame; @@ -259,6 +266,7 @@ public class ConfigSubscriber { // This indicates the clients will possibly reconfigure their services, so "reset" changed-logic in subscriptions. // Also if appropriate update the changed flag on the handler, which clients use. markSubsChangedSeen(currentGen); + internalRedeploy = internalRedeployOnly; generation = currentGen; } return reconfigDue; @@ -422,6 +430,12 @@ public class ConfigSubscriber { } /** + * Whether the current config generation received by this was due to a system-internal redeploy, + * not an application package change + */ + public boolean isInternalRedeploy() { return internalRedeploy; } + + /** * Convenience interface for clients who only subscribe to one config. Implement this, and pass it to {@link ConfigSubscriber#subscribe(SingleSubscriber, Class, String)}. * * @author vegardh diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java index 02c455bf1c3..76241c560e4 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/ConfigSubscription.java @@ -23,9 +23,9 @@ import com.yahoo.vespa.config.protocol.DefContent; * Represents one active subscription to one config * * @author vegardh - * @since 5.1 */ public abstract class ConfigSubscription<T extends ConfigInstance> { + protected static Logger log = Logger.getLogger(ConfigSubscription.class.getName()); protected final ConfigSubscriber subscriber; private final AtomicReference<ConfigState<T>> config = new AtomicReference<>(); @@ -35,34 +35,49 @@ public abstract class ConfigSubscription<T extends ConfigInstance> { private State state = State.OPEN; public static class ConfigState<T extends ConfigInstance> { + private final boolean configChanged; private final boolean generationChanged; private final T config; private final Long generation; - private ConfigState(boolean generationChanged, Long generation, boolean configChanged, T config) { - this.configChanged = configChanged; - this.config = config; + private final boolean internalRedeploy; + + private ConfigState(boolean generationChanged, Long generation, boolean internalRedeploy, boolean configChanged, T config) { this.generationChanged = generationChanged; this.generation = generation; + this.internalRedeploy = internalRedeploy; + this.configChanged = configChanged; + this.config = config; } + private ConfigState(Long generation, T config) { - this(false, generation,false, config); + this(false, generation, false, false, config); } + private ConfigState() { - this(false, 0L, false, null); + this(false, 0L, false, false, null); } + private ConfigState<T> createUnchanged() { return new ConfigState<T>(generation, config); } public boolean isConfigChanged() { return configChanged; } public boolean isGenerationChanged() { return generationChanged; } public Long getGeneration() { return generation; } + + /** + * Returns whether this config generation was caused by a system-internal redeploy, + * not an application package change + */ + public boolean isInternalRedeploy() { return internalRedeploy; } + public T getConfig() { return config; } + } + /** * If non-null: The user has set this generation explicitly. nextConfig should take this into account. * Access to these variables _must_ be synchronized, as nextConfig and reload() is likely to be run from * independent threads. */ - private final AtomicReference<Long> reloadedGeneration = new AtomicReference<>(); enum State { @@ -168,30 +183,28 @@ public abstract class ConfigSubscription<T extends ConfigInstance> { } void setConfig(Long generation, T config) { - this.config.set(new ConfigState<>(true, generation, true, config)); + this.config.set(new ConfigState<>(true, generation, false, true, config)); } // Only used by {@link FileConfigSubscription} protected void setConfigIncGen(T config) { ConfigState<T> prev = this.config.get(); - setConfig(prev.getGeneration() + 1, config); + this.config.set(new ConfigState<>(true, prev.getGeneration() + 1, prev.isInternalRedeploy(), true, config)); } - // Only used by {@link FileConfigSubscription} and {@link ConfigSetSubscription} - protected void setConfigIfChangedIncGen(T config) { - ConfigState<T> prev = this.config.get(); - this.config.set(new ConfigState<>(true, prev.getGeneration() + 1, - !config.equals(prev.getConfig()), config)); - } protected void setConfigIfChanged(T config) { ConfigState<T> prev = this.config.get(); - this.config.set(new ConfigState<>(true, prev.getGeneration(), - !config.equals(prev.getConfig()), config)); + this.config.set(new ConfigState<>(true, prev.getGeneration(), prev.isInternalRedeploy(), !config.equals(prev.getConfig()), config)); } void setGeneration(Long generation) { - ConfigState<T> c = config.get(); - this.config.set(new ConfigState<>(true, generation, c.isConfigChanged(), c.getConfig())); + ConfigState<T> prev = config.get(); + this.config.set(new ConfigState<>(true, generation, prev.isInternalRedeploy(), prev.isConfigChanged(), prev.getConfig())); + } + + void setInternalRedeploy(boolean internalRedeploy) { + ConfigState<T> prev = config.get(); + this.config.set(new ConfigState<>(prev.isGenerationChanged(), prev.getGeneration(), prev.isConfigChanged(), internalRedeploy, prev.getConfig())); } /** diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/FileConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/FileConfigSubscription.java index 8c3f87d0702..bcee06cd667 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/FileConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/FileConfigSubscription.java @@ -17,9 +17,8 @@ import com.yahoo.log.LogLevel; /** * Subscription used when config id is file:... - * @author vegardh - * @since 5.1 * + * @author vegardh */ public class FileConfigSubscription<T extends ConfigInstance> extends ConfigSubscription<T> { diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java index 3eea87ab5ba..9ef7b602228 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java @@ -22,13 +22,14 @@ import com.yahoo.vespa.config.protocol.Payload; * A JRT config subscription uses one {@link JRTConfigRequester} to fetch config using Vespa RPC from a config source, typically proxy or server * * @author vegardh - * @since 5.1 */ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubscription<T> { + private JRTConfigRequester requester; private TimingValues timingValues; + // Last time we got an OK JRT callback for this - private long lastOK=0; + private long lastOK = 0; /** * The queue containing either nothing or the one (newest) request that has got callback from JRT, @@ -67,8 +68,11 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc } /** - * Polls the callback queue and <em>maybe</em> sets the following (caller must check): generation, generation changed, config, config changed - * Important: it never <em>resets</em> those flags, we must persist that state until the {@link ConfigSubscriber} clears it + * Polls the callback queue and <em>maybe</em> sets the following (caller must check): + * generation, generation changed, config, config changed + * Important: it never <em>resets</em> those flags, we must persist that state until the + * {@link ConfigSubscriber} clears it + * * @param timeoutMillis timeout when polling (returns after at most this time) * @return true if it got anything off the queue and <em>maybe</em> changed any state, false if timed out taking from queue */ @@ -85,6 +89,7 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc return false; } if (jrtReq.hasUpdatedGeneration()) { + setInternalRedeploy(jrtReq.isInternalRedeploy()); if (jrtReq.hasUpdatedConfig()) { setNewConfig(jrtReq); } else { diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java index 4870d69bb23..1b2daba32a4 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/JarConfigSubscription.java @@ -22,10 +22,9 @@ import com.yahoo.vespa.config.ConfigPayload; * * @author vegardh * @author gjoranv - * @since 5.1 - * */ public class JarConfigSubscription<T extends ConfigInstance> extends ConfigSubscription<T> { + private final String jarName; private final String path; private ZipEntry zipEntry = null; diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/MockConnection.java b/config/src/main/java/com/yahoo/config/subscription/impl/MockConnection.java index cb623437dba..9ad8f5c6ba2 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/MockConnection.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/MockConnection.java @@ -111,12 +111,14 @@ public class MockConnection implements ConnectionPool, com.yahoo.vespa.config.Co } static class OKResponseHandler extends AbstractResponseHandler { + protected void createResponse() { JRTServerConfigRequestV3 jrtReq = JRTServerConfigRequestV3.createFromRequest(request); Payload payload = Payload.from(ConfigPayload.empty()); long generation = 1; - jrtReq.addOkResponse(payload, generation, ConfigUtils.getMd5(payload.getData())); + jrtReq.addOkResponse(payload, generation, false, ConfigUtils.getMd5(payload.getData())); } + } public interface ResponseHandler extends Runnable { @@ -131,6 +133,7 @@ public class MockConnection implements ConnectionPool, com.yahoo.vespa.config.Co } public abstract static class AbstractResponseHandler implements ResponseHandler { + private RequestWaiter requestWaiter; protected Request request; diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/RawConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/RawConfigSubscription.java index 0d3f04361aa..60811dd38d1 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/RawConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/RawConfigSubscription.java @@ -16,10 +16,9 @@ import com.yahoo.vespa.config.ConfigPayload; * Config is the actual text given after the config id, with newlines * * @author vegardh - * @since 5.1 - * */ public class RawConfigSubscription<T extends ConfigInstance> extends ConfigSubscription<T> { + final String inputPayload; String payload; diff --git a/config/src/main/java/com/yahoo/vespa/config/RawConfig.java b/config/src/main/java/com/yahoo/vespa/config/RawConfig.java index 75c5161103e..3aaff1a6b57 100755 --- a/config/src/main/java/com/yahoo/vespa/config/RawConfig.java +++ b/config/src/main/java/com/yahoo/vespa/config/RawConfig.java @@ -31,6 +31,7 @@ public class RawConfig extends ConfigInstance { private final String configMd5; private final Optional<VespaVersion> vespaVersion; private long generation; + private boolean internalRedeploy; /** * Constructor for an empty config (not yet resolved). @@ -38,27 +39,29 @@ public class RawConfig extends ConfigInstance { * @param defMd5 The md5 sum of the .def-file. */ public RawConfig(ConfigKey<?> key, String defMd5) { - this(key, defMd5, null, "", 0L, 0, Collections.emptyList(), Optional.empty()); + this(key, defMd5, null, "", 0L, false, 0, Collections.emptyList(), Optional.empty()); } public RawConfig(ConfigKey<?> key, String defMd5, Payload payload, String configMd5, long generation, - List<String> defContent, Optional<VespaVersion> vespaVersion) { - this(key, defMd5, payload, configMd5, generation, 0, defContent, vespaVersion); + boolean internalRedeploy, List<String> defContent, Optional<VespaVersion> vespaVersion) { + this(key, defMd5, payload, configMd5, generation, internalRedeploy, 0, defContent, vespaVersion); } /** Copy constructor */ public RawConfig(RawConfig rawConfig) { this(rawConfig.key, rawConfig.defMd5, rawConfig.payload, rawConfig.configMd5, - rawConfig.generation, rawConfig.errorCode, rawConfig.defContent, rawConfig.getVespaVersion()); + rawConfig.generation, rawConfig.internalRedeploy, rawConfig.errorCode, + rawConfig.defContent, rawConfig.getVespaVersion()); } public RawConfig(ConfigKey<?> key, String defMd5, Payload payload, String configMd5, long generation, - int errorCode, List<String> defContent, Optional<VespaVersion> vespaVersion) { + boolean internalRedeploy, int errorCode, List<String> defContent, Optional<VespaVersion> vespaVersion) { this.key = key; this.defMd5 = ConfigUtils.getDefMd5FromRequest(defMd5, defContent); this.payload = payload; this.configMd5 = configMd5; this.generation = generation; + this.internalRedeploy = internalRedeploy; this.errorCode = errorCode; this.defContent = defContent; this.vespaVersion = vespaVersion; @@ -69,8 +72,15 @@ public class RawConfig extends ConfigInstance { * @param req a {@link JRTClientConfigRequest} */ public static RawConfig createFromResponseParameters(JRTClientConfigRequest req) { - return new RawConfig(req.getConfigKey(), req.getConfigKey().getMd5(), req.getNewPayload(), req.getNewConfigMd5(), - req.getNewGeneration(), 0, req.getDefContent().asList(), req.getVespaVersion()); + return new RawConfig(req.getConfigKey(), + req.getConfigKey().getMd5(), + req.getNewPayload(), + req.getNewConfigMd5(), + req.getNewGeneration(), + req.isInternalRedeploy(), + 0, + req.getDefContent().asList(), + req.getVespaVersion()); } /** @@ -78,58 +88,47 @@ public class RawConfig extends ConfigInstance { * @param req a {@link JRTClientConfigRequest} */ public static RawConfig createFromServerRequest(JRTServerConfigRequest req) { - return new RawConfig(req.getConfigKey(), req.getConfigKey().getMd5() , Payload.from(new Utf8String(""), CompressionInfo.uncompressed()), req.getRequestConfigMd5(), - req.getRequestGeneration(), 0, req.getDefContent().asList(), req.getVespaVersion()); + return new RawConfig(req.getConfigKey(), + req.getConfigKey().getMd5() , + Payload.from(new Utf8String(""), CompressionInfo.uncompressed()), + req.getRequestConfigMd5(), + req.getRequestGeneration(), + req.isInternalRedeploy(), + 0, + req.getDefContent().asList(), + req.getVespaVersion()); } - public ConfigKey<?> getKey() { - return key; - } + public ConfigKey<?> getKey() { return key; } - public String getName() { - return key.getName(); - } + public String getName() { return key.getName(); } - public String getNamespace() { - return key.getNamespace(); - } + public String getNamespace() { return key.getNamespace(); } - public String getConfigId() { - return key.getConfigId(); - } + public String getConfigId() { return key.getConfigId(); } - public String getConfigMd5() { - return configMd5; - } + public String getConfigMd5() { return configMd5; } - public String getDefMd5() { - return defMd5; - } + public String getDefMd5() { return defMd5; } - public long getGeneration() { - return generation; - } + public long getGeneration() { return generation; } - public void setGeneration(long generation) { - this.generation = generation; - } + public void setGeneration(long generation) { this.generation = generation; } - public Payload getPayload() { - return payload; - } + /** + * Returns whether this config generation was created by a system internal redeploy, not an + * application package change. + */ + public boolean isInternalRedeploy() { return internalRedeploy; } - public int errorCode() { - return errorCode; - } + public Payload getPayload() { return payload; } - public String getDefNamespace() { - return key.getNamespace(); - } + public int errorCode() { return errorCode; } - public Optional<VespaVersion> getVespaVersion() { - return vespaVersion; - } + public String getDefNamespace() { return key.getNamespace(); } + + public Optional<VespaVersion> getVespaVersion() { return vespaVersion; } /** * Returns true if this config is equal to the config (same payload md5) in the given request. diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/ConfigResponse.java b/config/src/main/java/com/yahoo/vespa/config/protocol/ConfigResponse.java index 5666417a50a..c07be8337fe 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/ConfigResponse.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/ConfigResponse.java @@ -13,8 +13,7 @@ import java.util.List; * of this must be thread safe, because a response may be cached and, the methods below should be callable * from multiple request handler threads. * - * @author lulf - * @since 5.1.14 + * @author Ulf Lilleengen */ public interface ConfigResponse { @@ -24,6 +23,8 @@ public interface ConfigResponse { long getGeneration(); + boolean isInternalRedeploy(); + String getConfigMd5(); void serialize(OutputStream os, CompressionType uncompressed) throws IOException; diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequest.java index 8e554dc45d8..97ba08a1c09 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequest.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequest.java @@ -4,12 +4,13 @@ package com.yahoo.vespa.config.protocol; /** * Interface for config requests used by clients. * - * @author lulf - * @since 5.3 + * @author Ulf Lilleengen */ public interface JRTClientConfigRequest extends JRTConfigRequest { + /** * Validate config response given by the server. If none is given, or an error occurred, this should return false. + * * @return true if valid response, false if not. */ boolean validateResponse(); @@ -17,19 +18,22 @@ public interface JRTClientConfigRequest extends JRTConfigRequest { /** * Test whether ot not the returned config has an updated generation. This should return false if no response have * been given. + * * @return true if generation is updated, false if not. */ boolean hasUpdatedGeneration(); /** * Return the payload in the response given by the server. The payload will be empty if no response was given. + * * @return the config payload. */ Payload getNewPayload(); /** - * Create a new {@link JRTClientConfigRequest} based on this request based on the same request parameters, but having - * the timeout changed. + * Create a new {@link JRTClientConfigRequest} based on this request based on the same request parameters, + * but having the timeout changed. + * * @param timeout server timeout of the new request. * @return a new {@link JRTClientConfigRequest} instance. */ @@ -37,44 +41,57 @@ public interface JRTClientConfigRequest extends JRTConfigRequest { /** * Test whether or not the returned request is an error. + * * @return true if error, false if not. */ boolean isError(); /** * Get the generation of the newly provided config. If none has been given, 0 should be returned. + * * @return the new generation. */ long getNewGeneration(); + /** Returns whether this config changes is due to an internal change not an application package change */ + boolean isInternalRedeploy(); + /** * Get the config md5 of the config returned by the server. Return an empty string if no response has been returned. + * * @return a config md5. */ String getNewConfigMd5(); /** - * Test whether or not the payload is contained in this response or not. Should return false for error responses as well. + * Test whether or not the payload is contained in this response or not. + * Should return false for error responses as well. + * * @return true if empty, false if not. */ boolean containsPayload(); /** - * Test whether or not the response contains an updated config or not. False if no response has been returned. + * Test whether or not the response contains an updated config or not. + * False if no response has been returned. + * * @return true if config is updated, false if not. */ boolean hasUpdatedConfig(); /** - * Get the {@link Trace} given in the response by the server. The {@link Trace} can be used to add further tracing - * and later printed to provide useful debug info. + * Get the {@link Trace} given in the response by the server. + * The {@link Trace} can be used to add further tracing and later printed to provide useful debug info. + * * @return a {@link Trace}. */ Trace getResponseTrace(); /** * Get config definition content. + * * @return def as lines. */ DefContent getDefContent(); + } diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequestV3.java b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequestV3.java index 3d7c0dc1e80..1c842a4d1b0 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequestV3.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTClientConfigRequestV3.java @@ -19,8 +19,7 @@ import java.util.Optional; * * See {@link JRTServerConfigRequestV3} for protocol details. * - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ public class JRTClientConfigRequestV3 extends SlimeClientConfigRequest { @@ -71,7 +70,10 @@ public class JRTClientConfigRequestV3 extends SlimeClientConfigRequest { requestData.getVespaVersion()); } - public static <T extends ConfigInstance> JRTClientConfigRequest createFromSub(JRTConfigSubscription<T> sub, Trace trace, CompressionType compressionType, Optional<VespaVersion> vespaVersion) { + public static <T extends ConfigInstance> JRTClientConfigRequest createFromSub(JRTConfigSubscription<T> sub, + Trace trace, + CompressionType compressionType, + Optional<VespaVersion> vespaVersion) { String hostname = ConfigUtils.getCanonicalHostName(); ConfigKey<T> key = sub.getKey(); ConfigSubscription.ConfigState<T> configState = sub.getConfigState(); @@ -88,7 +90,11 @@ public class JRTClientConfigRequestV3 extends SlimeClientConfigRequest { } - public static JRTClientConfigRequest createFromRaw(RawConfig config, long serverTimeout, Trace trace, CompressionType compressionType, Optional<VespaVersion> vespaVersion) { + public static JRTClientConfigRequest createFromRaw(RawConfig config, + long serverTimeout, + Trace trace, + CompressionType compressionType, + Optional<VespaVersion> vespaVersion) { String hostname = ConfigUtils.getCanonicalHostName(); return createWithParams(config.getKey(), DefContent.fromList(config.getDefContent()), diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequest.java index 54607ec4502..763f672a513 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequest.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequest.java @@ -6,13 +6,14 @@ import com.yahoo.vespa.config.GetConfigRequest; /** * Interface for config requests at the server end point. * - * @author lulf - * @since 5.3 + * @author Ulf Lilleengen */ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigRequest { + /** * Notify this request that its delayed due to no new config being available at this point. The value * provided in this function should be returned when calling {@link #isDelayedResponse()}. + * * @param delayedResponse true if response is delayed, false if not. */ void setDelayedResponse(boolean delayedResponse); @@ -20,6 +21,7 @@ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigReque /** * Signal error when handling this request. The error should be reflected in the request state and propagated * back to the client. + * * @param errorCode error code, as described in {@link com.yahoo.vespa.config.ErrorCode}. * @param message message to display for this error, typically printed by client. */ @@ -27,33 +29,46 @@ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigReque /** * Signal that the request was handled and provide return values typically needed by a client. + * * @param payload The config payload that the client should receive. * @param generation The config generation of the given payload. + * @param internalRedeployment whether this payload was generated from an internal redeployment not an + * application package change * @param configMd5 The md5sum of the given payload. */ - void addOkResponse(Payload payload, long generation, String configMd5); + void addOkResponse(Payload payload, long generation, boolean internalRedeployment, String configMd5); /** * Get the current config md5 of the client config. + * * @return a config md5. */ String getRequestConfigMd5(); /** * Get the current config generation of the client config. + * * @return the current config generation. */ long getRequestGeneration(); /** * Check whether or not this request is delayed. + * * @return true if delayed, false if not. */ boolean isDelayedResponse(); /** + * Returns whether the response config was created by a system internal redeploy, not an application + * package change + */ + boolean isInternalRedeploy(); + + /** * Get the request trace for this request. The trace can be used to trace config execution to provide useful * debug info in production environments. + * * @return a {@link Trace} instance. */ Trace getRequestTrace(); @@ -65,4 +80,5 @@ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigReque * @return A {@link Payload} that satisfies this request format. */ Payload payloadFromResponse(ConfigResponse response); + } diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequestV3.java b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequestV3.java index b0096444820..f70ebf39a28 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequestV3.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/JRTServerConfigRequestV3.java @@ -20,17 +20,21 @@ import java.io.IOException; * The implementation of addOkResponse is optimized for doing as little copying of payload data as possible, ensuring * that we get a lower memory footprint. * - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ +// TODO: Merge with parent public class JRTServerConfigRequestV3 extends SlimeServerConfigRequest { + /** Response field */ + private boolean internalRedeploy = false; + protected JRTServerConfigRequestV3(Request request) { super(request); } @Override - public void addOkResponse(Payload payload, long generation, String configMd5) { + public void addOkResponse(Payload payload, long generation, boolean internalRedeploy, String configMd5) { + this.internalRedeploy = internalRedeploy; boolean changedConfig = !configMd5.equals(getRequestConfigMd5()); boolean changedConfigAndNewGeneration = changedConfig && ConfigUtils.isGenerationNewer(generation, getRequestGeneration()); Payload responsePayload = payload.withCompression(getCompressionType()); @@ -41,6 +45,7 @@ public class JRTServerConfigRequestV3 extends SlimeServerConfigRequest { addCommonReturnValues(jsonGenerator); setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_CONFIG_MD5, configMd5); setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_CONFIG_GENERATION, generation); + setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_INTERNAL_REDEPLOY, internalRedeploy); jsonGenerator.writeObjectFieldStart(SlimeResponseData.RESPONSE_COMPRESSION_INFO); if (responsePayload == null) { throw new RuntimeException("Payload is null for ' " + this + ", not able to create response"); @@ -73,7 +78,11 @@ public class JRTServerConfigRequestV3 extends SlimeServerConfigRequest { return 3; } + @Override + public boolean isInternalRedeploy() { return internalRedeploy; } + public static JRTServerConfigRequestV3 createFromRequest(Request req) { return new JRTServerConfigRequestV3(req); } + } diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeClientConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeClientConfigRequest.java index 19323fb1a85..055f48bbcf3 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeClientConfigRequest.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeClientConfigRequest.java @@ -16,8 +16,7 @@ import java.util.logging.Logger; * Base class for new generation of config requests based on {@link Slime}. Allows for some customization of * payload encoding and decoding, as well as adding extra request/response fields. * - * @author lulf - * @since 5.18 + * @author Ulf Lilleengen */ public abstract class SlimeClientConfigRequest implements JRTClientConfigRequest { @@ -82,6 +81,7 @@ public abstract class SlimeClientConfigRequest implements JRTClientConfigRequest .append(",").append(getVespaVersion()).append("'\n"); sb.append("response='").append(getNewConfigMd5()) .append(",").append(getNewGeneration()) + .append(",").append(isInternalRedeploy()) .append("'\n"); return sb.toString(); } @@ -203,6 +203,11 @@ public abstract class SlimeClientConfigRequest implements JRTClientConfigRequest } @Override + public boolean isInternalRedeploy() { + return responseData.getResponseInternalRedeployment(); + } + + @Override public long getRequestGeneration() { return requestData.getRequestGeneration(); } diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java index e7aae3251d0..9e05b5b2d86 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeConfigResponse.java @@ -15,8 +15,7 @@ import java.util.List; /** * Class for serializing config responses based on {@link com.yahoo.slime.Slime} implementing the {@link ConfigResponse} interface. * - * @author lulf - * @since 5.1 + * @author Ulf Lilleengen */ public class SlimeConfigResponse implements ConfigResponse { @@ -24,17 +23,26 @@ public class SlimeConfigResponse implements ConfigResponse { private final CompressionInfo compressionInfo; private final InnerCNode targetDef; private final long generation; + private final boolean internalRedeployment; private final String configMd5; public static SlimeConfigResponse fromConfigPayload(ConfigPayload payload, InnerCNode targetDef, long generation, String configMd5) { Utf8Array data = payload.toUtf8Array(true); - return new SlimeConfigResponse(data, targetDef, generation, configMd5, CompressionInfo.create(CompressionType.UNCOMPRESSED, data.getByteLength())); + return new SlimeConfigResponse(data, targetDef, generation, false, + configMd5, + CompressionInfo.create(CompressionType.UNCOMPRESSED, data.getByteLength())); } - public SlimeConfigResponse(Utf8Array payload, InnerCNode targetDef, long generation, String configMd5, CompressionInfo compressionInfo) { + public SlimeConfigResponse(Utf8Array payload, + InnerCNode targetDef, + long generation, + boolean internalRedeployment, + String configMd5, + CompressionInfo compressionInfo) { this.payload = payload; this.targetDef = targetDef; this.generation = generation; + this.internalRedeployment = internalRedeployment; this.configMd5 = configMd5; this.compressionInfo = compressionInfo; } @@ -62,6 +70,13 @@ public class SlimeConfigResponse implements ConfigResponse { return generation; } + /** + * Returns whether this application instance was produced by an internal redeployment, + * not an application package change + */ + @Override + public boolean isInternalRedeploy() { return internalRedeployment; } + @Override public String getConfigMd5() { return configMd5; diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java index 04f6df7a45b..6add29074d1 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeResponseData.java @@ -11,10 +11,10 @@ import com.yahoo.text.Utf8; * Contains response data for a slime response and methods for decoding the response data that * are common to all {@link Slime} based config requests. * - * @author lulf - * @since 5.18 + * @author Ulf Lilleengen */ class SlimeResponseData { + static final String RESPONSE_VERSION = "version"; static final String RESPONSE_DEF_NAME = "defName"; static final String RESPONSE_DEF_NAMESPACE = "defNamespace"; @@ -24,6 +24,7 @@ class SlimeResponseData { static final String RESPONSE_TRACE = "trace"; static final String RESPONSE_CONFIG_MD5 = "configMD5"; static final String RESPONSE_CONFIG_GENERATION = "generation"; + static final String RESPONSE_INTERNAL_REDEPLOY = "internalRedeploy"; static final String RESPONSE_COMPRESSION_INFO = "compressionInfo"; private final Request request; @@ -66,4 +67,10 @@ class SlimeResponseData { CompressionInfo getCompressionInfo() { return CompressionInfo.fromSlime(getResponseField(RESPONSE_COMPRESSION_INFO)); } + + boolean getResponseInternalRedeployment() { + Inspector inspector = getResponseField(RESPONSE_INTERNAL_REDEPLOY); + return inspector.valid() ? inspector.asBool() : false; + } + } diff --git a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeServerConfigRequest.java b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeServerConfigRequest.java index 662eacc4eea..41bf38ef1dc 100644 --- a/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeServerConfigRequest.java +++ b/config/src/main/java/com/yahoo/vespa/config/protocol/SlimeServerConfigRequest.java @@ -19,8 +19,7 @@ import java.util.logging.Logger; * payload encoding and decoding, as well as adding extra request/response fields. Used by both V2 and V3 * config protocol. * - * @author lulf - * @since 5.18 + * @author Ulf Lilleengen */ abstract class SlimeServerConfigRequest implements JRTServerConfigRequest { @@ -164,6 +163,10 @@ abstract class SlimeServerConfigRequest implements JRTServerConfigRequest { jsonGenerator.writeNumberField(fieldName, value); } + protected static void setResponseField(JsonGenerator jsonGenerator, String fieldName, boolean value) throws IOException { + jsonGenerator.writeBooleanField(fieldName, value); + } + @Override public long getRequestGeneration() { return requestData.getRequestGeneration(); diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java index 8acab56d838..d17a2ff61f4 100644 --- a/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java @@ -85,7 +85,7 @@ public class ConfigSetSubscriptionTest { assertEquals(hA1.getConfig().times(), 89); assertEquals(hS.getConfig().stringVal(), "StringVal"); - //Reconfigure all configs, generation should change + // Reconfigure all configs, generation should change a0builder.message("A new message, 0").times(880); a1builder.message("A new message, 1").times(890); barBuilder.stringVal("new StringVal"); diff --git a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java index 33560ff666d..b19da2c1689 100644 --- a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java +++ b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java @@ -60,14 +60,14 @@ public class RawConfigTest { assertThat(config.hashCode(), is(not(new RawConfig(key, "a").hashCode()))); // different def md5 // different generation - config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); - RawConfig config2 = new RawConfig(key, defMd5, payload, configMd5, 2L, defContent, Optional.empty()); + config = new RawConfig(key, defMd5, payload, configMd5, generation, false, defContent, Optional.empty()); + RawConfig config2 = new RawConfig(key, defMd5, payload, configMd5, 2L, false, defContent, Optional.empty()); assertThat(config, is(not(config2))); assertThat(config.hashCode(), is(not(config2.hashCode()))); // different config md5 and with vespa version final VespaVersion vespaVersion = VespaVersion.fromString("5.37.38"); - RawConfig config3 = new RawConfig(key, defMd5, payload, "9999", generation, defContent, Optional.of(vespaVersion)); + RawConfig config3 = new RawConfig(key, defMd5, payload, "9999", generation, false, defContent, Optional.of(vespaVersion)); assertThat(config, is(not(config3))); assertThat(config.hashCode(), is(not(config3.hashCode()))); // Check that vespa version is set correctly @@ -81,19 +81,19 @@ public class RawConfigTest { assertFalse(config.equals(key)); // errors - RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, 1, defContent, Optional.empty()); + RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 1, defContent, Optional.empty()); assertThat(errorConfig1, is(errorConfig1)); assertThat(config, is(not(errorConfig1))); assertThat(config.hashCode(), is(not(errorConfig1.hashCode()))); assertThat(errorConfig1, is(errorConfig1)); - RawConfig errorConfig2 = new RawConfig(key, defMd5, payload, configMd5, generation, 2, defContent, Optional.empty()); + RawConfig errorConfig2 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 2, defContent, Optional.empty()); assertThat(errorConfig1, is(not(errorConfig2))); assertThat(errorConfig1.hashCode(), is(not(errorConfig2.hashCode()))); } @Test public void payload() { - RawConfig config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); + RawConfig config = new RawConfig(key, defMd5, payload, configMd5, generation, false, defContent, Optional.empty()); assertThat(config.getPayload(), is(payload)); assertThat(config.getConfigMd5(), is(configMd5)); assertThat(config.getGeneration(), is(generation)); @@ -104,19 +104,19 @@ public class RawConfigTest { public void require_correct_defmd5() { final String defMd5ForEmptyDefContent = "d41d8cd98f00b204e9800998ecf8427e"; - RawConfig config = new RawConfig(key, null, payload, configMd5, generation, defContent, Optional.empty()); + RawConfig config = new RawConfig(key, null, payload, configMd5, generation, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, "", payload, configMd5, generation, defContent, Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); + config = new RawConfig(key, defMd5, payload, configMd5, generation, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, null, payload, configMd5, generation, null, Optional.empty()); + config = new RawConfig(key, null, payload, configMd5, generation, false, null, Optional.empty()); assertNull(config.getDefMd5()); - config = new RawConfig(key, null, payload, configMd5, generation, Arrays.asList(""), Optional.empty()); + config = new RawConfig(key, null, payload, configMd5, generation, false, Arrays.asList(""), Optional.empty()); assertThat(config.getDefMd5(), is(defMd5ForEmptyDefContent)); - config = new RawConfig(key, "", payload, configMd5, generation, null, Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, null, Optional.empty()); assertThat(config.getDefMd5(), is("")); - config = new RawConfig(key, "", payload, configMd5, generation, Arrays.asList(""), Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, Arrays.asList(""), Optional.empty()); assertThat(config.getDefMd5(), is(defMd5ForEmptyDefContent)); } diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java index 9a7b216d6b0..7daac671b34 100644 --- a/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java @@ -20,8 +20,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; /** - * @author lulf - * @since 5.1 + * @author Ulf Lilleengen */ public class ConfigResponseTest { @@ -45,13 +44,12 @@ public class ConfigResponseTest { @Test public void require_that_slime_response_decompresses_on_serialize() throws IOException { - ConfigPayload configPayload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); DefParser dParser = new DefParser(SimpletypesConfig.getDefName(), new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))); InnerCNode targetDef = dParser.getTree(); Utf8Array data = configPayload.toUtf8Array(true); Utf8Array bytes = new Utf8Array(new LZ4PayloadCompressor().compress(data.getBytes())); - ConfigResponse response = new SlimeConfigResponse(bytes, targetDef, 3, "mymd5", CompressionInfo.create(CompressionType.LZ4, data.getByteLength())); + ConfigResponse response = new SlimeConfigResponse(bytes, targetDef, 3, false, "mymd5", CompressionInfo.create(CompressionType.LZ4, data.getByteLength())); List<String> payload = response.getLegacyPayload(); assertNotNull(payload); assertThat(payload.size(), is(6)); @@ -63,4 +61,5 @@ public class ConfigResponseTest { response.serialize(baos, CompressionType.UNCOMPRESSED); assertThat(baos.toString(), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}")); } + } diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java index 1f5274b832b..cdaaf2061f4 100644 --- a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java @@ -125,7 +125,7 @@ public abstract class JRTConfigRequestBase { @Test public void next_request_when_error_is_correct() { - serverReq.addOkResponse(createPayload(), 999999, "newmd5"); + serverReq.addOkResponse(createPayload(), 999999, false, "newmd5"); serverReq.addErrorResponse(ErrorCode.OUTDATED_CONFIG, "error message"); System.out.println(serverReq); JRTClientConfigRequest next = clientReq.nextRequest(6); @@ -141,7 +141,7 @@ public abstract class JRTConfigRequestBase { Payload payload = createPayload("vale"); String md5 = ConfigUtils.getMd5(payload.getData()); long generation = 4L; - serverReq.addOkResponse(payload, generation, md5); + serverReq.addOkResponse(payload, generation, false, md5); assertTrue(clientReq.validateResponse()); assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is(payload.getData().toString())); assertThat(clientReq.getNewGeneration(), is(4L)); @@ -168,7 +168,7 @@ public abstract class JRTConfigRequestBase { @Test public void generation_only_is_updated() { Payload payload = createPayload(); - serverReq.addOkResponse(payload, 4L, ConfigUtils.getMd5(payload.getData())); + serverReq.addOkResponse(payload, 4L, false, ConfigUtils.getMd5(payload.getData())); boolean value = clientReq.validateResponse(); assertTrue(clientReq.errorMessage(), value); assertFalse(clientReq.hasUpdatedConfig()); @@ -188,7 +188,7 @@ public abstract class JRTConfigRequestBase { @Test public void nothing_is_updated() { Payload payload = createPayload(); - serverReq.addOkResponse(payload, currentGeneration, configMd5); + serverReq.addOkResponse(payload, currentGeneration, false, configMd5); assertTrue(clientReq.validateResponse()); assertFalse(clientReq.hasUpdatedConfig()); assertFalse(clientReq.hasUpdatedGeneration()); @@ -199,7 +199,7 @@ public abstract class JRTConfigRequestBase { Payload payload = Payload.from(ConfigPayload.empty()); clientReq = createReq(payload); serverReq = createReq(clientReq.getRequest()); - serverReq.addOkResponse(payload, currentGeneration, ConfigUtils.getMd5(payload.getData())); + serverReq.addOkResponse(payload, currentGeneration, false, ConfigUtils.getMd5(payload.getData())); boolean val = clientReq.validateResponse(); assertTrue(clientReq.errorMessage(), val); assertFalse(clientReq.hasUpdatedConfig()); @@ -238,7 +238,7 @@ public abstract class JRTConfigRequestBase { @Override protected void createResponse() { JRTServerConfigRequest serverRequest = createReq(request); - serverRequest.addOkResponse(createPayload(), currentGeneration, configMd5); + serverRequest.addOkResponse(createPayload(), currentGeneration, false, configMd5); } }); diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java index ccb68da5f85..896ee116301 100644 --- a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java @@ -71,7 +71,7 @@ public class JRTConfigRequestV3Test extends JRTConfigRequestBase { public void emptypayload() { ConfigPayload payload = ConfigPayload.empty(); SlimeConfigResponse response = SlimeConfigResponse.fromConfigPayload(payload, null, 0, ConfigUtils.getMd5(payload)); - serverReq.addOkResponse(serverReq.payloadFromResponse(response), response.getGeneration(), response.getConfigMd5()); + serverReq.addOkResponse(serverReq.payloadFromResponse(response), response.getGeneration(), false, response.getConfigMd5()); assertTrue(clientReq.validateResponse()); assertTrue(clientReq.hasUpdatedGeneration()); assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is("{}")); 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 102a36e833f..64bc9868034 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 @@ -177,6 +177,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye /** * Creates a new deployment from the active application, if available. + * This is used for system internal redeployments, not on application package changes. * * @param application the active application to be redeployed * @return a new deployment from the local active, or empty if a local active application @@ -189,6 +190,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye /** * Creates a new deployment from the active application, if available. + * This is used for system internal redeployments, not on application package changes. * * @param application the active application to be redeployed * @param timeout the timeout to use for each individual deployment operation @@ -203,7 +205,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye LocalSession activeSession = getActiveSession(tenant, application); if (activeSession == null) return Optional.empty(); TimeoutBudget timeoutBudget = new TimeoutBudget(clock, timeout); - LocalSession newSession = tenant.getSessionFactory().createSessionFromExisting(activeSession, logger, timeoutBudget); + LocalSession newSession = tenant.getSessionFactory().createSessionFromExisting(activeSession, logger, false, timeoutBudget); tenant.getLocalSessionRepo().addSession(newSession); // Keep manually deployed tenant applications on the latest version, don't change version otherwise @@ -345,12 +347,15 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye throw new IllegalStateException("Session not prepared: " + sessionId); } - public long createSessionFromExisting(ApplicationId applicationId, DeployLogger logger, TimeoutBudget timeoutBudget) { + public long createSessionFromExisting(ApplicationId applicationId, + DeployLogger logger, + boolean internalRedeploy, + TimeoutBudget timeoutBudget) { Tenant tenant = tenantRepository.getTenant(applicationId.tenant()); LocalSessionRepo localSessionRepo = tenant.getLocalSessionRepo(); SessionFactory sessionFactory = tenant.getSessionFactory(); LocalSession fromSession = getExistingSession(tenant, applicationId); - LocalSession session = sessionFactory.createSessionFromExisting(fromSession, logger, timeoutBudget); + LocalSession session = sessionFactory.createSessionFromExisting(fromSession, logger, internalRedeploy, timeoutBudget); localSessionRepo.addSession(session); return session.getSessionId(); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java b/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java index ea14b9194e8..6c9b7216e59 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java @@ -9,8 +9,7 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository; /** * Contains the context for serving getconfig requests so that this information does not have to be looked up multiple times. * - * @author lulf - * @since 5.8 + * @author Ulf Lilleengen */ public class GetConfigContext { 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 8e865f96db3..f723ca2fe91 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 @@ -51,7 +51,7 @@ public class SuperModelController { ConfigKey<?> configKey = request.getConfigKey(); InnerCNode targetDef = getConfigDefinition(request.getConfigKey(), request.getDefContent()); ConfigPayload payload = model.getConfig(configKey); - return responseFactory.createResponse(payload, targetDef, generation); + return responseFactory.createResponse(payload, targetDef, generation, false); } private InnerCNode getConfigDefinition(ConfigKey<?> configKey, DefContent defContent) { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java index a73fc95eb05..64123420622 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java @@ -32,23 +32,26 @@ import java.util.Set; * a Vespa application, i.e. generation, model and zookeeper data, as well as methods for resolving config * and other queries against the model. * - * @author Harald Musum + * @author hmusum */ public class Application implements ModelResult { private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(Application.class.getName()); private final long appGeneration; // The generation of the set of configs belonging to an application + private final boolean internalRedeploy; private final Version vespaVersion; private final Model model; private final ServerCache cache; private final MetricUpdater metricUpdater; private final ApplicationId app; - public Application(Model model, ServerCache cache, long appGeneration, Version vespaVersion, MetricUpdater metricUpdater, ApplicationId app) { + public Application(Model model, ServerCache cache, long appGeneration, boolean internalRedeploy, + Version vespaVersion, MetricUpdater metricUpdater, ApplicationId app) { Objects.requireNonNull(model, "The model cannot be null"); this.model = model; this.cache = cache; this.appGeneration = appGeneration; + this.internalRedeploy = internalRedeploy; this.vespaVersion = vespaVersion; this.metricUpdater = metricUpdater; this.app = app; @@ -106,7 +109,7 @@ public class Application implements ModelResult { debug("Resolving config " + cacheKey); } - if (!req.noCache()) { + if ( ! req.noCache()) { ConfigResponse config = cache.get(cacheKey); if (config != null) { if (logDebug()) { @@ -131,9 +134,9 @@ public class Application implements ModelResult { throw new ConfigurationRuntimeException("Unable to resolve config " + configKey); } - ConfigResponse configResponse = responseFactory.createResponse(payload, def.getCNode(), appGeneration); + ConfigResponse configResponse = responseFactory.createResponse(payload, def.getCNode(), appGeneration, internalRedeploy); metricUpdater.incrementProcTime(System.currentTimeMillis() - start); - if (!req.noCache()) { + if ( ! req.noCache()) { cache.put(cacheKey, configResponse, configResponse.getConfigMd5()); metricUpdater.setCacheConfigElems(cache.configElems()); metricUpdater.setCacheChecksumElems(cache.checkSumElems()); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java index 74c85829ef2..26ef79ebe02 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java @@ -58,7 +58,7 @@ public class SessionCreateHandler extends SessionHandler { long sessionId; if (request.hasProperty("from")) { ApplicationId applicationId = getFromApplicationId(request); - sessionId = applicationRepository.createSessionFromExisting(applicationId, logger, timeoutBudget); + sessionId = applicationRepository.createSessionFromExisting(applicationId, logger, false, timeoutBudget); } else { validateDataAndHeader(request); String name = getNameProperty(request, logger); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java index 9d583d27341..d83548dcc3d 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java @@ -88,7 +88,9 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { new com.yahoo.component.Version(modelFactory.getVersion().toString()), wantedNodeVespaVersion); MetricUpdater applicationMetricUpdater = metrics.getOrCreateMetricUpdater(Metrics.createDimensions(applicationId)); - return new Application(modelFactory.createModel(modelContext), cache, appGeneration, modelFactory.getVersion(), + return new Application(modelFactory.createModel(modelContext), cache, appGeneration, + applicationPackage.getMetaData().isInternalRedeploy(), + modelFactory.getVersion(), applicationMetricUpdater, applicationId); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactory.java index 07e07fa2595..247ae388639 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactory.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactory.java @@ -9,17 +9,19 @@ import com.yahoo.vespa.config.protocol.ConfigResponse; * Represents a component that creates config responses from a payload. Different implementations * can do transformations of the payload such as compression. * - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ public interface ConfigResponseFactory { /** * Create a {@link ConfigResponse} for a given payload and generation. + * * @param payload The {@link com.yahoo.vespa.config.ConfigPayload} to put in the response. * @param defFile The {@link com.yahoo.config.codegen.InnerCNode} def file for this config. * @param generation The payload generation. @return A {@link ConfigResponse} that can be sent to the client. + * @param internalRedeployment whether this config generation was produced by an internal redeployment, + * not a change to the application package */ - ConfigResponse createResponse(ConfigPayload payload, InnerCNode defFile, long generation); + ConfigResponse createResponse(ConfigPayload payload, InnerCNode defFile, long generation, boolean internalRedeployment); } 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 b37d013cc6b..d93537d539a 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 @@ -22,7 +22,6 @@ import java.util.logging.Logger; /** * @author hmusum -* @since 5.1 */ class GetConfigProcessor implements Runnable { @@ -30,8 +29,9 @@ class GetConfigProcessor implements Runnable { private static final String localHostName = HostName.getLocalhost(); private final JRTServerConfigRequest request; + /* True only when this request has expired its server timeout and we need to respond to the client */ - private boolean forceResponse = false; + private final boolean forceResponse; private final RpcServer rpcServer; private String logPre = ""; @@ -64,7 +64,7 @@ class GetConfigProcessor implements Runnable { // TODO: Increment statistics (Metrics) failed counters when requests fail public void run() { //Request has already been detached - if (!request.validateParameters()) { + if ( ! request.validateParameters()) { // Error code is set in verifyParameters if parameters are not OK. log.log(LogLevel.WARNING, "Parameters for request " + request + " did not validate: " + request.errorCode() + " : " + request.errorMessage()); respond(request); @@ -121,7 +121,7 @@ class GetConfigProcessor implements Runnable { // config == null is not an error, but indicates that the config will be returned later. if ((config != null) && (!config.hasEqualConfig(request) || config.hasNewerGeneration(request) || forceResponse)) { // debugLog(trace, "config response before encoding:" + config.toString()); - request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.getConfigMd5()); + request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.isInternalRedeploy(), config.getConfigMd5()); if (logDebug(trace)) { debugLog(trace, "return response: " + request.getShortDescription()); } @@ -147,7 +147,7 @@ class GetConfigProcessor implements Runnable { ConfigPayload emptyPayload = ConfigPayload.empty(); String configMd5 = ConfigUtils.getMd5(emptyPayload); ConfigResponse config = SlimeConfigResponse.fromConfigPayload(emptyPayload, null, 0, configMd5); - request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.getConfigMd5()); + request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), false, config.getConfigMd5()); respond(request); } @@ -161,4 +161,5 @@ class GetConfigProcessor implements Runnable { trace.trace(RpcServer.TRACELEVEL_DEBUG, logPre + message); } } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/LZ4ConfigResponseFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/LZ4ConfigResponseFactory.java index 609f1d5f79f..ff15eb7bfa7 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/LZ4ConfigResponseFactory.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/LZ4ConfigResponseFactory.java @@ -14,19 +14,22 @@ import com.yahoo.vespa.config.util.ConfigUtils; /** * Compressor that compresses config payloads to lz4. * - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ public class LZ4ConfigResponseFactory implements ConfigResponseFactory { private static LZ4PayloadCompressor compressor = new LZ4PayloadCompressor(); @Override - public ConfigResponse createResponse(ConfigPayload payload, InnerCNode defFile, long generation) { + public ConfigResponse createResponse(ConfigPayload payload, + InnerCNode defFile, + long generation, + boolean internalRedeployment) { Utf8Array rawPayload = payload.toUtf8Array(true); String configMd5 = ConfigUtils.getMd5(rawPayload); CompressionInfo info = CompressionInfo.create(CompressionType.LZ4, rawPayload.getByteLength()); Utf8Array compressed = new Utf8Array(compressor.compress(rawPayload.getBytes())); - return new SlimeConfigResponse(compressed, defFile, generation, configMd5, info); + return new SlimeConfigResponse(compressed, defFile, generation, internalRedeployment, configMd5, info); } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/UncompressedConfigResponseFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/UncompressedConfigResponseFactory.java index ac3cfa2fda1..995e981e0f3 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/UncompressedConfigResponseFactory.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/UncompressedConfigResponseFactory.java @@ -13,17 +13,19 @@ import com.yahoo.vespa.config.util.ConfigUtils; /** * Simply returns an uncompressed payload. * - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ public class UncompressedConfigResponseFactory implements ConfigResponseFactory { @Override - public ConfigResponse createResponse(ConfigPayload payload, InnerCNode defFile, long generation) { + public ConfigResponse createResponse(ConfigPayload payload, + InnerCNode defFile, + long generation, + boolean internalRedeployment) { Utf8Array rawPayload = payload.toUtf8Array(true); String configMd5 = ConfigUtils.getMd5(rawPayload); CompressionInfo info = CompressionInfo.create(CompressionType.UNCOMPRESSED, rawPayload.getByteLength()); - return new SlimeConfigResponse(rawPayload, defFile, generation, configMd5, info); + return new SlimeConfigResponse(rawPayload, defFile, generation, internalRedeployment, configMd5, info); } } 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 653df81f296..3978a1f25f8 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 @@ -9,8 +9,7 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository; * class represents the common stuff between sessions working on the local file * system ({@link LocalSession}s) and sessions working on zookeeper {@link RemoteSession}s. * - * @author lulf - * @since 5.1 + * @author Ulf Lilleengen */ public abstract class Session { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java index bd9da36a2ba..a3dea83d50c 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java @@ -10,7 +10,7 @@ import java.io.File; /** * A session factory responsible for creating deploy sessions. * - * @author lulf + * @author Ulf Lilleengen */ public interface SessionFactory { @@ -31,9 +31,11 @@ public interface SessionFactory { * * @param existingSession The session to use as base * @param logger a deploy logger where the deploy log will be written. + * @param internalRedeploy if this session is for a system internal redeploy not an application package change * @param timeoutBudget Timeout for creating session and waiting for other servers. * @return a new session */ - LocalSession createSessionFromExisting(LocalSession existingSession, DeployLogger logger, TimeoutBudget timeoutBudget); + LocalSession createSessionFromExisting(LocalSession existingSession, DeployLogger logger, + boolean internalRedeploy, TimeoutBudget timeoutBudget); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java index 7285ff905ff..10590a26690 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java @@ -74,9 +74,12 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader { this.clock = globalComponentRegistry.getClock(); } + /** Create a session for a true application package change */ @Override - public LocalSession createSession(File applicationFile, ApplicationId applicationId, TimeoutBudget timeoutBudget) { - return create(applicationFile, applicationId, nonExistingActiveSession, timeoutBudget); + public LocalSession createSession(File applicationFile, + ApplicationId applicationId, + TimeoutBudget timeoutBudget) { + return create(applicationFile, applicationId, nonExistingActiveSession, false, timeoutBudget); } private void ensureZKPathDoesNotExist(Path sessionPath) { @@ -89,13 +92,14 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader { File configApplicationDir, String applicationName, long sessionId, - long currentlyActiveSession) { + long currentlyActiveSession, + boolean internalRedeploy) { long deployTimestamp = System.currentTimeMillis(); String user = System.getenv("USER"); if (user == null) { user = "unknown"; } - DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationName, deployTimestamp, sessionId, currentlyActiveSession); + DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationName, deployTimestamp, internalRedeploy, sessionId, currentlyActiveSession); return FilesApplicationPackage.fromFileWithDeployData(configApplicationDir, deployData); } @@ -119,19 +123,21 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader { @Override public LocalSession createSessionFromExisting(LocalSession existingSession, DeployLogger logger, + boolean internalRedeploy, TimeoutBudget timeoutBudget) { File existingApp = getSessionAppDir(existingSession.getSessionId()); ApplicationId existingApplicationId = existingSession.getApplicationId(); long liveApp = getLiveApp(existingApplicationId); logger.log(LogLevel.DEBUG, "Create from existing application id " + existingApplicationId + ", live app for it is " + liveApp); - LocalSession session = create(existingApp, existingApplicationId, liveApp, timeoutBudget); + LocalSession session = create(existingApp, existingApplicationId, liveApp, internalRedeploy, timeoutBudget); session.setApplicationId(existingApplicationId); session.setVespaVersion(existingSession.getVespaVersion()); return session; } - private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSession, TimeoutBudget timeoutBudget) { + private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSession, + boolean internalRedeploy, TimeoutBudget timeoutBudget) { long sessionId = sessionCounter.nextSessionId(); Path sessionIdPath = sessionsPath.append(String.valueOf(sessionId)); log.log(LogLevel.DEBUG, TenantRepository.logPre(tenant) + "Next session id is " + sessionId + " , sessionIdPath=" + sessionIdPath.getAbsolute()); @@ -145,8 +151,12 @@ public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader { nodeFlavors); File userApplicationDir = tenantFileSystemDirs.getUserApplicationDir(sessionId); IOUtils.copyDirectory(applicationFile, userApplicationDir); - ApplicationPackage applicationPackage = createApplication(applicationFile, userApplicationDir, - applicationId.application().value(), sessionId, currentlyActiveSession); + ApplicationPackage applicationPackage = createApplication(applicationFile, + userApplicationDir, + applicationId.application().value(), + sessionId, + currentlyActiveSession, + internalRedeploy); applicationPackage.writeMetaData(); return createSessionFromApplication(applicationPackage, sessionId, sessionZooKeeperClient, timeoutBudget, clock); } catch (Exception e) { 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 bc07ac7d79c..2b72db63d55 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 @@ -122,12 +122,11 @@ public class SuperModelRequestHandlerTest { } private static class TestApplication extends Application { - private long version = 0; public TestApplication(VespaModel vespaModel, ServerCache cache, long appGeneration, ApplicationId app, long version) { - super(vespaModel, cache, appGeneration, Version.fromIntValues(1, 2, 3), MetricUpdater.createTestUpdater(), app); - this.version = version; + super(vespaModel, cache, appGeneration, false, Version.fromIntValues(1, 2, 3), MetricUpdater.createTestUpdater(), app); } + } public static NodeFlavors emptyNodeFlavors() { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationConvergenceCheckerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationConvergenceCheckerTest.java index 1d692e8f78f..3c3edae7914 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationConvergenceCheckerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationConvergenceCheckerTest.java @@ -28,7 +28,7 @@ import static org.junit.Assert.assertThat; import static com.yahoo.vespa.config.server.application.ApplicationConvergenceChecker.ServiceResponse; /** - * @author lulf + * @author Ulf Lilleengen */ public class ApplicationConvergenceCheckerTest { @@ -43,7 +43,12 @@ public class ApplicationConvergenceCheckerTest { @Before public void setup() { Model mockModel = MockModel.createContainer("localhost", 1337); - application = new Application(mockModel, new ServerCache(), 3, Version.fromIntValues(0, 0, 0), MetricUpdater.createTestUpdater(), appId); + application = new Application(mockModel, + new ServerCache(), + 3, + false, + Version.fromIntValues(0, 0, 0), + MetricUpdater.createTestUpdater(), appId); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java index a83f2676a4d..233c0e99ed8 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationMapperTest.java @@ -31,9 +31,9 @@ public class ApplicationMapperTest { vespaVersions.add(Version.fromString("1.2.3")); vespaVersions.add(Version.fromString("1.2.4")); vespaVersions.add(Version.fromString("1.2.5")); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java index 98bedb76599..94bb81021dc 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationSetTest.java @@ -29,9 +29,9 @@ public class ApplicationSetTest { vespaVersions.add(Version.fromString("1.2.3")); vespaVersions.add(Version.fromString("1.2.4")); vespaVersions.add(Version.fromString("1.2.5")); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); - applications.add(new Application(new ModelStub(), null, 0, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(0), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(1), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); + applications.add(new Application(new ModelStub(), null, 0, false, vespaVersions.get(2), MetricUpdater.createTestUpdater(), ApplicationId.defaultId())); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java index 02cf6303ba8..90a27c39736 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ApplicationTest.java @@ -42,8 +42,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** - * @author lulf - * @since 5.1.14 + * @author Ulf Lilleengen */ public class ApplicationTest { @@ -53,7 +52,7 @@ public class ApplicationTest { ApplicationName.from("foobar"), InstanceName.defaultName()); ServerCache cache = new ServerCache(); Version vespaVersion = Version.fromIntValues(1, 2, 3); - Application app = new Application(new ModelStub(), cache, 1337, vespaVersion, MetricUpdater.createTestUpdater(), appId); + Application app = new Application(new ModelStub(), cache, 1337L, false, vespaVersion, MetricUpdater.createTestUpdater(), appId); assertThat(app.getApplicationGeneration(), is(1337l)); assertNotNull(app.getModel()); assertThat(app.getCache(), is(cache)); @@ -71,24 +70,24 @@ public class ApplicationTest { File testApp = new File("src/test/apps/app"); ServerCache cache = createCacheAndAddContent(); VespaModel model = new VespaModel(FilesApplicationPackage.fromFile(testApp)); - final ApplicationId applicationId = new ApplicationId.Builder().tenant("foo").applicationName("foo").build(); - handler = new Application(model, cache, 1, Version.fromIntValues(1, 2, 3), + ApplicationId applicationId = new ApplicationId.Builder().tenant("foo").applicationName("foo").build(); + handler = new Application(model, cache, 1L, false, Version.fromIntValues(1, 2, 3), new MetricUpdater(Metrics.createTestMetrics(), Metrics.createDimensions(applicationId)), applicationId); } private static ServerCache createCacheAndAddContent() { ServerCache cache = new ServerCache(); - final ConfigDefinitionKey key = new ConfigDefinitionKey(SimpletypesConfig.CONFIG_DEF_NAME, SimpletypesConfig.CONFIG_DEF_NAMESPACE); + ConfigDefinitionKey key = new ConfigDefinitionKey(SimpletypesConfig.CONFIG_DEF_NAME, SimpletypesConfig.CONFIG_DEF_NAMESPACE); com.yahoo.vespa.config.buildergen.ConfigDefinition def = getDef(key, SimpletypesConfig.CONFIG_DEF_SCHEMA); // TODO Why do we have to use empty def md5 here? cache.addDef(key, def); - final ConfigDefinitionKey key2 = new ConfigDefinitionKey(SlobroksConfig.CONFIG_DEF_NAME, SlobroksConfig.CONFIG_DEF_NAMESPACE); + ConfigDefinitionKey key2 = new ConfigDefinitionKey(SlobroksConfig.CONFIG_DEF_NAME, SlobroksConfig.CONFIG_DEF_NAMESPACE); com.yahoo.vespa.config.buildergen.ConfigDefinition def2 = getDef(key2, SlobroksConfig.CONFIG_DEF_SCHEMA); cache.addDef(key2, def2); - final ConfigDefinitionKey key3 = new ConfigDefinitionKey(LogdConfig.CONFIG_DEF_NAME, LogdConfig.CONFIG_DEF_NAMESPACE); + ConfigDefinitionKey key3 = new ConfigDefinitionKey(LogdConfig.CONFIG_DEF_NAME, LogdConfig.CONFIG_DEF_NAMESPACE); com.yahoo.vespa.config.buildergen.ConfigDefinition def3 = getDef(key3, LogdConfig.CONFIG_DEF_SCHEMA); cache.addDef(key3, def3); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java index 76204d5c5f2..c2ddec7e795 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/FileDistributionStatusTest.java @@ -161,7 +161,13 @@ public class FileDistributionStatusTest { private Application createApplication(List<String> hostnames) { Model mockModel = MockModel.createConfigProxies(hostnames, 1337); - return new Application(mockModel, new ServerCache(), 3, Version.fromIntValues(0, 0, 0), MetricUpdater.createTestUpdater(), appId); + return new Application(mockModel, + new ServerCache(), + 3, + false, + Version.fromIntValues(0, 0, 0), + MetricUpdater.createTestUpdater(), + appId); } HttpResponse getStatus(FileDistributionStatus fileDistributionStatus, Application application) { 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 bf7f7038c1a..8074dd35267 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 @@ -43,7 +43,14 @@ public class ZooKeeperClientTest extends TestWithCurator { public void setupZK() throws IOException { this.zk = ConfigCurator.create(curator); ZooKeeperClient zkc = new ZooKeeperClient(zk, new BaseDeployLogger(), true, Path.fromString(appPath)); - ApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(new File("src/test/apps/zkfeed"), new DeployData("foo", "/bar/baz", "appName", 1345l, 3l, 2l)); + ApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(new File("src/test/apps/zkfeed"), + new DeployData("foo", + "/bar/baz", + "appName", + 1345L, + false, + 3L, + 2L)); Map<Version, FileRegistry> fileRegistries = createFileRegistries(); app.writeMetaData(); zkc.setupZooKeeper(); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java index 930ae98c9e9..eb5dc7a2abf 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java @@ -191,7 +191,8 @@ public class SessionHandlerTest { public String applicationName; @Override - public LocalSession createSession(File applicationDirectory, ApplicationId applicationId, TimeoutBudget timeoutBudget) { + public LocalSession createSession(File applicationDirectory, ApplicationId applicationId, + TimeoutBudget timeoutBudget) { createCalled = true; this.applicationName = applicationId.application().value(); if (doThrow) { @@ -208,7 +209,8 @@ public class SessionHandlerTest { } @Override - public LocalSession createSessionFromExisting(LocalSession existingSession, DeployLogger logger, TimeoutBudget timeoutBudget) { + public LocalSession createSessionFromExisting(LocalSession existingSession, DeployLogger logger, + boolean internalRedeploy, TimeoutBudget timeoutBudget) { if (doThrow) { throw new RuntimeException("foo"); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java index e14e59b9fe7..2a05688c435 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java @@ -280,7 +280,13 @@ public class SessionActiveHandlerTest extends SessionHandlerTest { ActivateRequest(long sessionId, long previousSessionId, Session.Status initialStatus, String subPath, Clock clock) { this.sessionId = sessionId; this.initialStatus = initialStatus; - this.deployData = new DeployData("foo", "bar", appName, 0l, sessionId, previousSessionId); + this.deployData = new DeployData("foo", + "bar", + appName, + 0l, + false, + sessionId, + previousSessionId); this.subPath = subPath; this.clock = clock; } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactoryTest.java index 9193c2409c7..7c1d5fa8dbc 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/ConfigResponseFactoryTest.java @@ -13,38 +13,38 @@ import org.junit.Test; import java.io.StringReader; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertEquals; /** - * @author lulf - * @since 5.19 + * @author Ulf Lilleengen */ public class ConfigResponseFactoryTest { - private InnerCNode def; + private InnerCNode def; @Before public void setup() { - DefParser dParser = new DefParser(SimpletypesConfig.getDefName(), new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))); + DefParser dParser = new DefParser(SimpletypesConfig.getDefName(), + new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))); def = dParser.getTree(); } @Test public void testUncompressedFacory() { UncompressedConfigResponseFactory responseFactory = new UncompressedConfigResponseFactory(); - ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), def, 3); - assertThat(response.getCompressionInfo().getCompressionType(), is(CompressionType.UNCOMPRESSED)); - assertThat(response.getGeneration(), is(3l)); - assertThat(response.getPayload().getByteLength(), is(2)); + ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), def, 3, false); + assertEquals(CompressionType.UNCOMPRESSED, response.getCompressionInfo().getCompressionType()); + assertEquals(3L,response.getGeneration()); + assertEquals(2, response.getPayload().getByteLength()); } @Test public void testLZ4CompressedFacory() { LZ4ConfigResponseFactory responseFactory = new LZ4ConfigResponseFactory(); - ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), def, 3); - assertThat(response.getCompressionInfo().getCompressionType(), is(CompressionType.LZ4)); - assertThat(response.getGeneration(), is(3l)); - assertThat(response.getPayload().getByteLength(), is(3)); + ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), def, 3, false); + assertEquals(CompressionType.LZ4, response.getCompressionInfo().getCompressionType()); + assertEquals(3L, response.getGeneration()); + assertEquals(3, response.getPayload().getByteLength()); } + } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java index 578224833a1..50c39b74fa2 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java @@ -104,6 +104,9 @@ public class GetConfigProcessorTest { } @Override + public boolean isInternalRedeploy() { return false; } + + @Override public String getConfigMd5() { return "mymd5"; } 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 ef742ae3d38..6bb566963c0 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 @@ -73,7 +73,13 @@ public class RpcServerTest extends TestWithRpc { private void testEnabled() throws IOException, SAXException { generationCounter.increment(); - Application app = new Application(new VespaModel(MockApplicationPackage.createEmpty()), new ServerCache(), 2l, Version.fromIntValues(1, 2, 3), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()); + Application app = new Application(new VespaModel(MockApplicationPackage.createEmpty()), + new ServerCache(), + 2L, + false, + Version.fromIntValues(1, 2, 3), + MetricUpdater.createTestUpdater(), + ApplicationId.defaultId()); ApplicationSet appSet = ApplicationSet.fromSingle(app); rpcServer.configActivated(TenantName.defaultName(), appSet); ConfigKey<?> key = new ConfigKey<>(LbServicesConfig.class, "*"); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java index 531a2e3745b..0ca487cfb67 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionFactoryTest.java @@ -31,8 +31,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** - * @author lulf - * @since 5.1 + * @author Ulf Lilleengen */ public class SessionFactoryTest extends TestWithTenant { private SessionFactory factory; @@ -64,10 +63,13 @@ public class SessionFactoryTest extends TestWithTenant { public void require_that_session_can_be_created_from_existing() throws IOException { LocalSession session = getLocalSession(); assertNotNull(session); - assertThat(session.getSessionId(), is(2l)); - LocalSession session2 = factory.createSessionFromExisting(session, new BaseDeployLogger(), TimeoutBudgetTest.day()); + assertThat(session.getSessionId(), is(2L)); + LocalSession session2 = factory.createSessionFromExisting(session, + new BaseDeployLogger(), + false, + TimeoutBudgetTest.day()); assertNotNull(session2); - assertThat(session2.getSessionId(), is(3l)); + assertThat(session2.getSessionId(), is(3L)); } @Test(expected = RuntimeException.class) 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 9a4b0b05186..f47ed69ad14 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 @@ -65,8 +65,13 @@ public class TenantRepositoryTest extends TestWithCurator { @Test public void testListenersAdded() throws IOException, SAXException { tenantRepository.getTenant(tenant1).getReloadHandler().reloadConfig(ApplicationSet.fromSingle( - new Application(new VespaModel(MockApplicationPackage.createEmpty()), new ServerCache(), 4l, - Version.fromIntValues(1, 2, 3), MetricUpdater.createTestUpdater(), ApplicationId.defaultId()))); + new Application(new VespaModel(MockApplicationPackage.createEmpty()), + new ServerCache(), + 4L, + false, + Version.fromIntValues(1, 2, 3), + MetricUpdater.createTestUpdater(), + ApplicationId.defaultId()))); assertThat(listener.reloaded.get(), is(1)); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java index ef320f0f084..27ae0945b4a 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java @@ -54,8 +54,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; /** - * @author lulf - * @since 5.1 + * @author Ulf Lilleengen */ public class TenantRequestHandlerTest extends TestWithCurator { @@ -263,7 +262,8 @@ public class TenantRequestHandlerTest extends TestWithCurator { public void testHasApplication() throws IOException, SAXException { assertdefaultAppNotFound(); server.reloadConfig(reloadConfig(1l, Clock.systemUTC())); - assertTrue(server.hasApplication(new ApplicationId.Builder().applicationName(ApplicationName.defaultName()).tenant(tenant).build(), Optional.of(vespaVersion))); + assertTrue(server.hasApplication(new ApplicationId.Builder().applicationName(ApplicationName.defaultName()).tenant(tenant).build(), + Optional.of(vespaVersion))); } private void assertdefaultAppNotFound() { @@ -286,18 +286,17 @@ public class TenantRequestHandlerTest extends TestWithCurator { @Test public void testListConfigs() throws IOException, SAXException { assertdefaultAppNotFound(); - /*assertTrue(server.allConfigIds(ApplicationId.defaultId()).isEmpty()); - assertTrue(server.allConfigsProduced(ApplicationId.defaultId()).isEmpty()); - assertTrue(server.listConfigs(ApplicationId.defaultId(), false).isEmpty()); - assertTrue(server.listConfigs(ApplicationId.defaultId(), true).isEmpty());*/ VespaModel model = new VespaModel(FilesApplicationPackage.fromFile(new File("src/test/apps/app"))); - server.reloadConfig(ApplicationSet.fromSingle(new Application(model, new ServerCache(), 1, vespaVersion, MetricUpdater.createTestUpdater(), ApplicationId.defaultId()))); + server.reloadConfig(ApplicationSet.fromSingle(new Application(model, + new ServerCache(), + 1, + false, + vespaVersion, + MetricUpdater.createTestUpdater(), + ApplicationId.defaultId()))); Set<ConfigKey<?>> configNames = server.listConfigs(ApplicationId.defaultId(), Optional.of(vespaVersion), false); assertTrue(configNames.contains(new ConfigKey<>("sentinel", "hosts", "cloud.config"))); - //for (ConfigKey<?> ck : configNames) { - // assertTrue(!"".equals(ck.getConfigId())); - //} configNames = server.listConfigs(ApplicationId.defaultId(), Optional.of(vespaVersion), true); System.out.println(configNames); diff --git a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java index 41e3e43f156..0aefc83301d 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java @@ -86,7 +86,7 @@ public class HandlersConfigurerDi { container = new Container(subscriberFactory, configId, deconstructor, osgiWrapper); try { - getNewConfigGraph(discInjector, false); + getNewComponentGraph(discInjector, false); } catch (InterruptedException e) { throw new RuntimeException("Interrupted while setting up handlers for the first time."); } @@ -143,13 +143,10 @@ public class HandlersConfigurerDi { * * @return true if this resulted in a new graph that should be applied to the currently running container */ - public boolean getNewConfigGraph(Injector discInjector, boolean restartOnRedeploy) throws InterruptedException { - ComponentGraph newGraph = container.getNewConfigGraph(currentGraph, createFallbackInjector(vespaContainer, discInjector), restartOnRedeploy); - if (newGraph == currentGraph) return false; - currentGraph = newGraph; + public void getNewComponentGraph(Injector discInjector, boolean restartOnRedeploy) throws InterruptedException { + currentGraph = container.getNewComponentGraph(currentGraph, createFallbackInjector(vespaContainer, discInjector), restartOnRedeploy); assert (currentGraph.getInstance(RegistriesHack.class) != null); // TODO: Remove, seems quite pointless? - return true; } @SuppressWarnings("deprecation") diff --git a/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java index 008ca5c1d5a..afbf163500f 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java @@ -112,7 +112,7 @@ public class HandlersConfigurerTestWrapper { public void reloadConfig() { configurer.reloadConfig(++lastGeneration); try { - configurer.getNewConfigGraph(Guice.createInjector(), false); + configurer.getNewComponentGraph(Guice.createInjector(), false); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/container-di/src/main/java/com/yahoo/container/di/config/Subscriber.java b/container-di/src/main/java/com/yahoo/container/di/config/Subscriber.java index 9fd30f888b9..0feab7779ad 100644 --- a/container-di/src/main/java/com/yahoo/container/di/config/Subscriber.java +++ b/container-di/src/main/java/com/yahoo/container/di/config/Subscriber.java @@ -14,6 +14,7 @@ public interface Subscriber { long waitNextGeneration(); long generation(); + boolean internalRedeploy(); boolean configChanged(); Map<ConfigKey<ConfigInstance>, ConfigInstance> config(); diff --git a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala b/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala index afef3e96821..e38ff9d4491 100644 --- a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala +++ b/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala @@ -50,9 +50,12 @@ object CloudSubscriberFactory { private val handles: Map[ConfigKeyT, ConfigHandle[_ <: ConfigInstance]] = keys.map(subscribe).toMap - //if waitNextGeneration has not yet been called, -1 should be returned + // if waitNextGeneration has not yet been called, -1 should be returned var generation: Long = -1 + // True if this reconfiguration was caused by a system-internal redeploy, not an external application change + var internalRedeploy: Boolean = false + private def subscribe(key: ConfigKeyT) = (key, subscriber.subscribe(key.getConfigClass, key.getConfigId)) override def configChanged = handles.values.exists(_.isChanged) @@ -86,6 +89,7 @@ object CloudSubscriberFactory { } generation = subscriber.getGeneration + internalRedeploy = subscriber.isInternalRedeploy generation } diff --git a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala b/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala index dc94d789f7b..0d829b0456d 100644 --- a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala +++ b/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala @@ -29,25 +29,27 @@ final class ConfigRetriever(bootstrapKeys: Set[ConfigKeyT], @tailrec - final def getConfigs(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long): ConfigSnapshot = { + final def getConfigs(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long, restartOnRedeploy: Boolean = false): ConfigSnapshot = { require(componentConfigKeys intersect bootstrapKeys isEmpty) log.log(DEBUG, "getConfigs: " + componentConfigKeys) setupComponentSubscriber(componentConfigKeys ++ bootstrapKeys) - getConfigsOptional(leastGeneration) match { + getConfigsOptional(leastGeneration, restartOnRedeploy) match { case Some(snapshot) => resetComponentSubscriberIfBootstrap(snapshot); snapshot - case None => getConfigs(componentConfigKeys, leastGeneration) + case None => getConfigs(componentConfigKeys, leastGeneration, restartOnRedeploy) } } - private def getConfigsOptional(leastGeneration: Long): Option[ConfigSnapshot] = { + private def getConfigsOptional(leastGeneration: Long, restartOnRedeploy: Boolean): Option[ConfigSnapshot] = { val newestComponentGeneration = componentSubscriber.waitNextGeneration() log.log(DEBUG, s"getConfigsOptional: new component generation: $newestComponentGeneration") // leastGeneration is only used to ensure newer generation when the previous generation was invalidated due to an exception if (newestComponentGeneration < leastGeneration) { None + } else if (restartOnRedeploy && ! componentSubscriber.internalRedeploy()) { // Don't reconfig - wait for restart + None } else if (bootstrapSubscriber.generation < newestComponentGeneration) { val newestBootstrapGeneration = bootstrapSubscriber.waitNextGeneration() log.log(DEBUG, s"getConfigsOptional: new bootstrap generation: ${bootstrapSubscriber.generation}") diff --git a/container-di/src/main/scala/com/yahoo/container/di/Container.scala b/container-di/src/main/scala/com/yahoo/container/di/Container.scala index 0ef01e5aa3d..2a185d41a6c 100644 --- a/container-di/src/main/scala/com/yahoo/container/di/Container.scala +++ b/container-di/src/main/scala/com/yahoo/container/di/Container.scala @@ -44,9 +44,9 @@ class Container( var leastGeneration = -1L @throws(classOf[InterruptedException]) - def getNewConfigGraph(oldGraph: ComponentGraph = new ComponentGraph, - fallbackInjector: GuiceInjector = Guice.createInjector(), - restartOnRedeploy: Boolean = false): ComponentGraph = { + def getNewComponentGraph(oldGraph: ComponentGraph = new ComponentGraph, + fallbackInjector: GuiceInjector = Guice.createInjector(), + restartOnRedeploy: Boolean = false): ComponentGraph = { def deconstructObsoleteComponents(oldGraph: ComponentGraph, newGraph: ComponentGraph) { val oldComponents = new IdentityHashMap[AnyRef, AnyRef]() @@ -56,8 +56,7 @@ class Container( } try { - val newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector) - if (restartOnRedeploy) return oldGraph // wait for restart to cause reconfig instead + val newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, restartOnRedeploy) newGraph.reuseNodes(oldGraph) constructComponents(newGraph) deconstructObsoleteComponents(oldGraph, newGraph) @@ -115,9 +114,10 @@ class Container( } final def getConfigAndCreateGraph(graph: ComponentGraph = new ComponentGraph, - fallbackInjector: Injector): ComponentGraph = { + fallbackInjector: Injector, + restartOnRedeploy: Boolean): ComponentGraph = { - val snapshot = configurer.getConfigs(graph.configKeys, leastGeneration) + val snapshot = configurer.getConfigs(graph.configKeys, leastGeneration, restartOnRedeploy) log.log(DEBUG, """createNewGraph: @@ -141,8 +141,7 @@ class Container( .format(getBootstrapGeneration, getComponentsGeneration, previousConfigGeneration).stripMargin) installBundles(configs) getConfigAndCreateGraph( - createComponentsGraph(configs, getBootstrapGeneration,fallbackInjector), - fallbackInjector) + createComponentsGraph(configs, getBootstrapGeneration,fallbackInjector), fallbackInjector, restartOnRedeploy) case ComponentsConfigs(configs) => log.log(DEBUG, """Got components configs, diff --git a/container-di/src/test/java/demo/ContainerTestBase.java b/container-di/src/test/java/demo/ContainerTestBase.java index d413a61759b..9c2415c3514 100644 --- a/container-di/src/test/java/demo/ContainerTestBase.java +++ b/container-di/src/test/java/demo/ContainerTestBase.java @@ -59,7 +59,7 @@ public class ContainerTestBase extends ContainerTest { throw new UnsupportedOperationException("getBundle not supported."); } }); - componentGraph = container.getNewConfigGraph(componentGraph, Guice.createInjector(), false); + componentGraph = container.getNewComponentGraph(componentGraph, Guice.createInjector(), false); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala b/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala index 83645a8b35c..9f07acc7dc9 100644 --- a/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala +++ b/container-di/src/test/scala/com/yahoo/container/di/ContainerTest.scala @@ -44,7 +44,7 @@ class ContainerTest { val container = newContainer(dirConfigSource) - val component = createComponentTakingConfig(container.getNewConfigGraph()) + val component = createComponentTakingConfig(container.getNewComponentGraph()) assertThat(component.config.stringVal(), is("myString")) container.shutdownConfigurer() @@ -57,7 +57,7 @@ class ContainerTest { val container = newContainer(dirConfigSource) - val componentGraph = container.getNewConfigGraph() + val componentGraph = container.getNewComponentGraph() val component = createComponentTakingConfig(componentGraph) assertThat(component.config.stringVal(), is("original")) @@ -66,7 +66,7 @@ class ContainerTest { dirConfigSource.writeConfig("test", """stringVal "reconfigured" """) container.reloadConfig(2) - val newComponentGraph = container.getNewConfigGraph(componentGraph) + val newComponentGraph = container.getNewComponentGraph(componentGraph) val component2 = createComponentTakingConfig(newComponentGraph) assertThat(component2.config.stringVal(), is("reconfigured")) @@ -80,7 +80,7 @@ class ContainerTest { val container = newContainer(dirConfigSource) - val graph = container.getNewConfigGraph() + val graph = container.getNewComponentGraph() val component = createComponentTakingConfig(graph) assertThat(component.getId.toString, is("id1")) @@ -89,7 +89,7 @@ class ContainerTest { ("id2", classOf[ComponentTakingConfig]))) container.reloadConfig(2) - val newGraph = container.getNewConfigGraph(graph) + val newGraph = container.getNewComponentGraph(graph) assertThat(ComponentGraph.getNode(newGraph, "id1"), notNullValue(classOf[Node])) assertThat(ComponentGraph.getNode(newGraph, "id2"), notNullValue(classOf[Node])) @@ -107,12 +107,12 @@ class ContainerTest { val container = newContainer(dirConfigSource) - val oldGraph = container.getNewConfigGraph() + val oldGraph = container.getNewComponentGraph() val componentToDestruct = oldGraph.getInstance(classOf[DestructableComponent]) writeBootstrapConfigs("id2", classOf[DestructableComponent]) container.reloadConfig(2) - container.getNewConfigGraph(oldGraph) + container.getNewComponentGraph(oldGraph) assertTrue(componentToDestruct.deconstructed) } @@ -123,7 +123,7 @@ class ContainerTest { val container = newContainer(dirConfigSource) var currentGraph: ComponentGraph = null try { - currentGraph = container.getNewConfigGraph() + currentGraph = container.getNewComponentGraph() fail("Expected to log and die.") } catch { case _: Throwable => fail("Expected to log and die") @@ -136,14 +136,14 @@ class ContainerTest { writeBootstrapConfigs(Array(simpleComponentEntry)) val container = newContainer(dirConfigSource) - var currentGraph = container.getNewConfigGraph() + var currentGraph = container.getNewComponentGraph() val simpleComponent = currentGraph.getInstance(classOf[SimpleComponent]) writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionInConstructor]) container.reloadConfig(2) try { - currentGraph = container.getNewConfigGraph(currentGraph) + currentGraph = container.getNewComponentGraph(currentGraph) fail("Expected exception") } catch { case _: ComponentConstructorException => // Expected, do nothing @@ -156,7 +156,7 @@ class ContainerTest { dirConfigSource.writeConfig("test", """stringVal "myString" """) writeBootstrapConfigs(Array(simpleComponentEntry, componentTakingConfigEntry)) container.reloadConfig(3) - currentGraph = container.getNewConfigGraph(currentGraph) + currentGraph = container.getNewComponentGraph(currentGraph) assertEquals(3, currentGraph.generation) assertSame(simpleComponent, currentGraph.getInstance(classOf[SimpleComponent])) @@ -169,7 +169,7 @@ class ContainerTest { writeBootstrapConfigs(Array(simpleComponentEntry)) val container = newContainer(dirConfigSource) - var currentGraph = container.getNewConfigGraph() + var currentGraph = container.getNewComponentGraph() val simpleComponent = currentGraph.getInstance(classOf[SimpleComponent]) @@ -177,7 +177,7 @@ class ContainerTest { dirConfigSource.writeConfig("test", """stringVal "myString" """) container.reloadConfig(2) try { - currentGraph = container.getNewConfigGraph(currentGraph) + currentGraph = container.getNewComponentGraph(currentGraph) fail("Expected exception") } catch { case _: IllegalArgumentException => // Expected, do nothing @@ -192,20 +192,20 @@ class ContainerTest { writeBootstrapConfigs("myId", classOf[ComponentTakingConfig]) val container = newContainer(dirConfigSource) - var currentGraph = container.getNewConfigGraph() + var currentGraph = container.getNewComponentGraph() writeBootstrapConfigs("thrower", classOf[ComponentThrowingExceptionForMissingConfig]) container.reloadConfig(2) try { - currentGraph = container.getNewConfigGraph(currentGraph) + currentGraph = container.getNewComponentGraph(currentGraph) fail("expected exception") } catch { case e: Exception => } val newGraph = Future { - currentGraph = container.getNewConfigGraph(currentGraph) + currentGraph = container.getNewComponentGraph(currentGraph) currentGraph } @@ -230,7 +230,7 @@ class ContainerTest { dirConfigSource.writeConfig("jersey-injection", """inject[0]" """) val container = newContainer(dirConfigSource) - val componentGraph = container.getNewConfigGraph() + val componentGraph = container.getNewComponentGraph() val restApiContext = componentGraph.getInstance(clazz) assertNotNull(restApiContext) @@ -278,7 +278,7 @@ class ContainerTest { dirConfigSource.writeConfig("jersey-injection", injectionConfig) val container = newContainer(dirConfigSource) - val componentGraph = container.getNewConfigGraph() + val componentGraph = container.getNewComponentGraph() val restApiContext = componentGraph.getInstance(restApiClass) } @@ -328,12 +328,12 @@ class ContainerTest { val container = newContainer(dirConfigSource, deconstructor) - val oldGraph = container.getNewConfigGraph() + val oldGraph = container.getNewComponentGraph() val destructableEntity = oldGraph.getInstance(classOf[DestructableEntity]) writeBootstrapConfigs("id2", classOf[DestructableProvider]) container.reloadConfig(2) - container.getNewConfigGraph(oldGraph) + container.getNewComponentGraph(oldGraph) assertTrue(destructableEntity.deconstructed) } diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java index d42afadfd9b..932d31c0036 100644 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java @@ -200,11 +200,9 @@ public final class ConfiguredApplication implements Application { try { ContainerBuilder builder = createBuilderWithGuiceBindings(); - // Block until new config: - boolean gotNewConfigToApply = configurer.getNewConfigGraph(builder.guiceModules().activate(), - qrConfig.restartOnDeploy()); - if (gotNewConfigToApply) - intitializeAndActivateContainer(builder); + // Block until new config arrives, and it should be applied + configurer.getNewComponentGraph(builder.guiceModules().activate(), qrConfig.restartOnDeploy()); + intitializeAndActivateContainer(builder); } catch (ConfigInterruptedException | InterruptedException e) { break; } catch (Exception | LinkageError e) { // LinkageError: OSGi problems diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java index 9d5fcb31288..a81c4adcb2e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java @@ -85,7 +85,7 @@ public class ApplicationPackageBuilder { public ApplicationPackageBuilder allow(ValidationId validationId) { validationOverridesBody.append(" <allow until='"); - validationOverridesBody.append(asIso8601Date(Instant.now().plus(Duration.ofDays(29)))); + validationOverridesBody.append(asIso8601Date(Instant.now().plus(Duration.ofDays(28)))); validationOverridesBody.append("'>"); validationOverridesBody.append(validationId.value()); validationOverridesBody.append("</allow>\n"); diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala index 99cc8259ab3..1bc95f52313 100644 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala +++ b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala @@ -21,6 +21,8 @@ class StandaloneSubscriberFactory(root: VespaModel) extends SubscriberFactory { override def close() {} + override def internalRedeploy() = { false } + override def config = { def getConfig(key: ConfigKeyT) = { @@ -46,6 +48,7 @@ class StandaloneSubscriberFactory(root: VespaModel) extends SubscriberFactory { //if waitNextGeneration has not yet been called, -1 should be returned var generation = -1L + } override def getSubscriber(configKeys: java.util.Set[_ <: ConfigKey[_]]) = |