diff options
63 files changed, 278 insertions, 116 deletions
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 fbc10c772d7..81ecd8620b9 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 @@ -352,6 +352,7 @@ public class ConfigProxyRpcServer implements Runnable, TargetWatcher, RpcServer request.getRequestTrace().trace(TRACELEVEL, "Config proxy returnOkResponse()"); request.addOkResponse(config.getPayload(), config.getGeneration(), + config.isInternalRedeploy(), config.applyOnRestart(), config.getConfigMd5()); log.log(Level.FINE, () -> "Return response: " + request.getShortDescription() + ",configMd5=" + config.getConfigMd5() + 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 33e798da15e..87a8764310a 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, false, defContent, Optional.empty()); + generation, false, false, defContent, Optional.empty()); String defName2 = "bar"; barConfig = new RawConfig(new ConfigKey<>(defName2, configId, namespace), defMd5, fooPayload, configMd5, - generation, false, defContent, Optional.empty()); + generation, false, 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 485a091d9ae..c5e680f950a 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, 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()); + config = new RawConfig(configKey, defMd5, payload, configMd5, generation, false, false, defContent, Optional.empty()); + config2 = new RawConfig(configKey2, defMd52, payload2, configMd5, generation, false, false, defContent, Optional.empty()); + configDifferentMd5 = new RawConfig(configKey, differentDefMd5, payloadDifferentMd5, configMd5, generation, false, 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 87c1fa151f8..c858f894fc4 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 @@ -34,9 +34,9 @@ public class ProxyServerTest { // errorConfig based on fooConfig 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(), false, - ErrorCode.UNKNOWN_DEFINITION, fooConfig.getDefContent(), Optional.empty()); + static final RawConfig errorConfig = new RawConfig(errorConfigKey, fooConfig.getDefMd5(), + fooConfig.getPayload(), fooConfig.getConfigMd5(), + fooConfig.getGeneration(), false, false, ErrorCode.UNKNOWN_DEFINITION, fooConfig.getDefContent(), Optional.empty()); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -178,9 +178,9 @@ public class ProxyServerTest { assertEquals(1, cache.size()); // Simulate an empty response - RawConfig emptyConfig = new RawConfig(fooConfig.getKey(), fooConfig.getDefMd5(), Payload.from("{}"), - fooConfig.getConfigMd5(), 0, false, - 0, fooConfig.getDefContent(), Optional.empty()); + RawConfig emptyConfig = new RawConfig(fooConfig.getKey(), fooConfig.getDefMd5(), + Payload.from("{}"), fooConfig.getConfigMd5(), + 0, false, false, 0, fooConfig.getDefContent(), Optional.empty()); source.put(fooConfig.getKey(), emptyConfig); res = proxy.resolveConfig(tester.createRequest(fooConfig)); @@ -239,7 +239,7 @@ public class ProxyServerTest { static RawConfig createConfigWithNextConfigGeneration(RawConfig config, int errorCode, Payload payload, long configGeneration) { return new RawConfig(config.getKey(), config.getDefMd5(), payload, config.getConfigMd5(), - configGeneration, false, + configGeneration, false, false, errorCode, config.getDefContent(), Optional.empty()); } diff --git a/config/abi-spec.json b/config/abi-spec.json index a41df9aa60d..b60f9053642 100644 --- a/config/abi-spec.json +++ b/config/abi-spec.json @@ -218,6 +218,7 @@ "public boolean isClosed()", "public com.yahoo.config.subscription.ConfigHandle subscribe(com.yahoo.config.subscription.ConfigSubscriber$SingleSubscriber, java.lang.Class, java.lang.String)", "public long getGeneration()", + "public boolean isInternalRedeploy()", "protected void finalize()" ], "fields": [ diff --git a/config/src/apps/vespa-get-config/getconfig.cpp b/config/src/apps/vespa-get-config/getconfig.cpp index 65dc800c275..92966b934f1 100644 --- a/config/src/apps/vespa-get-config/getconfig.cpp +++ b/config/src/apps/vespa-get-config/getconfig.cpp @@ -220,7 +220,7 @@ GetConfig::Main() FRTConfigRequestFactory requestFactory(protocolVersion, traceLevel, vespaVersion, config::protocol::readProtocolCompressionType()); FRTConnection connection(spec, _server->supervisor(), TimingValues()); ConfigKey key(configId, defName, defNamespace, defMD5, defSchema); - ConfigState state(configMD5, generation, false); + ConfigState state(configMD5, generation, false, false); FRTConfigRequest::UP request = requestFactory.createConfigRequest(key, &connection, state, serverTimeout * 1000); _target->InvokeSync(request->getRequest(), clientTimeout); // seconds @@ -244,6 +244,7 @@ GetConfig::Main() printf("configMD5 %s\n", rState.md5.c_str()); printf("generation %" PRId64 "\n", rState.generation); + printf("internalRedeploy %s\n", rState.internalRedeploy == 0 ? "false" : "true"); printf("trace %s\n", response->getTrace().toString().c_str()); } else if (traceLevel > 0) { printf("trace %s\n", response->getTrace().toString().c_str()); 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 b4750eebfee..ad131f8e0dd 100644 --- a/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java +++ b/config/src/main/java/com/yahoo/config/subscription/ConfigSubscriber.java @@ -43,6 +43,9 @@ public class ConfigSubscriber implements AutoCloseable { /** 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; + /** * Whether the last generation should only be applied on restart, not immediately. * Once this is set it will not be unset, as no future generation should be applied @@ -259,6 +262,7 @@ public class ConfigSubscriber implements AutoCloseable { 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 { boolean allGenerationsChanged = true; boolean allGenerationsTheSame = true; @@ -275,6 +279,7 @@ public class ConfigSubscriber implements AutoCloseable { allGenerationsTheSame &= currentGen.equals(config.getGeneration()); allGenerationsChanged &= config.isGenerationChanged(); anyConfigChanged |= config.isConfigChanged(); + internalRedeployOnly &= config.isInternalRedeploy(); applyOnRestartOnly |= requireChange && config.applyOnRestart(); // only if this is reconfig timeLeftMillis = timeoutInMillis + started - System.currentTimeMillis(); } @@ -296,6 +301,7 @@ public class ConfigSubscriber implements AutoCloseable { // Also if appropriate update the changed flag on the handler, which clients use. markSubsChangedSeen(currentGen); synchronized (monitor) { + internalRedeploy = internalRedeployOnly; generation = currentGen; } } @@ -485,6 +491,12 @@ public class ConfigSubscriber implements AutoCloseable { } /** + * Whether the current config generation received by this was due to a system-internal redeploy, + * not an application package change + */ + public boolean isInternalRedeploy() { synchronized (monitor) { 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 07bbd950c83..15f6395c417 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 @@ -39,26 +39,29 @@ public abstract class ConfigSubscription<T extends ConfigInstance> { private final boolean generationChanged; private final T config; private final Long generation; + private final boolean internalRedeploy; private final boolean applyOnRestart; private ConfigState(boolean generationChanged, Long generation, + boolean internalRedeploy, boolean applyOnRestart, boolean configChanged, T config) { this.generationChanged = generationChanged; this.generation = generation; + this.internalRedeploy = internalRedeploy; this.applyOnRestart = applyOnRestart; this.configChanged = configChanged; this.config = config; } private ConfigState(Long generation, T config) { - this(false, generation, false, false, config); + this(false, generation, false, false, false, config); } private ConfigState() { - this(false, 0L, false, false, null); + this(false, 0L, false, false, false, null); } private ConfigState<T> createUnchanged() { return new ConfigState<>(generation, config); } @@ -66,6 +69,12 @@ public abstract class ConfigSubscription<T extends ConfigInstance> { 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 boolean applyOnRestart() { return applyOnRestart; } public T getConfig() { return config; } @@ -181,29 +190,34 @@ public abstract class ConfigSubscription<T extends ConfigInstance> { return !prev.getGeneration().equals(requiredGen) || prev.isConfigChanged(); } - void setConfig(Long generation, boolean applyOnRestart, T config) { - this.config.set(new ConfigState<>(true, generation, applyOnRestart, true, config)); + void setConfig(Long generation, boolean internalRedeploy, boolean applyOnRestart, T config) { + this.config.set(new ConfigState<>(true, generation, internalRedeploy, applyOnRestart, true, config)); } /** Used by {@link FileConfigSubscription} and {@link ConfigSetSubscription} */ protected void setConfigIncGen(T config) { ConfigState<T> prev = this.config.get(); - this.config.set(new ConfigState<>(true, prev.getGeneration() + 1, prev.applyOnRestart(), true, config)); + this.config.set(new ConfigState<>(true, prev.getGeneration() + 1, prev.isInternalRedeploy(), prev.applyOnRestart(), true, config)); } protected void setConfigIfChanged(T config) { ConfigState<T> prev = this.config.get(); - this.config.set(new ConfigState<>(true, prev.getGeneration(), prev.applyOnRestart(), !config.equals(prev.getConfig()), config)); + this.config.set(new ConfigState<>(true, prev.getGeneration(), prev.isInternalRedeploy(), prev.applyOnRestart(), !config.equals(prev.getConfig()), config)); } void setGeneration(Long generation) { ConfigState<T> prev = config.get(); - this.config.set(new ConfigState<>(true, generation, prev.applyOnRestart(), prev.isConfigChanged(), prev.getConfig())); + this.config.set(new ConfigState<>(true, generation, prev.isInternalRedeploy(), prev.applyOnRestart(), prev.isConfigChanged(), prev.getConfig())); + } + + void setInternalRedeploy(boolean internalRedeploy) { + ConfigState<T> prev = config.get(); + this.config.set(new ConfigState<>(prev.isGenerationChanged(), prev.getGeneration(), internalRedeploy, prev.applyOnRestart(), prev.isConfigChanged(), prev.getConfig())); } void setApplyOnRestart(boolean applyOnRestart) { ConfigState<T> prev = config.get(); - this.config.set(new ConfigState<>(prev.isGenerationChanged(), prev.getGeneration(), applyOnRestart, prev.isConfigChanged(), prev.getConfig())); + this.config.set(new ConfigState<>(prev.isGenerationChanged(), prev.getGeneration(), prev.isInternalRedeploy(), applyOnRestart, prev.isConfigChanged(), prev.getConfig())); } /** diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java index b052c79f429..ba8fc8a5e19 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/GenericJRTConfigSubscription.java @@ -32,7 +32,7 @@ public class GenericJRTConfigSubscription extends JRTConfigSubscription<RawConfi @Override protected void setNewConfig(JRTClientConfigRequest jrtReq) { - setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsApplyOnRestart(), RawConfig.createFromResponseParameters(jrtReq) ); + setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsInternalRedeploy(), jrtReq.responseIsApplyOnRestart(), RawConfig.createFromResponseParameters(jrtReq) ); log.log(FINE, () -> "in setNewConfig, config=" + this.getConfigState().getConfig()); } @@ -50,6 +50,17 @@ public class GenericJRTConfigSubscription extends JRTConfigSubscription<RawConfi // Override to propagate internal redeploy into the config value in addition to the config state @Override + void setInternalRedeploy(boolean internalRedeploy) { + super.setInternalRedeploy(internalRedeploy); + ConfigState<RawConfig> configState = getConfigState(); + + if (configState.getConfig() != null) { + configState.getConfig().setInternalRedeploy(internalRedeploy); + } + } + + // Override to propagate internal redeploy into the config value in addition to the config state + @Override void setApplyOnRestart(boolean applyOnRestart) { super.setApplyOnRestart(applyOnRestart); ConfigState<RawConfig> configState = getConfigState(); 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 b48e5905239..e9d5d317995 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 @@ -93,6 +93,7 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc } log.log(FINE, () -> "Polled queue and found config " + jrtReq); if (jrtReq.hasUpdatedGeneration()) { + setInternalRedeploy(jrtReq.responseIsInternalRedeploy()); setApplyOnRestart(jrtReq.responseIsApplyOnRestart()); if (jrtReq.hasUpdatedConfig()) { setNewConfig(jrtReq); @@ -111,7 +112,7 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc } catch (IllegalArgumentException e) { badConfigE = e; } - setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsApplyOnRestart(), configInstance); + setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsInternalRedeploy(), jrtReq.responseIsApplyOnRestart(), configInstance); if (badConfigE != null) { throw new IllegalArgumentException("Bad config from jrt", badConfigE); } 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 05da9a72837..9fc5d9d3300 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 @@ -63,7 +63,7 @@ public class JarConfigSubscription<T extends ConfigInstance> extends ConfigSubsc } catch (IOException e) { throw new ConfigurationRuntimeException(e); } - setConfig(0L, false, config); + setConfig(0L, false, false, config); try { jarFile.close(); } catch (IOException e) { 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 58eed7f9e78..3a284489109 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 @@ -115,7 +115,7 @@ public class MockConnection implements ConnectionPool, com.yahoo.vespa.config.Co JRTServerConfigRequestV3 jrtReq = JRTServerConfigRequestV3.createFromRequest(request); Payload payload = Payload.from(ConfigPayload.empty()); long generation = 1; - jrtReq.addOkResponse(payload, generation, false, ConfigUtils.getMd5(payload.getData())); + jrtReq.addOkResponse(payload, generation, false, false, ConfigUtils.getMd5(payload.getData())); } } 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 68ff6bb0135..1ff0a058a93 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 @@ -35,7 +35,7 @@ public class RawConfigSubscription<T extends ConfigInstance> extends ConfigSubsc if (payload == null) { payload = inputPayload; ConfigPayload configPayload = new CfgConfigPayloadBuilder().deserialize(Arrays.asList(payload.split("\n"))); - setConfig(0L, false, configPayload.toInstance(configClass, key.getConfigId())); + setConfig(0L, false, false, configPayload.toInstance(configClass, key.getConfigId())); return true; } try { 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 1c28d4c5e05..cf0f1243acf 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; private boolean applyOnRestart; /** @@ -40,30 +41,31 @@ 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, false, 0, Collections.emptyList(), Optional.empty()); + this(key, defMd5, null, "", 0L, false, false, 0, Collections.emptyList(), Optional.empty()); } public RawConfig(ConfigKey<?> key, String defMd5, Payload payload, String configMd5, long generation, - boolean applyOnRestart, List<String> defContent, + boolean internalRedeploy, boolean applyOnRestart, List<String> defContent, Optional<VespaVersion> vespaVersion) { - this(key, defMd5, payload, configMd5, generation, applyOnRestart, 0, defContent, vespaVersion); + this(key, defMd5, payload, configMd5, generation, internalRedeploy, applyOnRestart, 0, defContent, vespaVersion); } /** Copy constructor */ public RawConfig(RawConfig rawConfig) { this(rawConfig.key, rawConfig.defMd5, rawConfig.payload, rawConfig.configMd5, - rawConfig.generation, rawConfig.applyOnRestart, + rawConfig.generation, rawConfig.internalRedeploy, rawConfig.applyOnRestart, rawConfig.errorCode, rawConfig.defContent, rawConfig.getVespaVersion()); } public RawConfig(ConfigKey<?> key, String defMd5, Payload payload, String configMd5, long generation, - boolean applyOnRestart, int errorCode, List<String> defContent, + boolean internalRedeploy, boolean applyOnRestart, 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.applyOnRestart = applyOnRestart; this.errorCode = errorCode; this.defContent = defContent; @@ -81,6 +83,7 @@ public class RawConfig extends ConfigInstance { req.getNewPayload(), req.getNewConfigMd5(), req.getNewGeneration(), + req.responseIsInternalRedeploy(), req.responseIsApplyOnRestart(), 0, req.getDefContent().asList(), @@ -98,6 +101,7 @@ public class RawConfig extends ConfigInstance { Payload.from(new Utf8String(""), CompressionInfo.uncompressed()), req.getRequestConfigMd5(), req.getRequestGeneration(), + req.isInternalRedeploy(), req.applyOnRestart(), 0, req.getDefContent().asList(), @@ -121,8 +125,16 @@ public class RawConfig extends ConfigInstance { public void setGeneration(long generation) { this.generation = generation; } + public void setInternalRedeploy(boolean internalRedeploy) { this.internalRedeploy = internalRedeploy; } + public void setApplyOnRestart(boolean applyOnRestart) { this.applyOnRestart = applyOnRestart; } + /** + * Returns whether this config generation was created by a system internal redeploy, not an + * application package change. + */ + public boolean isInternalRedeploy() { return internalRedeploy; } + public boolean applyOnRestart() { return applyOnRestart; } public Payload getPayload() { return payload; } 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 31e280e708c..27f4816849d 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 @@ -20,6 +20,8 @@ public interface ConfigResponse { long getGeneration(); + boolean isInternalRedeploy(); + boolean applyOnRestart(); String getConfigMd5(); 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 8535cc23225..8f85e2353a5 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 @@ -53,6 +53,9 @@ public interface JRTClientConfigRequest extends JRTConfigRequest { */ long getNewGeneration(); + /** Returns whether this config change is due to an internal change not an application package change */ + boolean responseIsInternalRedeploy(); + /** Returns true if this config should only be applied at the last restart, false if it should be applied immediately */ boolean responseIsApplyOnRestart(); 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 87e63399fc3..e04a2179602 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 @@ -180,6 +180,7 @@ public class JRTClientConfigRequestV3 implements JRTClientConfigRequest { .append("'\n"); sb.append("response='").append(getNewConfigMd5()) .append(",").append(getNewGeneration()) + .append(",").append(responseIsInternalRedeploy()) .append(",").append(responseIsApplyOnRestart()) .append("'\n"); return sb.toString(); @@ -290,6 +291,11 @@ public class JRTClientConfigRequestV3 implements JRTClientConfigRequest { } @Override + public boolean responseIsInternalRedeploy() { + return responseData.getResponseInternalRedeployment(); + } + + @Override public boolean responseIsApplyOnRestart() { return responseData.getResponseApplyOnRestart(); } 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 c085be5924c..dbc6a7bb98d 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 @@ -32,11 +32,14 @@ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigReque * * @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 applyOnRestart true if this config should only be applied on the next restart, * false if it should be applied right away * @param configMd5 The md5sum of the given payload. */ - void addOkResponse(Payload payload, long generation, boolean applyOnRestart, String configMd5); + void addOkResponse(Payload payload, long generation, boolean internalRedeployment, boolean applyOnRestart, + String configMd5); /** * Get the current config md5 of the client config. @@ -59,6 +62,12 @@ public interface JRTServerConfigRequest extends JRTConfigRequest, GetConfigReque */ boolean isDelayedResponse(); + /** + * Returns whether the response config was created by a system internal redeploy, not an application + * package change + */ + boolean isInternalRedeploy(); + boolean applyOnRestart(); /** 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 53a2f4019f9..0bc7c44fe9d 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 @@ -36,6 +36,7 @@ public class JRTServerConfigRequestV3 implements JRTServerConfigRequest { protected final Request request; private final SlimeRequestData requestData; /** Response field */ + private boolean internalRedeploy = false; private boolean applyOnRestart = false; // Response values private boolean isDelayed = false; @@ -67,7 +68,9 @@ public class JRTServerConfigRequestV3 implements JRTServerConfigRequest { } @Override - public void addOkResponse(Payload payload, long generation, boolean applyOnRestart, String configMd5) { + public void addOkResponse(Payload payload, long generation, boolean internalRedeploy, boolean applyOnRestart, + String configMd5) { + this.internalRedeploy = internalRedeploy; this.applyOnRestart = applyOnRestart; boolean changedConfig = !configMd5.equals(getRequestConfigMd5()); boolean changedConfigAndNewGeneration = changedConfig && ConfigUtils.isGenerationNewer(generation, getRequestGeneration()); @@ -79,6 +82,7 @@ public class JRTServerConfigRequestV3 implements JRTServerConfigRequest { addCommonReturnValues(jsonGenerator); setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_CONFIG_MD5, configMd5); setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_CONFIG_GENERATION, generation); + setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_INTERNAL_REDEPLOY, internalRedeploy); setResponseField(jsonGenerator, SlimeResponseData.RESPONSE_APPLY_ON_RESTART, applyOnRestart); jsonGenerator.writeObjectFieldStart(SlimeResponseData.RESPONSE_COMPRESSION_INFO); if (responsePayload == null) { @@ -111,6 +115,9 @@ public class JRTServerConfigRequestV3 implements JRTServerConfigRequest { } @Override + public boolean isInternalRedeploy() { return internalRedeploy; } + + @Override public boolean applyOnRestart() { return applyOnRestart; } public static JRTServerConfigRequestV3 createFromRequest(Request req) { 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 1fec7e17d06..8bdd336fd5c 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 @@ -17,24 +17,28 @@ public class SlimeConfigResponse implements ConfigResponse { private final Utf8Array payload; private final CompressionInfo compressionInfo; private final long generation; + private final boolean internalRedeploy; private final boolean applyOnRestart; private final String configMd5; public static SlimeConfigResponse fromConfigPayload(ConfigPayload payload, long generation, - boolean applyOnRestart, String configMd5) { + boolean internalRedeploy, boolean applyOnRestart, + String configMd5) { Utf8Array data = payload.toUtf8Array(true); - return new SlimeConfigResponse(data, generation, applyOnRestart, + return new SlimeConfigResponse(data, generation, internalRedeploy, applyOnRestart, configMd5, CompressionInfo.create(CompressionType.UNCOMPRESSED, data.getByteLength())); } public SlimeConfigResponse(Utf8Array payload, long generation, + boolean internalRedeploy, boolean applyOnRestart, String configMd5, CompressionInfo compressionInfo) { this.payload = payload; this.generation = generation; + this.internalRedeploy = internalRedeploy; this.applyOnRestart = applyOnRestart; this.configMd5 = configMd5; this.compressionInfo = compressionInfo; @@ -50,6 +54,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 internalRedeploy; } + @Override public boolean applyOnRestart() { return applyOnRestart; } 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 cc98587456c..1c9afa550d4 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 @@ -23,6 +23,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_APPLY_ON_RESTART = "applyOnRestart"; static final String RESPONSE_COMPRESSION_INFO = "compressionInfo"; @@ -67,6 +68,11 @@ class SlimeResponseData { return CompressionInfo.fromSlime(getResponseField(RESPONSE_COMPRESSION_INFO)); } + boolean getResponseInternalRedeployment() { + Inspector inspector = getResponseField(RESPONSE_INTERNAL_REDEPLOY); + return inspector.valid() && inspector.asBool(); + } + boolean getResponseApplyOnRestart() { Inspector inspector = getResponseField(RESPONSE_APPLY_ON_RESTART); return inspector.valid() && inspector.asBool(); diff --git a/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java b/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java index 4211345dff7..07ff27a0154 100644 --- a/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java +++ b/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java @@ -184,7 +184,7 @@ public class JRTConfigRequesterTest { ConfigSubscriber subscriber = new ConfigSubscriber(); final TimingValues timingValues = getTestTimingValues(); JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); - sub.setConfig(1L, false, config()); + sub.setConfig(1L, false, false, config()); final MockConnection connection = new MockConnection(new ErrorResponseHandler()); JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); @@ -212,7 +212,7 @@ public class JRTConfigRequesterTest { ConfigSubscriber subscriber = new ConfigSubscriber(); final TimingValues timingValues = getTestTimingValues(); JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); - sub.setConfig(1L, false, config()); + sub.setConfig(1L, false, false, config()); final MockConnection connection = new MockConnection(new ErrorResponseHandler(com.yahoo.jrt.ErrorCode.TIMEOUT)); JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); @@ -227,7 +227,7 @@ public class JRTConfigRequesterTest { ConfigSubscriber subscriber = new ConfigSubscriber(); final TimingValues timingValues = getTestTimingValues(); JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); - sub.setConfig(1L, false, config()); + sub.setConfig(1L, false, false, config()); final MockConnection connection = new MockConnection(new ErrorResponseHandler(ErrorCode.UNKNOWN_DEFINITION)); JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); 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 e1d11f82eea..9fd0b9ba1fa 100644 --- a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java +++ b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java @@ -61,14 +61,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, false, defContent, Optional.empty()); - RawConfig config2 = new RawConfig(key, defMd5, payload, configMd5, 2L, false, defContent, Optional.empty()); + config = new RawConfig(key, defMd5, payload, configMd5, generation, false, false, defContent, Optional.empty()); + RawConfig config2 = new RawConfig(key, defMd5, payload, configMd5, 2L, false, 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, false, defContent, Optional.of(vespaVersion)); + RawConfig config3 = new RawConfig(key, defMd5, payload, "9999", generation, false, false, defContent, Optional.of(vespaVersion)); assertThat(config, is(not(config3))); assertThat(config.hashCode(), is(not(config3.hashCode()))); // Check that vespa version is set correctly @@ -82,19 +82,19 @@ public class RawConfigTest { assertNotEquals(config, key); // errors - RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 1, defContent, Optional.empty()); + RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 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, false, 2, defContent, Optional.empty()); + RawConfig errorConfig2 = new RawConfig(key, defMd5, payload, configMd5, generation, false, 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, false, defContent, Optional.empty()); + RawConfig config = new RawConfig(key, defMd5, payload, configMd5, generation, false, false, defContent, Optional.empty()); assertThat(config.getPayload(), is(payload)); assertThat(config.getConfigMd5(), is(configMd5)); assertThat(config.getGeneration(), is(generation)); @@ -105,19 +105,19 @@ public class RawConfigTest { public void require_correct_defmd5() { final String defMd5ForEmptyDefContent = "d41d8cd98f00b204e9800998ecf8427e"; - RawConfig config = new RawConfig(key, null, payload, configMd5, generation, false, defContent, Optional.empty()); + RawConfig config = new RawConfig(key, null, payload, configMd5, generation, false, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, "", payload, configMd5, generation, false, defContent, Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, defMd5, payload, configMd5, generation, false, defContent, Optional.empty()); + config = new RawConfig(key, defMd5, payload, configMd5, generation, false, false, defContent, Optional.empty()); assertThat(config.getDefMd5(), is(defMd5)); - config = new RawConfig(key, null, payload, configMd5, generation, false, null, Optional.empty()); + config = new RawConfig(key, null, payload, configMd5, generation, false, false, null, Optional.empty()); assertNull(config.getDefMd5()); - config = new RawConfig(key, null, payload, configMd5, generation, false, List.of(""), Optional.empty()); + config = new RawConfig(key, null, payload, configMd5, generation, false,false, List.of(""), Optional.empty()); assertThat(config.getDefMd5(), is(defMd5ForEmptyDefContent)); - config = new RawConfig(key, "", payload, configMd5, generation, false, null, Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, false, null, Optional.empty()); assertThat(config.getDefMd5(), is("")); - config = new RawConfig(key, "", payload, configMd5, generation, false, List.of(""), Optional.empty()); + config = new RawConfig(key, "", payload, configMd5, generation, false, false, List.of(""), 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 a56c7ef2daa..98eda868bbf 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 @@ -24,7 +24,7 @@ public class ConfigResponseTest { @Test public void require_that_slime_response_is_initialized() throws IOException { ConfigPayload configPayload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); - ConfigResponse response = SlimeConfigResponse.fromConfigPayload(configPayload, 3, false, "mymd5"); + ConfigResponse response = SlimeConfigResponse.fromConfigPayload(configPayload, 3, false, false, "mymd5"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); response.serialize(baos, CompressionType.UNCOMPRESSED); String payload = baos.toString(StandardCharsets.UTF_8); @@ -43,7 +43,7 @@ public class ConfigResponseTest { ConfigPayload configPayload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); Utf8Array data = configPayload.toUtf8Array(true); Utf8Array bytes = new Utf8Array(new LZ4PayloadCompressor().compress(data.getBytes())); - ConfigResponse response = new SlimeConfigResponse(bytes, 3, false, "mymd5", CompressionInfo.create(CompressionType.LZ4, data.getByteLength())); + ConfigResponse response = new SlimeConfigResponse(bytes, 3, false, false, "mymd5", CompressionInfo.create(CompressionType.LZ4, data.getByteLength())); ByteArrayOutputStream baos = new ByteArrayOutputStream(); response.serialize(baos, CompressionType.UNCOMPRESSED); String payload = baos.toString(StandardCharsets.UTF_8); 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 006a6fc6a0a..6fc55e60a54 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 @@ -28,7 +28,6 @@ import java.util.Collections; import java.util.Optional; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -74,11 +73,12 @@ public class JRTConfigRequestV3Test { @Test public void emptypayload() { ConfigPayload payload = ConfigPayload.empty(); - SlimeConfigResponse response = SlimeConfigResponse.fromConfigPayload(payload, 0, false, ConfigUtils.getMd5(payload)); - serverReq.addOkResponse(serverReq.payloadFromResponse(response), response.getGeneration(), false, response.getConfigMd5()); + SlimeConfigResponse response = SlimeConfigResponse.fromConfigPayload(payload, 0, false, false, ConfigUtils.getMd5(payload)); + serverReq.addOkResponse(serverReq.payloadFromResponse(response), response.getGeneration(), false, false, response.getConfigMd5()); assertTrue(clientReq.validateResponse()); assertTrue(clientReq.hasUpdatedGeneration()); - assertEquals("{}", clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString()); + assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is("{}")); + assertFalse(clientReq.responseIsInternalRedeploy()); } @Test @@ -92,9 +92,11 @@ public class JRTConfigRequestV3Test { @Test public void next_request_when_error_is_correct() { - serverReq.addOkResponse(createPayload(), 999999, false, "newmd5"); + serverReq.addOkResponse(createPayload(), 999999, false, false, "newmd5"); serverReq.addErrorResponse(ErrorCode.OUTDATED_CONFIG, "error message"); + System.out.println(serverReq); JRTClientConfigRequest next = clientReq.nextRequest(6); + System.out.println(next); // Should use config md5 and generation from the request, not the response // when there are errors assertThat(next.getRequestConfigMd5(), is(clientReq.getRequestConfigMd5())); @@ -106,7 +108,7 @@ public class JRTConfigRequestV3Test { Payload payload = createPayload("vale"); String md5 = ConfigUtils.getMd5(payload.getData()); long generation = 4L; - serverReq.addOkResponse(payload, generation, false, md5); + serverReq.addOkResponse(payload, generation, false, false, md5); assertTrue(clientReq.validateResponse()); assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is(payload.getData().toString())); assertThat(clientReq.getNewGeneration(), is(4L)); @@ -132,7 +134,7 @@ public class JRTConfigRequestV3Test { @Test public void generation_only_is_updated() { Payload payload = createPayload(); - serverReq.addOkResponse(payload, 4L, false, ConfigUtils.getMd5(payload.getData())); + serverReq.addOkResponse(payload, 4L, false, false, ConfigUtils.getMd5(payload.getData())); boolean value = clientReq.validateResponse(); assertTrue(clientReq.errorMessage(), value); assertFalse(clientReq.hasUpdatedConfig()); @@ -142,7 +144,7 @@ public class JRTConfigRequestV3Test { @Test public void nothing_is_updated() { Payload payload = createPayload(); - serverReq.addOkResponse(payload, currentGeneration, false, configMd5); + serverReq.addOkResponse(payload, currentGeneration, false, false, configMd5); assertTrue(clientReq.validateResponse()); assertFalse(clientReq.hasUpdatedConfig()); assertFalse(clientReq.hasUpdatedGeneration()); @@ -153,7 +155,7 @@ public class JRTConfigRequestV3Test { Payload payload = Payload.from(ConfigPayload.empty()); clientReq = createReq(payload); serverReq = createReq(clientReq.getRequest()); - serverReq.addOkResponse(payload, currentGeneration, false, ConfigUtils.getMd5(payload.getData())); + serverReq.addOkResponse(payload, currentGeneration, false, false, ConfigUtils.getMd5(payload.getData())); boolean val = clientReq.validateResponse(); assertTrue(clientReq.errorMessage(), val); assertFalse(clientReq.hasUpdatedConfig()); @@ -190,7 +192,7 @@ public class JRTConfigRequestV3Test { @Override public void createResponse() { JRTServerConfigRequest serverRequest = createReq(request); - serverRequest.addOkResponse(createPayload(), currentGeneration, false, configMd5); + serverRequest.addOkResponse(createPayload(), currentGeneration, false, false, configMd5); } }); diff --git a/config/src/tests/configagent/configagent.cpp b/config/src/tests/configagent/configagent.cpp index d6766cce822..7cc0abee0bc 100644 --- a/config/src/tests/configagent/configagent.cpp +++ b/config/src/tests/configagent/configagent.cpp @@ -36,7 +36,7 @@ public: _value(value), _fillCalled(false), _valid(valid), - _state(md5, timestamp, false), + _state(md5, timestamp, false, false), _errorMessage(errorMsg), _errorCode(errorC0de), _isError(iserror) @@ -141,6 +141,7 @@ TEST("require that agent returns correct values") { ConfigState cs; ASSERT_EQUAL(cs.md5, handler.getConfigState().md5); ASSERT_EQUAL(cs.generation, handler.getConfigState().generation); + ASSERT_EQUAL(cs.internalRedeploy, handler.getConfigState().internalRedeploy); ASSERT_EQUAL(cs.applyOnRestart, handler.getConfigState().applyOnRestart); } diff --git a/config/src/tests/frt/frt.cpp b/config/src/tests/frt/frt.cpp index 0d70605fa62..60973aea2bf 100644 --- a/config/src/tests/frt/frt.cpp +++ b/config/src/tests/frt/frt.cpp @@ -276,10 +276,10 @@ TEST("require that v3 request is correctly initialized") { traceIn.trace(2, "Hei"); FRTConfigRequestV3 v3req(&conn, key, md5, currentGeneration, hostName, timeout, traceIn, VespaVersion::fromString("1.2.3"), CompressionType::LZ4); - ASSERT_TRUE(v3req.verifyState(ConfigState(md5, 3, false))); - ASSERT_FALSE(v3req.verifyState(ConfigState(md5, 2, false))); - ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 3, false))); - ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 2, false))); + ASSERT_TRUE(v3req.verifyState(ConfigState(md5, 3, false, false))); + ASSERT_FALSE(v3req.verifyState(ConfigState(md5, 2, false, false))); + ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 3, false, false))); + ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 2, false, false))); ConfigDefinition origDef(MyConfig::CONFIG_DEF_SCHEMA); diff --git a/config/src/vespa/config/common/configstate.h b/config/src/vespa/config/common/configstate.h index 2dbea3cc30f..155c182271b 100644 --- a/config/src/vespa/config/common/configstate.h +++ b/config/src/vespa/config/common/configstate.h @@ -15,16 +15,19 @@ public: ConfigState() : md5(""), generation(0), + internalRedeploy(false), applyOnRestart(false) { } - ConfigState(const vespalib::string & md5sum, int64_t gen, bool _applyOnRestart) + ConfigState(const vespalib::string & md5sum, int64_t gen, bool _internalRedeploy, bool _applyOnRestart) : md5(md5sum), generation(gen), + internalRedeploy(_internalRedeploy), applyOnRestart(_applyOnRestart) { } vespalib::string md5; int64_t generation; + bool internalRedeploy; bool applyOnRestart; bool isNewerGenerationThan(const ConfigState & other) const { diff --git a/config/src/vespa/config/frt/protocol.cpp b/config/src/vespa/config/frt/protocol.cpp index 269c3477ba8..5019cce23c5 100644 --- a/config/src/vespa/config/frt/protocol.cpp +++ b/config/src/vespa/config/frt/protocol.cpp @@ -40,6 +40,7 @@ const Memory RESPONSE_CONFIG_MD5 = "configMD5"; const Memory RESPONSE_CONFIG_GENERATION = "generation"; const Memory RESPONSE_PAYLOAD = "payload"; const Memory RESPONSE_TRACE = "trace"; +const Memory RESPONSE_INTERNAL_REDEPLOY = "internalRedeploy"; const Memory RESPONSE_APPLY_ON_RESTART = "applyOnRestart"; const Inspector & diff --git a/config/src/vespa/config/frt/protocol.h b/config/src/vespa/config/frt/protocol.h index 0ec16952701..a7465ef979d 100644 --- a/config/src/vespa/config/frt/protocol.h +++ b/config/src/vespa/config/frt/protocol.h @@ -52,6 +52,7 @@ extern const vespalib::Memory RESPONSE_CONFIG_MD5; extern const vespalib::Memory RESPONSE_CONFIG_GENERATION; extern const vespalib::Memory RESPONSE_PAYLOAD; extern const vespalib::Memory RESPONSE_TRACE; +extern const vespalib::Memory RESPONSE_INTERNAL_REDEPLOY; extern const vespalib::Memory RESPONSE_APPLY_ON_RESTART; const vespalib::slime::Inspector & extractPayload(const vespalib::Slime & data); diff --git a/config/src/vespa/config/frt/slimeconfigresponse.cpp b/config/src/vespa/config/frt/slimeconfigresponse.cpp index af224008d01..f778fe574f1 100644 --- a/config/src/vespa/config/frt/slimeconfigresponse.cpp +++ b/config/src/vespa/config/frt/slimeconfigresponse.cpp @@ -68,6 +68,7 @@ SlimeConfigResponse::readState() const const Slime & data(*_data); return ConfigState(data.get()[RESPONSE_CONFIG_MD5].asString().make_string(), data.get()[RESPONSE_CONFIG_GENERATION].asLong(), + data.get()[RESPONSE_INTERNAL_REDEPLOY].asBool(), data.get()[RESPONSE_APPLY_ON_RESTART].asBool()); } 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 0bddd8d0637..933a8d86230 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 @@ -46,7 +46,7 @@ public class SuperModelController { ConfigKey<?> configKey = request.getConfigKey(); validateConfigDefinition(request.getConfigKey(), request.getDefContent()); ConfigPayload payload = model.getConfig(configKey); - return responseFactory.createResponse(payload, generation, false); + return responseFactory.createResponse(payload, generation, false, false); } private void validateConfigDefinition(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 55ef05650af..156b4be392e 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 @@ -44,18 +44,20 @@ public class Application implements ModelResult { /** The config generation for this application. */ private final long applicationGeneration; + 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 applicationGeneration, + public Application(Model model, ServerCache cache, long applicationGeneration, boolean internalRedeploy, Version vespaVersion, MetricUpdater metricUpdater, ApplicationId app) { Objects.requireNonNull(model, "The model cannot be null"); this.model = model; this.cache = cache; this.applicationGeneration = applicationGeneration; + this.internalRedeploy = internalRedeploy; this.vespaVersion = vespaVersion; this.metricUpdater = metricUpdater; this.app = app; @@ -169,6 +171,7 @@ public class Application implements ModelResult { ConfigResponse configResponse = responseFactory.createResponse(payload, applicationGeneration, + internalRedeploy, applyOnRestart); metricUpdater.incrementProcTime(System.currentTimeMillis() - start); if (useCache(req)) { 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 9e104461b33..a3a8bba567a 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 @@ -110,6 +110,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { return new Application(modelFactory.createModel(modelContext), serverCache, applicationGeneration, + applicationPackage.getMetaData().isInternalRedeploy(), modelFactory.version(), 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 8003b2a2be9..00a0e6f8f2e 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 @@ -27,12 +27,14 @@ public interface ConfigResponseFactory { /** * Creates a {@link ConfigResponse} for a given payload and generation. * - * @param payload the {@link ConfigPayload} to put in the response - * @param generation the payload generation + * @param payload the {@link ConfigPayload} to put in the response + * @param generation the payload generation + * @param internalRedeploy whether this config generation was produced by an internal redeployment + * not a change to the application package * @param applyOnRestart true if this config change should only be applied on restart, * false if it should be applied immediately * @return a {@link ConfigResponse} that can be sent to the client */ - ConfigResponse createResponse(ConfigPayload payload, long generation, boolean applyOnRestart); + ConfigResponse createResponse(ConfigPayload payload, long generation, boolean internalRedeploy, boolean applyOnRestart); } 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 010640b967f..5d637c0e0bd 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 @@ -125,7 +125,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.applyOnRestart(), config.getConfigMd5()); + request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), config.isInternalRedeploy(), config.applyOnRestart(), config.getConfigMd5()); if (logDebug(trace)) { debugLog(trace, "return response: " + request.getShortDescription()); } @@ -166,8 +166,8 @@ class GetConfigProcessor implements Runnable { log.log(Level.FINE, () -> "Returning empty sentinel config for request from " + request.getClientHostName()); ConfigPayload emptyPayload = ConfigPayload.empty(); String configMd5 = ConfigUtils.getMd5(emptyPayload); - ConfigResponse config = SlimeConfigResponse.fromConfigPayload(emptyPayload, 0, false, configMd5); - request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), false, config.getConfigMd5()); + ConfigResponse config = SlimeConfigResponse.fromConfigPayload(emptyPayload, 0, false, false, configMd5); + request.addOkResponse(request.payloadFromResponse(config), config.getGeneration(), false, false, config.getConfigMd5()); respond(request); } 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 619e6c0a2a2..a818eaeb8f5 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 @@ -22,12 +22,13 @@ public class LZ4ConfigResponseFactory implements ConfigResponseFactory { @Override public ConfigResponse createResponse(ConfigPayload payload, long generation, + boolean internalRedeploy, boolean applyOnRestart) { 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, generation, applyOnRestart, configMd5, info); + return new SlimeConfigResponse(compressed, generation, internalRedeploy, applyOnRestart, 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 8d852ebd8c9..e3f3f1c1395 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 @@ -19,11 +19,12 @@ public class UncompressedConfigResponseFactory implements ConfigResponseFactory @Override public ConfigResponse createResponse(ConfigPayload payload, long generation, + boolean internalRedeploy, boolean applyOnRestart) { Utf8Array rawPayload = payload.toUtf8Array(true); String configMd5 = ConfigUtils.getMd5(rawPayload); CompressionInfo info = CompressionInfo.create(CompressionType.UNCOMPRESSED, rawPayload.getByteLength()); - return new SlimeConfigResponse(rawPayload, generation, applyOnRestart, configMd5, info); + return new SlimeConfigResponse(rawPayload, generation, internalRedeploy, applyOnRestart, configMd5, info); } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java index 35449238f78..fa69df9f628 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java @@ -48,9 +48,9 @@ public class ServerCacheTest { cache = new ServerCache(new TestConfigDefinitionRepo(), userConfigDefinitionRepo); - cache.put(fooBarCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5), configMd5); - cache.put(bazQuuxCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5), configMd5); - cache.put(fooBarCacheKeyDifferentMd5, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5_2), configMd5_2); + cache.put(fooBarCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, false, configMd5), configMd5); + cache.put(bazQuuxCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, false, configMd5), configMd5); + cache.put(fooBarCacheKeyDifferentMd5, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, false, configMd5_2), configMd5_2); } @Test 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 e33b52e5fd4..26ef180cf8d 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 @@ -125,7 +125,7 @@ public class SuperModelRequestHandlerTest { private static class TestApplication extends Application { TestApplication(VespaModel vespaModel, ServerCache cache, long appGeneration, ApplicationId app) { - super(vespaModel, cache, appGeneration, new Version(1, 2, 3), MetricUpdater.createTestUpdater(), app); + super(vespaModel, cache, appGeneration, false, new Version(1, 2, 3), MetricUpdater.createTestUpdater(), app); } } 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 9ef63b768a9..3270bb5cd76 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 @@ -70,7 +70,7 @@ public class ApplicationMapperTest { } private Application createApplication(Version version) { - return new Application(new ModelStub(), null, 0, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId()); + return new Application(new ModelStub(), null, 0, false, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId()); } } 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 b37c5defbc3..a5ec2583595 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 @@ -58,6 +58,6 @@ public class ApplicationSetTest { } private Application createApplication(Version version) { - return new Application(new ModelStub(), null, 0, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId()); + return new Application(new ModelStub(), null, 0, false, version, MetricUpdater.createTestUpdater(), ApplicationId.defaultId()); } } 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 acd0acc3792..1e70304ef42 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 @@ -57,7 +57,7 @@ public class ApplicationTest { ApplicationName.from("foobar"), InstanceName.defaultName()); ServerCache cache = new ServerCache(); Version vespaVersion = new Version(1, 2, 3); - Application app = new Application(new ModelStub(), cache, 1337L, 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)); @@ -76,7 +76,7 @@ public class ApplicationTest { ServerCache cache = createCacheAndAddContent(); VespaModel model = new VespaModel(FilesApplicationPackage.fromFile(testApp)); ApplicationId applicationId = new ApplicationId.Builder().tenant("foo").applicationName("foo").build(); - handler = new Application(model, cache, 1L, new Version(1, 2, 3), + handler = new Application(model, cache, 1L, false, new Version(1, 2, 3), new MetricUpdater(Metrics.createTestMetrics(), Metrics.createDimensions(applicationId)), applicationId); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java index 2fbc14dda50..8a3a3b6e0ca 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java @@ -64,6 +64,7 @@ public class ConfigConvergenceCheckerTest { application = new Application(mockModel, new ServerCache(), 3, + false, new Version(0, 0, 0), MetricUpdater.createTestUpdater(), appId); checker = new ConfigConvergenceChecker(); @@ -136,6 +137,7 @@ public class ConfigConvergenceCheckerTest { MockModel.createContainerHost(service2.getHost(), service2.getPort())) ); Application application = new Application(model, new ServerCache(), 4, + false, new Version(0, 0, 0), MetricUpdater.createTestUpdater(), appId); 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 b650e8eb291..e425943ef59 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 @@ -164,6 +164,7 @@ public class FileDistributionStatusTest { return new Application(mockModel, new ServerCache(), 3, + false, new Version(0, 0, 0), MetricUpdater.createTestUpdater(), appId); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java index 947308962d4..aa3f240b12e 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java @@ -181,6 +181,7 @@ public class TenantApplicationsTest { applications.activateApplication(ApplicationSet.from(new Application(model, new ServerCache(), 1, + false, vespaVersion, MetricUpdater.createTestUpdater(), applicationId)), diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigResponseTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigResponseTest.java index ef0a5f6113d..336a8c623b8 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigResponseTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/HttpConfigResponseTest.java @@ -20,7 +20,7 @@ public class HttpConfigResponseTest { public void require_that_response_is_created_from_config() throws IOException { final long generation = 1L; ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); - ConfigResponse configResponse = SlimeConfigResponse.fromConfigPayload(payload, generation, false, "mymd5"); + ConfigResponse configResponse = SlimeConfigResponse.fromConfigPayload(payload, generation, false, false, "mymd5"); HttpConfigResponse response = HttpConfigResponse.createFromConfig(configResponse); assertThat(SessionHandlerTest.getRenderedString(response), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}")); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/DeploymentMetricsRetrieverTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/DeploymentMetricsRetrieverTest.java index b5cdfa8eda2..3a68e0f4c65 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/DeploymentMetricsRetrieverTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/DeploymentMetricsRetrieverTest.java @@ -34,8 +34,8 @@ public class DeploymentMetricsRetrieverTest { public void getMetrics() { MockModel mockModel = new MockModel(mockHosts()); MockDeploymentMetricsRetriever mockMetricsRetriever = new MockDeploymentMetricsRetriever(); - Application application = new Application(mockModel, null, 0, - null, null, ApplicationId.fromSerializedForm("tenant:app:instance")); + Application application = new Application(mockModel, null, 0, false, + null, null, ApplicationId.fromSerializedForm("tenant:app:instance")); DeploymentMetricsRetriever clusterMetricsRetriever = new DeploymentMetricsRetriever(mockMetricsRetriever); clusterMetricsRetriever.getMetrics(application); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/ProtonMetricsRetrieverTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/ProtonMetricsRetrieverTest.java index 7fab01faf3d..bbf9bd0bcb7 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/ProtonMetricsRetrieverTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/metrics/ProtonMetricsRetrieverTest.java @@ -29,8 +29,8 @@ public class ProtonMetricsRetrieverTest { public void getMetrics() { ProtonMetricsRetrieverTest.MockModel mockModel = new MockModel(mockHosts()); ProtonMetricsRetrieverTest.MockProtonMetricsRetriever mockMetricsRetriever = new MockProtonMetricsRetriever(); - Application application = new Application(mockModel, null, 0, - null, null, ApplicationId.fromSerializedForm("tenant:app:instance")); + Application application = new Application(mockModel, null, 0, false, + null, null, ApplicationId.fromSerializedForm("tenant:app:instance")); ProtonMetricsRetriever clusterMetricsRetriever = new ProtonMetricsRetriever(mockMetricsRetriever); clusterMetricsRetriever.getMetrics(application); 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 5d8b8c92472..c932627ce6a 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 @@ -16,7 +16,7 @@ public class ConfigResponseFactoryTest { @Test public void testUncompressedFactory() { UncompressedConfigResponseFactory responseFactory = new UncompressedConfigResponseFactory(); - ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), 3, false); + ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), 3, false, false); assertEquals(CompressionType.UNCOMPRESSED, response.getCompressionInfo().getCompressionType()); assertEquals(3L,response.getGeneration()); assertEquals(2, response.getPayload().getByteLength()); @@ -25,7 +25,7 @@ public class ConfigResponseFactoryTest { @Test public void testLZ4CompressedFactory() { LZ4ConfigResponseFactory responseFactory = new LZ4ConfigResponseFactory(); - ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), 3, false); + ConfigResponse response = responseFactory.createResponse(ConfigPayload.empty(), 3, false, 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/RpcServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java index 735eae2700f..704b00c4e0b 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 @@ -131,6 +131,7 @@ public class RpcServerTest { Application app = new Application(new VespaModel(MockApplicationPackage.createEmpty()), new ServerCache(), 2L, + false, new Version(1, 2, 3), MetricUpdater.createTestUpdater(), applicationId); 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 4900c50f1d7..d9d6fb01086 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 @@ -92,6 +92,7 @@ public class TenantRepositoryTest { applicationRepo.activateApplication(ApplicationSet.from(new Application(new VespaModel(MockApplicationPackage.createEmpty()), new ServerCache(), 4L, + false, new Version(1, 2, 3), MetricUpdater.createTestUpdater(), id)), 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 ac632f5acce..af163e88fee 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 @@ -74,7 +74,7 @@ public class HandlersConfigurerDi { this.vespaContainer = vespaContainer; container = new Container(subscriberFactory, configId, deconstructor, osgiWrapper); - getNewComponentGraph(discInjector); + getNewComponentGraph(discInjector, false); } private static class ContainerAndDiOsgi extends OsgiImpl implements OsgiWrapper { @@ -128,9 +128,10 @@ public class HandlersConfigurerDi { /** * Wait for new config to arrive and produce the new graph */ - public void getNewComponentGraph(Injector discInjector) { + public void getNewComponentGraph(Injector discInjector, boolean restartOnRedeploy) { currentGraph = container.getNewComponentGraph(currentGraph, - createFallbackInjector(vespaContainer, discInjector)); + createFallbackInjector(vespaContainer, discInjector), + restartOnRedeploy); } private Injector createFallbackInjector(com.yahoo.container.Container vespaContainer, Injector discInjector) { 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 42e54d3a78f..d98a865e1fb 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 @@ -117,7 +117,7 @@ public class HandlersConfigurerTestWrapper { public void reloadConfig() { configurer.reloadConfig(++lastGeneration); - configurer.getNewComponentGraph(guiceInjector()); + configurer.getNewComponentGraph(guiceInjector(), false); } public void shutdown() { diff --git a/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java index 75a660789e2..1133363be8e 100644 --- a/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java +++ b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java @@ -30,9 +30,8 @@ public class CloudSubscriberFactory implements SubscriberFactory { private static final Logger log = Logger.getLogger(CloudSubscriberFactory.class.getName()); private final ConfigSource configSource; - private final Map<CloudSubscriber, Integer> activeSubscribers = new WeakHashMap<>(); - private Optional<Long> testGeneration = Optional.empty(); + private Map<CloudSubscriber, Integer> activeSubscribers = new WeakHashMap<>(); public CloudSubscriberFactory(ConfigSource configSource) { this.configSource = configSource; @@ -71,6 +70,9 @@ public class CloudSubscriberFactory implements SubscriberFactory { // if waitNextGeneration has not yet been called, -1 should be returned private long generation = -1L; + // True if this reconfiguration was caused by a system-internal redeploy, not an external application change + private boolean internalRedeploy = false; + private CloudSubscriber(Set<ConfigKey<ConfigInstance>> keys, ConfigSource configSource) { this.subscriber = new ConfigSubscriber(configSource); keys.forEach(k -> handles.put(k, subscriber.subscribe(k.getConfigClass(), k.getConfigId()))); @@ -86,6 +88,11 @@ public class CloudSubscriberFactory implements SubscriberFactory { return generation; } + @Override + public boolean internalRedeploy() { + return internalRedeploy; + } + //mapValues returns a view,, so we need to force evaluation of it here to prevent deferred evaluation. @Override public Map<ConfigKey<ConfigInstance>, ConfigInstance> config() { @@ -121,6 +128,7 @@ public class CloudSubscriberFactory implements SubscriberFactory { } generation = subscriber.getGeneration(); + internalRedeploy = subscriber.isInternalRedeploy(); return generation; } diff --git a/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java b/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java index 44e38648230..f892fe410a8 100644 --- a/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java +++ b/container-di/src/main/java/com/yahoo/container/di/ConfigRetriever.java @@ -46,10 +46,11 @@ public final class ConfigRetriever { } public ConfigSnapshot getConfigs(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys, - long leastGeneration) { + long leastGeneration, + boolean restartOnRedeploy) { // Loop until we get config. while (true) { - Optional<ConfigSnapshot> maybeSnapshot = getConfigsOnce(componentConfigKeys, leastGeneration); + Optional<ConfigSnapshot> maybeSnapshot = getConfigsOnce(componentConfigKeys, leastGeneration, restartOnRedeploy); if (maybeSnapshot.isPresent()) { var configSnapshot = maybeSnapshot.get(); resetComponentSubscriberIfBootstrap(configSnapshot); @@ -58,8 +59,13 @@ public final class ConfigRetriever { } } + ConfigSnapshot getConfigs(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys, long leastGeneration) { + return getConfigs(componentConfigKeys, leastGeneration, false); + } + Optional<ConfigSnapshot> getConfigsOnce(Set<ConfigKey<? extends ConfigInstance>> componentConfigKeys, - long leastGeneration) { + long leastGeneration, + boolean restartOnRedeploy) { if (!Sets.intersection(componentConfigKeys, bootstrapKeys).isEmpty()) { throw new IllegalArgumentException( "Component config keys [" + componentConfigKeys + "] overlaps with bootstrap config keys [" + bootstrapKeys + "]"); @@ -70,16 +76,18 @@ public final class ConfigRetriever { allKeys.addAll(bootstrapKeys); setupComponentSubscriber(allKeys); - return getConfigsOptional(leastGeneration); + return getConfigsOptional(leastGeneration, restartOnRedeploy); } - private Optional<ConfigSnapshot> getConfigsOptional(long leastGeneration) { + private Optional<ConfigSnapshot> getConfigsOptional(long leastGeneration, boolean restartOnRedeploy) { long newestComponentGeneration = componentSubscriber.waitNextGeneration(); log.log(FINE, "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) { return Optional.empty(); + } else if (restartOnRedeploy && !componentSubscriber.internalRedeploy()) { // Don't reconfig - wait for restart + return Optional.empty(); } else if (bootstrapSubscriber.generation() < newestComponentGeneration) { long newestBootstrapGeneration = bootstrapSubscriber.waitNextGeneration(); log.log(FINE, "getConfigsOptional: new bootstrap generation: " + bootstrapSubscriber.generation()); diff --git a/container-di/src/main/java/com/yahoo/container/di/Container.java b/container-di/src/main/java/com/yahoo/container/di/Container.java index 42aa29af95c..af580767a17 100644 --- a/container-di/src/main/java/com/yahoo/container/di/Container.java +++ b/container-di/src/main/java/com/yahoo/container/di/Container.java @@ -71,10 +71,10 @@ public class Container { }); } - public ComponentGraph getNewComponentGraph(ComponentGraph oldGraph, Injector fallbackInjector) { + public ComponentGraph getNewComponentGraph(ComponentGraph oldGraph, Injector fallbackInjector, boolean restartOnRedeploy) { try { Collection<Bundle> obsoleteBundles = new HashSet<>(); - ComponentGraph newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, obsoleteBundles); + ComponentGraph newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, restartOnRedeploy, obsoleteBundles); newGraph.reuseNodes(oldGraph); constructComponents(newGraph); deconstructObsoleteComponents(oldGraph, newGraph, obsoleteBundles); @@ -87,11 +87,12 @@ public class Container { private ComponentGraph getConfigAndCreateGraph(ComponentGraph graph, Injector fallbackInjector, + boolean restartOnRedeploy, Collection<Bundle> obsoleteBundles) // NOTE: Return value { ConfigSnapshot snapshot; while (true) { - snapshot = configurer.getConfigs(graph.configKeys(), leastGeneration); + snapshot = configurer.getConfigs(graph.configKeys(), leastGeneration, restartOnRedeploy); log.log(FINE, String.format("createNewGraph:\n" + "graph.configKeys = %s\n" + "graph.generation = %s\n" + "snapshot = %s\n", graph.configKeys(), graph.generation(), snapshot)); 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/test/java/com/yahoo/container/di/ConfigRetrieverTest.java b/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java index 4751b9b74b7..e6b0309981a 100644 --- a/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java +++ b/container-di/src/test/java/com/yahoo/container/di/ConfigRetrieverTest.java @@ -51,7 +51,7 @@ public class ConfigRetrieverTest { public void require_that_bootstrap_configs_come_first() { writeConfigs(); ConfigRetriever retriever = createConfigRetriever(); - ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0); + ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false); assertThat(bootstrapConfigs, Matchers.instanceOf(BootstrapConfigs.class)); } @@ -61,7 +61,7 @@ public class ConfigRetrieverTest { public void require_that_components_comes_after_bootstrap() { writeConfigs(); ConfigRetriever retriever = createConfigRetriever(); - ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0); + ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false); ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId()); ConfigSnapshot componentsConfigs = retriever.getConfigs(Collections.singleton(testConfigKey), 0); @@ -73,6 +73,22 @@ public class ConfigRetrieverTest { } } + @Test + @SuppressWarnings("unused") + public void require_no_reconfig_when_restart_on_redeploy() { + // TODO + writeConfigs(); + ConfigRetriever retriever = createConfigRetriever(); + ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false); + + ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId()); + Optional<ConfigSnapshot> componentsConfigs = retriever.getConfigsOnce(Collections.singleton(testConfigKey), 0, true); + + if (componentsConfigs.isPresent()) { + fail("Expected no configs"); + } + } + @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -84,8 +100,8 @@ public class ConfigRetrieverTest { writeConfigs(); ConfigRetriever retriever = createConfigRetriever(); ConfigKey<? extends ConfigInstance> testConfigKey = new ConfigKey<>(TestConfig.class, dirConfigSource.configId()); - ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0); - ConfigSnapshot componentsConfigs = retriever.getConfigs(Collections.singleton(testConfigKey), 0); + ConfigSnapshot bootstrapConfigs = retriever.getConfigs(Collections.emptySet(), 0, false); + ConfigSnapshot componentsConfigs = retriever.getConfigs(Collections.singleton(testConfigKey), 0, false); Set<ConfigKey<? extends ConfigInstance>> keys = new HashSet<>(); keys.add(testConfigKey); keys.add(new ConfigKey<>(TestConfig.class, "")); diff --git a/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java b/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java index de6bd76bf45..19f277ff8fb 100644 --- a/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java +++ b/container-di/src/test/java/com/yahoo/container/di/ContainerTest.java @@ -401,11 +401,11 @@ public class ContainerTest extends ContainerTestBase { } ComponentGraph getNewComponentGraph(Container container, ComponentGraph oldGraph) { - return container.getNewComponentGraph(oldGraph, Guice.createInjector()); + return container.getNewComponentGraph(oldGraph, Guice.createInjector(), false); } ComponentGraph getNewComponentGraph(Container container) { - return container.getNewComponentGraph(new ComponentGraph(), Guice.createInjector()); + return container.getNewComponentGraph(new ComponentGraph(), Guice.createInjector(), false); } private ComponentTakingConfig createComponentTakingConfig(ComponentGraph componentGraph) { diff --git a/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java b/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java index 815865536f0..f1f3c4a2ae4 100644 --- a/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java +++ b/container-di/src/test/java/com/yahoo/container/di/ContainerTestBase.java @@ -70,7 +70,7 @@ public class ContainerTestBase { throw new UnsupportedOperationException("getBundle not supported."); } }); - componentGraph = container.getNewComponentGraph(componentGraph, Guice.createInjector()); + componentGraph = container.getNewComponentGraph(componentGraph, Guice.createInjector(), false); } catch (Exception e) { throw new RuntimeException(e); } 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 f32d7dfbb02..3158c06b0b1 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 @@ -261,12 +261,16 @@ public final class ConfiguredApplication implements Application { private void startReconfigurerThread() { reconfigurerThread = new Thread(() -> { + boolean restartOnDeploy = false; while ( ! Thread.interrupted()) { try { ContainerBuilder builder = createBuilderWithGuiceBindings(); + // Restart on deploy is sticky: Once it is set no future generation should be applied until restart + restartOnDeploy = restartOnDeploy || qrConfig.restartOnDeploy(); + // Block until new config arrives, and it should be applied - configurer.getNewComponentGraph(builder.guiceModules().activate()); + configurer.getNewComponentGraph(builder.guiceModules().activate(), restartOnDeploy); initializeAndActivateContainer(builder); } catch (ConfigInterruptedException e) { break; diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java index 7b23f190daa..d65b41c11c7 100644 --- a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java +++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java @@ -36,6 +36,9 @@ public class StandaloneSubscriberFactory implements SubscriberFactory { } @Override + public boolean internalRedeploy() { return false; } + + @Override public boolean configChanged() { return generation == 0; } |