diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config-proxy/src/test |
Publish
Diffstat (limited to 'config-proxy/src/test')
16 files changed, 1377 insertions, 0 deletions
diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CacheTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CacheTest.java new file mode 100644 index 00000000000..e4ddee9da97 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CacheTest.java @@ -0,0 +1,77 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.ConfigCacheKey; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.protocol.Payload; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Optional; + +/** + * Helper class for memory and disk cache unit tests + * + * @author musum + * @since 5.1.10 + */ +public class CacheTest { + String defName = "foo"; + String configId = "id"; + String namespace = "bar"; + String defMd5 = "a"; + + long generation = 1L; + String defName2 = "baz-quux"; + String namespace2 = "search.config"; + // Test with a config id with / in it + String configId2 = "clients/gateways/gateway/component/com.yahoo.feedhandler.VespaFeedHandlerRemoveLocation"; + String defMd52 = "a2"; + String differentDefMd5 = "09ef"; + String configMd5 = "b"; + ConfigKey<?> configKey = new ConfigKey<>(defName, configId, namespace); + ConfigKey<?> configKey2 = new ConfigKey<>(defName2, configId2, namespace2); + ConfigCacheKey cacheKey; + ConfigCacheKey cacheKeyDifferentMd5; + ConfigCacheKey cacheKey2; + RawConfig config; + RawConfig config2; + RawConfig configDifferentMd5; + RawConfig unknown; + Payload payload; + Payload payload2; + Payload payloadDifferentMd5; + + public CacheTest() { + } + + @Before + public void setup() { + ArrayList<String> defContent = new ArrayList<>(); + defContent.add("bar string"); + + Slime slime = new Slime(); + slime.setString("bar \"value\""); + payload = Payload.from(new ConfigPayload(slime)); + + slime = new Slime(); + slime.setString("bar \"baz\""); + payload2 = Payload.from(new ConfigPayload(slime)); + + slime = new Slime(); + 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()); + unknown = new RawConfig(new ConfigKey<>("unknown", configId, namespace), defMd5, payload, configMd5, generation, defContent, Optional.empty()); + configDifferentMd5 = new RawConfig(configKey, differentDefMd5, payloadDifferentMd5, configMd5, generation, defContent, Optional.empty()); + + cacheKey = new ConfigCacheKey(configKey, config.getDefMd5()); + cacheKey2 = new ConfigCacheKey(configKey2, config2.getDefMd5()); + cacheKeyDifferentMd5 = new ConfigCacheKey(configKey, differentDefMd5); + } +}
\ No newline at end of file diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CheckDelayedResponsesTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CheckDelayedResponsesTest.java new file mode 100644 index 00000000000..f96f584f257 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/CheckDelayedResponsesTest.java @@ -0,0 +1,45 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author musum + */ +public class CheckDelayedResponsesTest { + + private final MapBackedConfigSource source = new MapBackedConfigSource(UpstreamConfigSubscriberTest.MockClientUpdater.create()); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setup() { + source.clear(); + source.put(ProxyServerTest.fooConfig.getKey(), ProxyServerTest.createConfigWithNextConfigGeneration(ProxyServerTest.fooConfig, 0)); + } + + @Test + public void basic() { + ConfigTester tester = new ConfigTester(); + ConfigProxyStatistics statistics = new ConfigProxyStatistics(); + DelayedResponses delayedResponses = new DelayedResponses(statistics); + final MockRpcServer mockRpcServer = new MockRpcServer(); + final MemoryCache memoryCache = new MemoryCache(); + memoryCache.put(ProxyServerTest.fooConfig); + final CheckDelayedResponses checkDelayedResponses = new CheckDelayedResponses(delayedResponses, memoryCache, mockRpcServer); + delayedResponses.add(new DelayedResponse(tester.createRequest(ProxyServerTest.fooConfig, 0))); + delayedResponses.add(new DelayedResponse(tester.createRequest(ProxyServerTest.fooConfig, 1200000))); // should not be returned yet + delayedResponses.add(new DelayedResponse(tester.createRequest(ProxyServerTest.errorConfig, 0))); // will not give a config when resolving + checkDelayedResponses.checkDelayedResponses(); + + assertThat(mockRpcServer.responses, is(1L)); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ClientUpdaterTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ClientUpdaterTest.java new file mode 100644 index 00000000000..7aca5990a12 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ClientUpdaterTest.java @@ -0,0 +1,113 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.vespa.config.ConfigCacheKey; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.protocol.JRTConfigRequestFactory; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static junit.framework.TestCase.assertNull; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author musum + */ +public class ClientUpdaterTest { + private MockRpcServer rpcServer; + private ConfigProxyStatistics statistics; + private DelayedResponses delayedResponses; + private Mode mode; + private MemoryCache memoryCache; + private ClientUpdater clientUpdater; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + + @Before + public void setup() { + rpcServer = new MockRpcServer(); + statistics = new ConfigProxyStatistics(); + delayedResponses = new DelayedResponses(statistics); + mode = new Mode(); + memoryCache = new MemoryCache(); + clientUpdater = new ClientUpdater(CacheManager.createTestCacheManager(memoryCache), + rpcServer, + statistics, + delayedResponses, + mode); + } + + @Test + public void basic() { + assertThat(rpcServer.responses, is(0L)); + + final RawConfig fooConfig = ProxyServerTest.fooConfig; + clientUpdater.updateSubscribers(fooConfig); + + // No delayed response, so not returned + assertResponseAndCache(rpcServer, memoryCache, fooConfig, 0, 1); + + delayedResponses.add(new DelayedResponse(JRTServerConfigRequestV3.createFromRequest(JRTConfigRequestFactory.createFromRaw(fooConfig, -10L).getRequest()))); + clientUpdater.updateSubscribers(fooConfig); + assertResponseAndCache(rpcServer, memoryCache, fooConfig, 1, 1); + + // Will not find bar config in delayed responses + RawConfig barConfig = new RawConfig(new ConfigKey<>("bar", "id", "namespace"), fooConfig.getDefMd5()); + clientUpdater.updateSubscribers(barConfig); + assertResponseAndCache(rpcServer, memoryCache, barConfig, 1, 2); + + + mode = new Mode(Mode.ModeName.MEMORYCACHE.name()); + // Nothing should be returned, so still 1 response + assertResponseAndCache(rpcServer, memoryCache, fooConfig, 1, 2); + assertThat(statistics.errors(), is(0L)); + } + + @Test + public void memoryCacheMode() { + final RawConfig fooConfig = ProxyServerTest.fooConfig; + mode = new Mode(Mode.ModeName.MEMORYCACHE.name()); + clientUpdater = new ClientUpdater(CacheManager.createTestCacheManager(memoryCache), + rpcServer, + statistics, + delayedResponses, + mode); + memoryCache.clear(); + assertThat(rpcServer.responses, is(0L)); + + clientUpdater.updateSubscribers(fooConfig); + assertNull(memoryCache.get(new ConfigCacheKey(fooConfig.getKey(), fooConfig.getDefMd5()))); + assertThat(memoryCache.size(), is(0)); + assertThat(rpcServer.responses, is(0L)); + } + + @Test + public void errorResponse() { + assertThat(rpcServer.responses, is(0L)); + + final RawConfig errorConfig = ProxyServerTest.errorConfig; + + clientUpdater.updateSubscribers(errorConfig); + // Error response, so not put into cache + assertNull(memoryCache.get(new ConfigCacheKey(errorConfig.getKey(), errorConfig.getDefMd5()))); + assertThat(rpcServer.responses, is(0L)); + assertThat(statistics.errors(), is(1L)); + } + + private static void assertResponseAndCache(MockRpcServer rpcServer, + MemoryCache memoryCache, + RawConfig expectedConfig, + long expectedResponses, + int cacheSize) { + assertThat(rpcServer.responses, is(expectedResponses)); + assertThat(memoryCache.size(), is(cacheSize)); + assertThat(memoryCache.get(new ConfigCacheKey(expectedConfig.getKey(), expectedConfig.getDefMd5())), is(expectedConfig)); + } +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServerTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServerTest.java new file mode 100644 index 00000000000..fbef9cf1d17 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyRpcServerTest.java @@ -0,0 +1,253 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.config.subscription.ConfigSourceSet; +import com.yahoo.jrt.Request; +import com.yahoo.jrt.Spec; +import com.yahoo.jrt.StringValue; +import com.yahoo.vespa.config.RawConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class ConfigProxyRpcServerTest { + private static final String hostname = "localhost"; + private static final int port = 12345; + private static final String address = "tcp/" + hostname + ":" + port; + ProxyServer proxyServer; + private ConfigProxyRpcServer rpcServer; + + @Before + public void setup() { + proxyServer = ProxyServer.createTestServer(new ConfigSourceSet(address)); + rpcServer = new ConfigProxyRpcServer(proxyServer, null); + } + + @After + public void teardown() { + rpcServer.shutdown(); + } + + @Test + public void basic() { + ConfigSourceSet configSources = new ConfigSourceSet(); + ProxyServer proxy = ProxyServer.createTestServer(configSources); + Spec spec = new Spec("localhost", 12345); + ConfigProxyRpcServer server = new ConfigProxyRpcServer(proxy, spec); + assertThat(server.getSpec(), is(spec)); + } + + /** + * Tests ping RPC command + */ + @Test + public void testRpcMethodPing() { + Request req = new Request("ping"); + rpcServer.ping(req); + + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asInt32(), is(0)); + } + + /** + * Tests listCachedConfig RPC command + */ + @Test + public void testRpcMethodListCachedConfig() { + Request req = new Request("listCachedConfig"); + rpcServer.listCachedConfig(req); + + assertFalse(req.errorMessage(), req.isError()); + String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(req.returnValues().size(), is(1)); + assertThat(ret.length, is(0)); + + final RawConfig config = ProxyServerTest.fooConfig; + proxyServer.getCacheManager().putInCache(config); + req = new Request("listCachedConfig"); + rpcServer.listCachedConfig(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(1)); + assertThat(ret[0], is(config.getNamespace() + "." + config.getName() + "," + + config.getConfigId() + "," + + config.getGeneration() + "," + + config.getConfigMd5())); + } + + /** + * Tests listCachedConfig RPC command + */ + @Test + public void testRpcMethodListCachedConfigFull() { + Request req = new Request("listCachedConfigFull"); + rpcServer.listCachedConfigFull(req); + + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(0)); + + final RawConfig config = ProxyServerTest.fooConfig; + proxyServer.getCacheManager().putInCache(config); + req = new Request("listCachedConfigFull"); + rpcServer.listCachedConfigFull(req); + assertFalse(req.errorMessage(), req.isError()); + ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(1)); + assertThat(ret[0], is(config.getNamespace() + "." + config.getName() + "," + + config.getConfigId() + "," + + config.getGeneration() + "," + + config.getConfigMd5() + "," + + config.getPayload().getData())); + } + + /** + * Tests printStatistics RPC command + */ + @Test + public void testRpcMethodListSourceConnections() { + Request req = new Request("listSourceConnections"); + rpcServer.listSourceConnections(req); + + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + final String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(2)); + assertThat(ret[0], is("Current source: " + address)); + assertThat(ret[1], is("All sources:\n" + address + "\n")); + } + + /** + * Tests printStatistics RPC command + */ + @Test + public void testRpcMethodPrintStatistics() { + Request req = new Request("printStatistics"); + rpcServer.printStatistics(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is("\n" + + "Delayed responses queue size: 0\n" + + "Contents: ")); + } + + /** + * Tests invalidateCache RPC command + */ + @Test + public void testRpcMethodInvalidateCache() { + Request req = new Request("invalidateCache"); + rpcServer.invalidateCache(req); + + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + final String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(2)); + assertThat(ret[0], is("0")); + assertThat(ret[1], is("success")); + } + + /** + * Tests getMode and setMode RPC commands + */ + @Test + public void testRpcMethodGetModeAndSetMode() { + Request req = new Request("getMode"); + rpcServer.getMode(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is("default")); + + req = new Request("setMode"); + String mode = "memorycache"; + req.parameters().add(new StringValue(mode)); + rpcServer.setMode(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(2)); + assertThat(ret[0], is("0")); + assertThat(ret[1], is("success")); + assertThat(proxyServer.getMode().name(), is(mode)); + + req = new Request("getMode"); + rpcServer.getMode(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is(mode)); + + req = new Request("setMode"); + String oldMode = mode; + mode = "invalid"; + req.parameters().add(new StringValue(mode)); + rpcServer.setMode(req); + + assertFalse(req.errorMessage(), req.isError()); + ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(2)); + assertThat(ret[0], is("1")); + assertThat(ret[1], is("Could not set mode to '" + mode + "'. Legal modes are '" + Mode.modes() + "'")); + assertThat(proxyServer.getMode().name(), is(oldMode)); + } + + /** + * Tests updateSources RPC command + */ + @Test + public void testRpcMethodUpdateSources() { + Request req = new Request("updateSources"); + String spec1 = "tcp/a:19070"; + String spec2 = "tcp/b:19070"; + req.parameters().add(new StringValue(spec1 + "," + spec2)); + rpcServer.updateSources(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is("Updated config sources to: " + spec1 + "," + spec2)); + + + proxyServer.setMode(Mode.ModeName.MEMORYCACHE.name()); + + req = new Request("updateSources"); + req.parameters().add(new StringValue(spec1 + "," + spec2)); + rpcServer.updateSources(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is("Cannot update sources when in '" + Mode.ModeName.MEMORYCACHE.name().toLowerCase() + "' mode")); + + // TODO source connections needs to have deterministic order to work + /*req = new Request("listSourceConnections"); + rpcServer.listSourceConnections(req); + assertFalse(req.errorMessage(), req.isError()); + final String[] ret = req.returnValues().get(0).asStringArray(); + assertThat(ret.length, is(2)); + assertThat(ret[0], is("Current source: " + spec1)); + assertThat(ret[1], is("All sources:\n" + spec2 + "\n" + spec1 + "\n")); + */ + } + + /** + * Tests dumpCache RPC command + */ + @Test + public void testRpcMethodDumpCache() { + Request req = new Request("dumpCache"); + String path = "/tmp"; + req.parameters().add(new StringValue(path)); + rpcServer.dumpCache(req); + assertFalse(req.errorMessage(), req.isError()); + assertThat(req.returnValues().size(), is(1)); + assertThat(req.returnValues().get(0).asString(), is("success")); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyStatisticsTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyStatisticsTest.java new file mode 100644 index 00000000000..8f1529142af --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigProxyStatisticsTest.java @@ -0,0 +1,46 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class ConfigProxyStatisticsTest { + + @Test + public void basic() { + ConfigProxyStatistics statistics = new ConfigProxyStatistics(); + assertThat(statistics.getEventInterval(), is(ConfigProxyStatistics.defaultEventInterval)); + assertThat(statistics.processedRequests(), is(0L)); + assertThat(statistics.errors(), is(0L)); + assertThat(statistics.delayedResponses(), is(0L)); + + statistics.delayedResponses(1); + statistics.incProcessedRequests(); + statistics.incRpcRequests(); + statistics.incErrorCount(); + + assertThat(statistics.processedRequests(), is(1L)); + assertThat(statistics.rpcRequests(), is(1L)); + assertThat(statistics.errors(), is(1L)); + assertThat(statistics.delayedResponses(), is(1L)); + + statistics.decDelayedResponses(); + assertThat(statistics.delayedResponses(), is(0L)); + + + Long eventInterval = 1L; + statistics = new ConfigProxyStatistics(eventInterval); + assertThat(statistics.getEventInterval(), is(eventInterval)); + + Thread t = new Thread(statistics); + t.start(); + + statistics.stop(); + } +} 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 new file mode 100644 index 00000000000..187a9f0726f --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ConfigTester.java @@ -0,0 +1,49 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.jrt.Request; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.protocol.CompressionType; +import com.yahoo.vespa.config.protocol.DefContent; +import com.yahoo.vespa.config.protocol.JRTClientConfigRequestV3; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3; +import com.yahoo.vespa.config.protocol.Trace; + +import java.util.Collections; +import java.util.Optional; + +/** + * @author bratseth + */ +public class ConfigTester { + + private final long defaultTimeout = 10000; + + public JRTServerConfigRequest createRequest(RawConfig config) { + return createRequest(config, defaultTimeout); + } + + public JRTServerConfigRequest createRequest(RawConfig config, long timeout) { + return createRequest(config.getName(), config.getConfigId(), config.getNamespace(), + config.getConfigMd5(), config.getDefMd5(), config.getGeneration(), timeout); + } + + public JRTServerConfigRequest createRequest(String configName, String configId, String namespace) { + return createRequest(configName, configId, namespace, defaultTimeout); + } + + public JRTServerConfigRequest createRequest(String configName, String configId, String namespace, long timeout) { + return createRequest(configName, configId, namespace, "", null, 0, timeout); + } + + public JRTServerConfigRequest createRequest(String configName, String configId, String namespace, String md5, String defMd5, long generation, long timeout) { + Request request = JRTClientConfigRequestV3. + createWithParams(new ConfigKey<>(configName, configId, namespace, defMd5, null), DefContent.fromList(Collections.emptyList()), + "fromHost", md5, generation, timeout, Trace.createDummy(), CompressionType.UNCOMPRESSED, + Optional.empty()).getRequest(); + return JRTServerConfigRequestV3.createFromRequest(request); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponseTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponseTest.java new file mode 100644 index 00000000000..c81846d78d8 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponseTest.java @@ -0,0 +1,83 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; +import org.junit.Test; + +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.11 + */ +public class DelayedResponseTest { + + private static final String configId = "id"; + private static final String namespace = "bar"; + + @Test + public void basic() { + ConfigTester tester = new ConfigTester(); + final long returnTime = System.currentTimeMillis(); + final long timeout = 1; + final String configName = "foo"; + final JRTServerConfigRequest request = tester.createRequest(configName, configId, namespace, timeout); + DelayedResponse delayedResponse = new DelayedResponse(request, returnTime); + assertThat(delayedResponse.getRequest(), is(request)); + assertThat(delayedResponse.getReturnTime(), is(returnTime)); + assertTrue(delayedResponse.getDelay(TimeUnit.SECONDS) < returnTime); + + DelayedResponse before = new DelayedResponse(request, returnTime - 1000L); + DelayedResponse after = new DelayedResponse(request, returnTime + 1000L); + + assertThat(delayedResponse.compareTo(delayedResponse), is(0)); + assertThat(delayedResponse.compareTo(before), is(1)); + assertThat(delayedResponse.compareTo(after), is(-1)); + assertThat(delayedResponse.compareTo(new Delayed() { + @Override + public long getDelay(TimeUnit unit) { + return 0; + } + + @Override + public int compareTo(Delayed o) { + return 0; + } + }), is(0)); + } + + @Test + public void testDelayedResponse() { + ConfigTester tester = new ConfigTester(); + final long timeout = 20000; + JRTServerConfigRequest request1 = tester.createRequest("baz", configId, namespace, timeout); + DelayedResponse delayed1 = new DelayedResponse(request1); + assertTrue(delayed1.getReturnTime() > System.currentTimeMillis()); + assertTrue(delayed1.getDelay(TimeUnit.MILLISECONDS) > 0); + assertTrue(delayed1.getDelay(TimeUnit.MILLISECONDS) <= timeout); + + // Just to make sure we do not get requests within the same millisecond + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // New request, should have larger delay than the first + JRTServerConfigRequest request2 = tester.createRequest("baz", configId, namespace, timeout); + DelayedResponse delayed2 = new DelayedResponse(request2); + assertTrue("delayed1=" + delayed1.getReturnTime() + ", delayed2=" + + delayed2.getReturnTime() + ": delayed2 should be greater than delayed1", + delayed2.getReturnTime() > delayed1.getReturnTime()); + + // Test compareTo() method + assertThat(delayed1.compareTo(delayed1), is(0)); + assertThat(delayed1.compareTo(delayed2), is(-1)); + assertThat(delayed2.compareTo(delayed1), is(1)); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponsesTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponsesTest.java new file mode 100644 index 00000000000..701ca959ee8 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/DelayedResponsesTest.java @@ -0,0 +1,32 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class DelayedResponsesTest { + + @Test + public void basic() throws InterruptedException { + ConfigTester tester = new ConfigTester(); + DelayedResponses responses = new DelayedResponses(new ConfigProxyStatistics()); + DelayedResponse delayedResponse = new DelayedResponse(tester.createRequest("foo", "id", "bar")); + responses.add(delayedResponse); + + assertThat(responses.size(), is(1)); + assertThat(responses.responses().take(), is(delayedResponse)); + assertThat(responses.size(), is(0)); + + responses.add(delayedResponse); + assertThat(responses.size(), is(1)); + responses.remove(delayedResponse); + assertThat(responses.size(), is(0)); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/Helper.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/Helper.java new file mode 100644 index 00000000000..dc2b667f823 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/Helper.java @@ -0,0 +1,58 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.*; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; +import com.yahoo.vespa.config.protocol.Payload; +import com.yahoo.vespa.config.util.ConfigUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class Helper { + + static final long serverTimeout = 100000; + static RawConfig fooConfig; + static RawConfig fooConfigV2; + static RawConfig barConfig; + static Payload fooConfigPayload; + + static JRTServerConfigRequest fooConfigRequest; + static JRTServerConfigRequest barConfigRequest; + + static ConfigCacheKey fooConfigConfigCacheKey; + static ConfigCacheKey barConfigConfigCacheKey; + + static { + ConfigTester tester = new ConfigTester(); + String defName = "foo"; + String configId = "id"; + String namespace = "bar"; + ConfigKey<?> configKey = new ConfigKey<>(defName, configId, namespace); + Payload payloadV1 = Payload.from("bar \"value\""); + Slime slime = new Slime(); + slime.setString("bar \"value\""); + fooConfigPayload = Payload.from(new ConfigPayload(slime)); + + List<String> defContent = Collections.singletonList("bar string"); + long generation = 1; + String defMd5 = ConfigUtils.getDefMd5(defContent); + String configMd5 = "5752ad0f757d7e711e32037f29940b73"; + fooConfig = new RawConfig(configKey, defMd5, payloadV1, configMd5, generation, defContent, Optional.empty()); + fooConfigV2 = new RawConfig(configKey, defMd5, fooConfigPayload, configMd5, generation, defContent, Optional.empty()); + fooConfigRequest = tester.createRequest(defName, configId, namespace, serverTimeout); + fooConfigConfigCacheKey = new ConfigCacheKey(fooConfig.getKey(), fooConfig.getDefMd5()); + + String defName2 = "bar"; + barConfig = new RawConfig(new ConfigKey<>(defName2, configId, namespace), defMd5, payloadV1, configMd5, generation, defContent, Optional.empty()); + barConfigRequest = tester.createRequest(defName2, configId, namespace, serverTimeout); + barConfigConfigCacheKey = new ConfigCacheKey(barConfig.getKey(), barConfig.getDefMd5()); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheConfigClientTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheConfigClientTest.java new file mode 100644 index 00000000000..b59d8710f36 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheConfigClientTest.java @@ -0,0 +1,29 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import org.junit.Test; + +import java.util.Collections; + +import static junit.framework.TestCase.assertNull; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class MemoryCacheConfigClientTest { + + @Test + public void basic() { + MemoryCache cache = new MemoryCache(); + cache.put(Helper.fooConfig); + MemoryCacheConfigClient client = new MemoryCacheConfigClient(cache); + assertThat(client.getConfig(Helper.fooConfig, null), is(Helper.fooConfig)); + assertNull(client.getConfig(Helper.barConfig, null)); + + assertThat(client.getActiveSourceConnection(), is("N/A")); + assertThat(client.getSourceConnections(), is(Collections.singletonList("N/A"))); + } +} 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 new file mode 100644 index 00000000000..3707217b9c6 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MemoryCacheTest.java @@ -0,0 +1,65 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.vespa.config.RawConfig; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class MemoryCacheTest extends CacheTest { + + @Test + public void basic() { + MemoryCache cache = new MemoryCache(); + + cache.put(config); + cache.put(config2); + assertThat(cache.size(), is(2)); + assertTrue(cache.containsKey(cacheKey)); + assertTrue(cache.containsKey(cacheKey2)); + + RawConfig response = cache.get(cacheKey); + assertNotNull(response); + assertThat(response.getName(), is(defName)); + assertThat(response.getPayload().toString(), is(payload.toString())); + assertThat(response.getGeneration(), is(generation)); + + response = cache.get(cacheKey2); + assertNotNull(response); + assertThat(response.getName(), is(defName2)); + assertThat(response.getPayload().toString(), is(payload2.toString())); + assertThat(response.getGeneration(), is(generation)); + + cache.clear(); + } + + @Test + public void testSameConfigNameDifferentMd5() { + MemoryCache cache = new MemoryCache(); + + cache.put(config); + cache.put(configDifferentMd5); // same name, different defMd5 + assertThat(cache.size(), is(2)); + assertTrue(cache.containsKey(cacheKey)); + + RawConfig response = cache.get(cacheKey); + assertNotNull(response); + assertThat(response.getName(), is(defName)); + assertThat(response.getPayload().getData(), is(payload.getData())); + assertThat(response.getGeneration(), is(generation)); + + response = cache.get(cacheKeyDifferentMd5); + assertNotNull(response); + assertThat(response.getName(), is(defName)); + assertThat(response.getPayload().getData(), is(payloadDifferentMd5.getData())); + assertThat(response.getGeneration(), is(generation)); + + cache.clear(); + assertThat(cache.size(), is(0)); + } +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockConnection.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockConnection.java new file mode 100644 index 00000000000..15f00cea18a --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockConnection.java @@ -0,0 +1,86 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.jrt.Request; +import com.yahoo.jrt.RequestWaiter; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3; +import com.yahoo.vespa.config.protocol.Payload; +import com.yahoo.vespa.config.util.ConfigUtils; + +/** + * For unit testing + * + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.11 + */ +public class MockConnection extends com.yahoo.config.subscription.impl.MockConnection { + + public MockConnection(MapBackedConfigSource configSource) { + this(new ProxyResponseHandler(configSource)); + } + + public MockConnection(ResponseHandler responseHandler) { + super(responseHandler); + } + + static class ProxyResponseHandler implements ResponseHandler { + private RequestWaiter requestWaiter; + private Request request; + private final MapBackedConfigSource configSource; + + protected ProxyResponseHandler(MapBackedConfigSource configSource) { + this.configSource = configSource; + } + + @Override + public RequestWaiter requestWaiter() { + return requestWaiter; + } + + @Override + public Request request() { + return request; + } + + @Override + public ResponseHandler requestWaiter(RequestWaiter requestWaiter) { + this.requestWaiter = requestWaiter; + return this; + } + + @Override + public ResponseHandler request(Request request) { + this.request = request; + return this; + } + + @Override + public void run() { + if (request.isError()) { + System.out.println("Returning error response"); + createErrorResponse(); + } else { + System.out.println("Returning OK response"); + createOkResponse(); + } + requestWaiter.handleRequestDone(request); + } + + protected void createOkResponse() { + JRTServerConfigRequestV3 jrtReq = JRTServerConfigRequestV3.createFromRequest(request); + long generation = 1; + RawConfig config = configSource.getConfig(jrtReq.getConfigKey()); + if (config == null || config.getPayload() == null) { + throw new RuntimeException("No config for " + jrtReq.getConfigKey() + " found"); + } + Payload payload = config.getPayload(); + jrtReq.addOkResponse(payload, generation, ConfigUtils.getMd5(payload.getData())); + } + + protected void createErrorResponse() { + JRTServerConfigRequestV3 jrtReq = JRTServerConfigRequestV3.createFromRequest(request); + jrtReq.addErrorResponse(request.errorCode(), request.errorMessage()); + } + } +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockRpcServer.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockRpcServer.java new file mode 100644 index 00000000000..ed41fbf0e26 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/MockRpcServer.java @@ -0,0 +1,23 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; + +/** + * @author musum + */ +public class MockRpcServer implements RpcServer { + + volatile long responses = 0; + volatile long errorResponses = 0; + + public void returnOkResponse(JRTServerConfigRequest request, RawConfig config) { + responses++; + } + + public void returnErrorResponse(JRTServerConfigRequest request, int errorCode, String message) { + responses++; + errorResponses++; + } +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java new file mode 100644 index 00000000000..c068a531a80 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ModeTest.java @@ -0,0 +1,61 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author musum + */ +public class ModeTest { + + @Test + public void basic() { + Mode mode = new Mode(); + assertModeName(Mode.ModeName.DEFAULT, mode); + assertTrue(mode.isDefault()); + + mode = new Mode(""); + assertModeName(Mode.ModeName.DEFAULT, mode); + assertTrue(mode.isDefault()); + + mode = new Mode(Mode.ModeName.DEFAULT.name()); + assertModeName(Mode.ModeName.DEFAULT, mode); + assertTrue(mode.isDefault()); + + mode = new Mode(Mode.ModeName.MEMORYCACHE.name()); + assertModeName(Mode.ModeName.MEMORYCACHE, mode); + assertTrue(mode.isMemoryCache()); + + assertTrue(new Mode(Mode.ModeName.DEFAULT.name()).requiresConfigSource()); + + assertFalse(new Mode(Mode.ModeName.MEMORYCACHE.name()).requiresConfigSource()); + + Set<String> modes = new HashSet<>(); + for (Mode.ModeName modeName : Mode.ModeName.values()) { + modes.add(modeName.name().toLowerCase()); + } + + assertThat(Mode.modes(), is(modes)); + + assertFalse(Mode.validModeName("foo")); + + assertThat(mode.toString(), is(Mode.ModeName.MEMORYCACHE.name().toLowerCase())); + } + + @Test(expected = IllegalArgumentException.class) + public void failWhenInvalidMode() { + new Mode("invalid_mode"); + } + + private void assertModeName(Mode.ModeName expected, Mode actual) { + assertThat(actual.name(), is(expected.name().toLowerCase())); + } +} 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 new file mode 100644 index 00000000000..3ed876d547f --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/ProxyServerTest.java @@ -0,0 +1,184 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.config.subscription.ConfigSourceSet; +import com.yahoo.config.subscription.RawSource; +import com.yahoo.vespa.config.*; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class ProxyServerTest { + + private final MapBackedConfigSource source = new MapBackedConfigSource(UpstreamConfigSubscriberTest.MockClientUpdater.create()); + private ProxyServer proxy = ProxyServer.createTestServer(source); + + static final RawConfig fooConfig = Helper.fooConfigV2; + + // errorConfig based on fooConfig + 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()); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setup() { + source.clear(); + source.put(fooConfig.getKey(), createConfigWithNextConfigGeneration(fooConfig, 0)); + source.put(errorConfigKey, createConfigWithNextConfigGeneration(fooConfig, ErrorCode.UNKNOWN_DEFINITION)); + proxy = ProxyServer.createTestServer(source); + } + + @After + public void shutdown() { + proxy.stop(); + } + + @Test + public void basic() { + assertTrue(proxy.getMode().isDefault()); + assertThat(proxy.getCacheManager().getMemoryCache().size(), is(0)); + assertThat(proxy.getTimingValues(), is(ProxyServer.defaultTimingValues())); + } + + /** + * Tests that the proxy server RPC commands for setting and getting mode works.. + */ + @Test + public void testModeSwitch() { + ConfigSourceSet source = new ConfigSourceSet(); // Need to use a ConfigSourceSet to test modes + ProxyServer proxy = ProxyServer.createTestServer(source); + assertTrue(proxy.getMode().isDefault()); + + for (String mode : Mode.modes()) { + proxy.setMode(mode); + assertThat(proxy.getMode().name(), is(mode)); + } + + // Also switch to DEFAULT mode, as that is not covered above + proxy.setMode("default"); + assertTrue(proxy.getMode().isDefault()); + + // Set mode to the same as the current mode + proxy.setMode(proxy.getMode().name()); + assertTrue(proxy.getMode().isDefault()); + + proxy.stop(); + } + + /** + * Tests that the proxy server can be tested with a MapBackedConfigSource, + * which is a simple hash map with configs + */ + @Test + public void testRawConfigSetBasics() { + ConfigTester tester = new ConfigTester(); + JRTServerConfigRequest errorConfigRequest = tester.createRequest(errorConfig); + + assertTrue(proxy.getMode().isDefault()); + RawConfig config = proxy.resolveConfig(Helper.fooConfigRequest); + assertThat(config, is(createConfigWithNextConfigGeneration(Helper.fooConfig, 0))); + + config = proxy.resolveConfig(Helper.barConfigRequest); + assertNull(config); + + config = proxy.resolveConfig(errorConfigRequest); + assertThat(config.errorCode(), is(ErrorCode.UNKNOWN_DEFINITION)); + } + + /** + * Verifies that config is retrieved from the real server when it is not found in the cache, + * that the cache is populated with the config and that the entry in the cache is used + * when it is found there. + */ + @Test + public void testGetConfigAndCaching() { + ConfigTester tester = new ConfigTester(); + final CacheManager cacheManager = proxy.getCacheManager(); + assertEquals(0, cacheManager.getCacheSize()); + RawConfig res = proxy.resolveConfig(tester.createRequest(fooConfig)); + assertNotNull(res); + assertThat(res.getPayload().toString(), is(Helper.fooConfigPayload.toString())); + assertEquals(1, cacheManager.getCacheSize()); + assertThat(cacheManager.getMemoryCache().get(new ConfigCacheKey(fooConfig.getKey(), fooConfig.getDefMd5())), is(res)); + + // Trying same config again + JRTServerConfigRequest newRequestBasedOnResponse = tester.createRequest(res); + RawConfig res2 = proxy.resolveConfig(newRequestBasedOnResponse); + assertFalse(ProxyServer.configOrGenerationHasChanged(res2, newRequestBasedOnResponse)); + assertEquals(1, cacheManager.getCacheSize()); + } + + /** + * Verifies that error responses are not cached. When the config has been successfully retrieved, + * it must be put in the cache. + */ + @Test + public void testNoCachingOfErrorRequests() { + ConfigTester tester = new ConfigTester(); + // Simulate an error response + source.put(fooConfig.getKey(), createConfigWithNextConfigGeneration(fooConfig, ErrorCode.INTERNAL_ERROR)); + + final CacheManager cacheManager = proxy.getCacheManager(); + assertEquals(0, cacheManager.getCacheSize()); + + RawConfig res = proxy.resolveConfig(tester.createRequest(fooConfig)); + assertNotNull(res); + assertNotNull(res.getPayload()); + assertTrue(res.isError()); + assertEquals(0, cacheManager.getCacheSize()); + + // Put a version of the same config into backend without error and see that it now works (i.e. we are + // not getting a cached response (of the error in the previous request) + source.put(fooConfig.getKey(), createConfigWithNextConfigGeneration(fooConfig, 0)); + + // Verify that we get the config now and that it is cached + res = proxy.resolveConfig(tester.createRequest(fooConfig)); + assertNotNull(res); + assertNotNull(res.getPayload().getData()); + assertThat(res.getPayload().toString(), is(Helper.fooConfigPayload.toString())); + assertEquals(1, cacheManager.getCacheSize()); + + JRTServerConfigRequest newRequestBasedOnResponse = tester.createRequest(res); + RawConfig res2 = proxy.resolveConfig(newRequestBasedOnResponse); + assertFalse(ProxyServer.configOrGenerationHasChanged(res2, newRequestBasedOnResponse)); + assertEquals(1, cacheManager.getCacheSize()); + } + + @Test + public void testReadingSystemProperties() { + ProxyServer.Properties properties = ProxyServer.getSystemProperties(); + assertThat(properties.eventInterval, is(ConfigProxyStatistics.defaultEventInterval)); + assertThat(properties.mode, is(Mode.ModeName.DEFAULT.name())); + assertThat(properties.configSources.length, is(1)); + assertThat(properties.configSources[0], is(ProxyServer.DEFAULT_PROXY_CONFIG_SOURCES)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIllegalConfigSource() { + RawSource source = new RawSource("bar 1"); + proxy = ProxyServer.createTestServer(source); + } + + static RawConfig createConfigWithNextConfigGeneration(RawConfig config, int errorCode) { + return new RawConfig(config.getKey(), config.getDefMd5(), + config.getPayload(), config.getConfigMd5(), + config.getGeneration() + 1, errorCode, config.getDefContent(), Optional.empty()); + } + +} diff --git a/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriberTest.java b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriberTest.java new file mode 100644 index 00000000000..695c4068155 --- /dev/null +++ b/config-proxy/src/test/java/com/yahoo/vespa/config/proxy/UpstreamConfigSubscriberTest.java @@ -0,0 +1,173 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.proxy; + +import com.yahoo.config.subscription.ConfigSourceSet; +import com.yahoo.config.subscription.impl.JRTConfigRequester; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.*; +import com.yahoo.vespa.config.protocol.*; +import com.yahoo.vespa.config.util.ConfigUtils; +import org.junit.*; +import org.junit.rules.TemporaryFolder; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +/** + * @author musum + */ +public class UpstreamConfigSubscriberTest { + private final ConfigSourceSet sourceSet = new ConfigSourceSet("tcp/foo:78"); + private final TimingValues timingValues = ProxyServer.defaultTimingValues(); + + private MapBackedConfigSource sourceResponses; + private MockClientUpdater clientUpdater; + private MockConnection mockConnection; + static RawConfig fooConfig; + static RawConfig errorConfig; + static ConfigKey<?> errorConfigKey; + static Payload fooPayload; + static Payload errorPayload; + long generation = 1; + + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setup() { + clientUpdater = MockClientUpdater.create(); + sourceResponses = new MapBackedConfigSource(clientUpdater); + + ConfigPayload payload = getConfigPayload("bar", "value"); + fooPayload = Payload.from(payload); + fooConfig = new RawConfig(Helper.fooConfig.getKey(), Helper.fooConfig.getDefMd5(), fooPayload, ConfigUtils.getMd5(payload), generation, 0, Helper.fooConfig.getDefContent(), Optional.empty()); + + payload = new ConfigPayload(new Slime()); + errorPayload = Payload.from(payload); + errorConfigKey = new ConfigKey<>("error", fooConfig.getConfigId(), fooConfig.getNamespace()); + errorConfig = new RawConfig(errorConfigKey, fooConfig.getDefMd5(), errorPayload, ConfigUtils.getMd5(payload), generation, ErrorCode.UNKNOWN_DEFINITION, fooConfig.getDefContent(), Optional.empty()); + + sourceResponses.clear(); + sourceResponses.put(fooConfig.getKey(), fooConfig); + + mockConnection = new MockConnection(sourceResponses); + } + + private ConfigPayload getConfigPayload(String key, String value) { + Slime slime = new Slime(); + slime.setObject().setString(key, value); + return new ConfigPayload(slime); + } + + @Test + public void basic() { + final UpstreamConfigSubscriber subscriber = createUpstreamConfigSubscriber(fooConfig); + new Thread(subscriber).start(); + waitForConfigGeneration(clientUpdater, generation); + assertThat(clientUpdater.getLastConfig(), is(fooConfig)); + subscriber.cancel(); + } + + @Test + public void require_that_reconfiguration_works() { + final UpstreamConfigSubscriber subscriber = createUpstreamConfigSubscriber(fooConfig); + + new Thread(subscriber).start(); + waitForConfigGeneration(clientUpdater, generation); + assertThat(clientUpdater.getLastConfig(), is(fooConfig)); + + // Add updated config + generation++; + final ConfigPayload payload = getConfigPayload("bar", "value2"); + fooPayload = Payload.from(payload); + RawConfig fooConfig2 = new RawConfig(fooConfig.getKey(), fooConfig.getDefMd5(), fooPayload, ConfigUtils.getMd5(payload), generation, fooConfig.getDefContent(), Optional.empty()); + sourceResponses.put(fooConfig2.getKey(), fooConfig2); + + waitForConfigGeneration(clientUpdater, generation); + assertThat(clientUpdater.getLastConfig(), is(not(fooConfig))); + subscriber.cancel(); + } + + @Test + public void require_that_error_response_is_handled() { + sourceResponses.put(errorConfigKey, errorConfig); + final UpstreamConfigSubscriber subscriber = createUpstreamConfigSubscriber(fooConfig); + + new Thread(subscriber).start(); + waitForConfigGeneration(clientUpdater, generation); + RawConfig lastConfig = clientUpdater.getLastConfig(); + assertThat(lastConfig, is(errorConfig)); + assertThat(lastConfig.errorCode(), is(ErrorCode.UNKNOWN_DEFINITION)); + subscriber.cancel(); + } + + private UpstreamConfigSubscriber createUpstreamConfigSubscriber(RawConfig config) { + return new UpstreamConfigSubscriber(config, clientUpdater, sourceSet, timingValues, createRequesterPool()); + } + + private Map<ConfigSourceSet, JRTConfigRequester> createRequesterPool() { + JRTConfigRequester request = JRTConfigRequester.get(mockConnection, timingValues); + + Map<ConfigSourceSet, JRTConfigRequester> requesterPool = new LinkedHashMap<>(); + requesterPool.put(sourceSet, request); + return requesterPool; + } + + private void waitForConfigGeneration(MockClientUpdater clientUpdater, long expectedGeneration) { + int i = 0; + RawConfig lastConfig; + do { + lastConfig = clientUpdater.getLastConfig(); + if (lastConfig != null) { + System.out.println("i=" + i + ", config=" + lastConfig + ",generation=" + lastConfig.getGeneration()); + } + if (lastConfig != null && lastConfig.getGeneration() == expectedGeneration) { + break; + } else { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + } + i++; + } while (i < 1000); + assertNotNull(lastConfig); + assertThat(lastConfig.getGeneration(), is(expectedGeneration)); + } + + static class MockClientUpdater extends ClientUpdater { + private RawConfig lastConfig; + + private MockClientUpdater(ConfigProxyStatistics statistics, Mode mode) { + super(CacheManager.createTestCacheManager(), + new MockRpcServer(), + statistics, + new DelayedResponses(statistics), + mode); + } + + public static MockClientUpdater create() { + Mode mode = new Mode(); + ConfigProxyStatistics statistics = new ConfigProxyStatistics(); + return new MockClientUpdater(statistics, mode); + } + + @Override + public void updateSubscribers(RawConfig newConfig) { + lastConfig = newConfig; + } + + RawConfig getLastConfig() { + return lastConfig; + } + } +} |