diff options
Diffstat (limited to 'container-search/src')
6 files changed, 162 insertions, 33 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java index ddb8a392730..786a0d0e04f 100644 --- a/container-search/src/main/java/com/yahoo/search/Query.java +++ b/container-search/src/main/java/com/yahoo/search/Query.java @@ -1,6 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search; +import ai.vespa.cloud.Environment; +import ai.vespa.cloud.Zone; +import ai.vespa.cloud.ZoneInfo; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.yahoo.collections.Tuple2; @@ -334,22 +337,30 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { public Query(HttpRequest request, Map<String, String> requestMap, CompiledQueryProfile queryProfile) { super(new QueryPropertyAliases(propertyAliases)); this.httpRequest = request; - init(requestMap, queryProfile, Embedder.throwsOnUse); + init(requestMap, queryProfile, Embedder.throwsOnUse, ZoneInfo.defaultInfo()); } // TODO: Deprecate most constructors above here private Query(Builder builder) { - this(builder.getRequest(), builder.getRequestMap(), builder.getQueryProfile(), builder.getEmbedder()); + this(builder.getRequest(), + builder.getRequestMap(), + builder.getQueryProfile(), + builder.getEmbedder(), + builder.getZoneInfo()); } - private Query(HttpRequest request, Map<String, String> requestMap, CompiledQueryProfile queryProfile, Embedder embedder) { + private Query(HttpRequest request, Map<String, String> requestMap, CompiledQueryProfile queryProfile, Embedder embedder, + ZoneInfo zoneInfo) { super(new QueryPropertyAliases(propertyAliases)); this.httpRequest = request; - init(requestMap, queryProfile, embedder); + init(requestMap, queryProfile, embedder, zoneInfo); } - private void init(Map<String, String> requestMap, CompiledQueryProfile queryProfile, Embedder embedder) { + private void init(Map<String, String> requestMap, + CompiledQueryProfile queryProfile, + Embedder embedder, + ZoneInfo zoneInfo) { startTime = httpRequest.getJDiscRequest().creationTime(TimeUnit.MILLISECONDS); if (queryProfile != null) { // Move all request parameters to the query profile just to validate that the parameter settings are legal @@ -361,7 +372,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { // Create the full chain properties().chain(new QueryProperties(this, queryProfile.getRegistry(), embedder)). chain(new ModelObjectMap()). - chain(new RequestContextProperties(requestMap)). + chain(new RequestContextProperties(requestMap, zoneInfo)). chain(queryProfileProperties). chain(new DefaultProperties()); @@ -1131,6 +1142,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { private Map<String, String> requestMap = null; private CompiledQueryProfile queryProfile = null; private Embedder embedder = Embedder.throwsOnUse; + private ZoneInfo zoneInfo = ZoneInfo.defaultInfo(); public Builder setRequest(String query) { request = HttpRequest.createTestRequest(query, com.yahoo.jdisc.http.HttpRequest.Method.GET); @@ -1175,6 +1187,13 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { public Embedder getEmbedder() { return embedder; } + public Builder setZoneInfo(ZoneInfo zoneInfo) { + this.zoneInfo = zoneInfo; + return this; + } + + public ZoneInfo getZoneInfo() { return zoneInfo; } + /** Creates a new query from this builder. No properties are required to before calling this. */ public Query build() { return new Query(this); } diff --git a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java index 739058ade71..8925c647ad2 100644 --- a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java +++ b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java @@ -1,6 +1,8 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.handler; +import ai.vespa.cloud.Environment; +import ai.vespa.cloud.Zone; import com.google.inject.Inject; import com.yahoo.collections.Tuple2; import com.yahoo.component.ComponentSpecification; @@ -53,6 +55,7 @@ import com.yahoo.statistics.Statistics; import com.yahoo.vespa.configdefinition.SpecialtokensConfig; import com.yahoo.yolean.Exceptions; import com.yahoo.yolean.trace.TraceNode; +import ai.vespa.cloud.ZoneInfo; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -70,6 +73,7 @@ import java.util.logging.Logger; * Handles search request. * * @author Steinar Knutsen + * @author bratseth */ public class SearchHandler extends LoggingRequestHandler { @@ -103,22 +107,46 @@ public class SearchHandler extends LoggingRequestHandler { private final ExecutionFactory executionFactory; private final AtomicLong numRequestsLeftToTrace; + private final ZoneInfo zoneInfo; + private final static RequestHandlerSpec REQUEST_HANDLER_SPEC = RequestHandlerSpec.builder() .withAclMapping(SearchHandler.aclRequestMapper()).build(); + @Inject public SearchHandler(Metric metric, ContainerThreadPool threadpool, CompiledQueryProfileRegistry queryProfileRegistry, ContainerHttpConfig config, Embedder embedder, + ExecutionFactory executionFactory, + ZoneInfo zoneInfo) { + this(metric, threadpool.executor(), queryProfileRegistry, embedder, executionFactory, + config.numQueriesToTraceOnDebugAfterConstruction(), + config.hostResponseHeaderKey().equals("") ? Optional.empty() : Optional.of(config.hostResponseHeaderKey()), + zoneInfo); + } + + /** + * @deprecated Use the @Inject annotated constructor instead. + */ + @Deprecated // Vespa 8 + public SearchHandler(Metric metric, + ContainerThreadPool threadpool, + CompiledQueryProfileRegistry queryProfileRegistry, + ContainerHttpConfig config, + Embedder embedder, ExecutionFactory executionFactory) { this(metric, threadpool.executor(), queryProfileRegistry, embedder, executionFactory, - config.numQueriesToTraceOnDebugAfterConstruction(), - config.hostResponseHeaderKey().equals("") ? Optional.empty() : Optional.of(config.hostResponseHeaderKey())); + config.numQueriesToTraceOnDebugAfterConstruction(), + config.hostResponseHeaderKey().equals("") ? Optional.empty() : Optional.of(config.hostResponseHeaderKey()), + ZoneInfo.defaultInfo()); } - @Deprecated + /** + * @deprecated Use the @Inject annotated constructor instead. + */ + @Deprecated // Vespa 8 public SearchHandler(Statistics statistics, Metric metric, ContainerThreadPool threadpool, @@ -128,7 +156,8 @@ public class SearchHandler extends LoggingRequestHandler { ExecutionFactory executionFactory) { this(metric, threadpool.executor(), queryProfileRegistry, embedder, executionFactory, config.numQueriesToTraceOnDebugAfterConstruction(), - config.hostResponseHeaderKey().equals("") ? Optional.empty() : Optional.of(config.hostResponseHeaderKey())); + config.hostResponseHeaderKey().equals("") ? Optional.empty() : Optional.of(config.hostResponseHeaderKey()), + ZoneInfo.defaultInfo()); } /** @@ -158,8 +187,9 @@ public class SearchHandler extends LoggingRequestHandler { ExecutionFactory executionFactory) { this(metric, executor, queryProfileRegistry, Embedder.throwsOnUse, executionFactory, containerHttpConfig.numQueriesToTraceOnDebugAfterConstruction(), - containerHttpConfig.hostResponseHeaderKey().equals("") ? - Optional.empty() : Optional.of(containerHttpConfig.hostResponseHeaderKey())); + containerHttpConfig.hostResponseHeaderKey().equals("") ? Optional.empty() + : Optional.of(containerHttpConfig.hostResponseHeaderKey()), + ZoneInfo.defaultInfo()); } /** @@ -176,8 +206,9 @@ public class SearchHandler extends LoggingRequestHandler { this(metric, executor, QueryProfileConfigurer.createFromConfig(queryProfileConfig).compile(), Embedder.throwsOnUse, executionFactory, containerHttpConfig.numQueriesToTraceOnDebugAfterConstruction(), - containerHttpConfig.hostResponseHeaderKey().equals("") ? - Optional.empty() : Optional.of( containerHttpConfig.hostResponseHeaderKey())); + containerHttpConfig.hostResponseHeaderKey().equals("") ? Optional.empty() + : Optional.of( containerHttpConfig.hostResponseHeaderKey()), + ZoneInfo.defaultInfo()); } /** @@ -192,7 +223,8 @@ public class SearchHandler extends LoggingRequestHandler { ExecutionFactory executionFactory, Optional<String> hostResponseHeaderKey) { this(metric, executor, queryProfileRegistry, Embedder.throwsOnUse, - executionFactory, 0, hostResponseHeaderKey); + executionFactory, 0, hostResponseHeaderKey, + ZoneInfo.defaultInfo()); } private SearchHandler(Metric metric, @@ -201,7 +233,8 @@ public class SearchHandler extends LoggingRequestHandler { Embedder embedder, ExecutionFactory executionFactory, long numQueriesToTraceOnDebugAfterStartup, - Optional<String> hostResponseHeaderKey) { + Optional<String> hostResponseHeaderKey, + ZoneInfo zoneInfo) { super(executor, metric, true); log.log(Level.FINE, () -> "SearchHandler.init " + System.identityHashCode(this)); this.queryProfileRegistry = queryProfileRegistry; @@ -213,6 +246,7 @@ public class SearchHandler extends LoggingRequestHandler { this.hostResponseHeaderKey = hostResponseHeaderKey; this.numRequestsLeftToTrace = new AtomicLong(numQueriesToTraceOnDebugAfterStartup); metric.set(SEARCH_CONNECTIONS, 0.0d, null); + this.zoneInfo = zoneInfo; } /** @deprecated use the other constructor */ @@ -308,6 +342,7 @@ public class SearchHandler extends LoggingRequestHandler { .setRequestMap(requestMap) .setQueryProfile(queryProfile) .setEmbedder(embedder) + .setZoneInfo(zoneInfo) .build(); boolean benchmarking = VespaHeaders.benchmarkOutput(request); diff --git a/container-search/src/main/java/com/yahoo/search/query/properties/RequestContextProperties.java b/container-search/src/main/java/com/yahoo/search/query/properties/RequestContextProperties.java index 06d827faf68..46aafcb11e3 100644 --- a/container-search/src/main/java/com/yahoo/search/query/properties/RequestContextProperties.java +++ b/container-search/src/main/java/com/yahoo/search/query/properties/RequestContextProperties.java @@ -1,13 +1,17 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.query.properties; +import ai.vespa.cloud.ZoneInfo; import com.yahoo.processing.request.CompoundName; import com.yahoo.search.query.Properties; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; /** - * Turns get(name) into get(name, request) using the request given at construction time. + * Turns get(name) into get(name, context) using the request given at construction time + * and the zone info. * This is used to allow the query's request to be supplied to all property requests * without forcing users of the query.properties() to supply this explicitly. * @@ -15,27 +19,36 @@ import java.util.Map; */ public class RequestContextProperties extends Properties { - private final Map<String, String> requestMap; - - public RequestContextProperties(Map<String, String> properties) { - this.requestMap = properties; + private final Map<String, String> context; + + public RequestContextProperties(Map<String, String> properties, ZoneInfo zoneInfo) { + if (zoneInfo == ZoneInfo.defaultInfo()) { + context = properties; + } + else { + Map<String, String> context = new HashMap<>(properties); + context.putIfAbsent("environment", zoneInfo.zone().environment().name()); + context.putIfAbsent("region", zoneInfo.zone().region()); + context.putIfAbsent("instance", zoneInfo.application().instance()); + this.context = Collections.unmodifiableMap(context); + } } @Override public Object get(CompoundName name, Map<String,String> context, com.yahoo.processing.request.Properties substitution) { - return super.get(name, context == null ? requestMap : context, substitution); + return super.get(name, context == null ? this.context : context, substitution); } @Override public void set(CompoundName name, Object value, Map<String,String> context) { - super.set(name, value, context == null ? requestMap : context); + super.set(name, value, context == null ? this.context : context); } @Override public Map<String, Object> listProperties(CompoundName path, Map<String,String> context, com.yahoo.processing.request.Properties substitution) { - return super.listProperties(path, context == null ? requestMap : context, substitution); + return super.listProperties(path, context == null ? this.context : context, substitution); } } diff --git a/container-search/src/test/java/com/yahoo/search/handler/SearchHandlerTest.java b/container-search/src/test/java/com/yahoo/search/handler/SearchHandlerTest.java index a6ae5f2b1b9..e1987aa50ca 100644 --- a/container-search/src/test/java/com/yahoo/search/handler/SearchHandlerTest.java +++ b/container-search/src/test/java/com/yahoo/search/handler/SearchHandlerTest.java @@ -186,7 +186,7 @@ public class SearchHandlerTest { RequestHandlerTestDriver.MockResponseHandler responseHandler = newDriver.sendRequest( "http://localhost/search/?yql=select%20*%20from%20foo%20where%20bar%20%3E%201453501295%27%3B"); responseHandler.readAll(); - assertThat(responseHandler.getStatus(), is(400)); + assertEquals(400, responseHandler.getStatus()); assertEquals(Request.RequestType.READ, responseHandler.getResponse().getRequestType()); } } diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsTestCase.java index 231020c4fa5..3542e1413eb 100644 --- a/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/query/profile/test/QueryProfileVariantsTestCase.java @@ -1,6 +1,11 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.query.profile.test; +import ai.vespa.cloud.ApplicationId; +import ai.vespa.cloud.Environment; +import ai.vespa.cloud.Zone; +import ai.vespa.cloud.ZoneInfo; +import com.yahoo.jdisc.application.Application; import com.yahoo.jdisc.http.HttpRequest.Method; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.processing.request.CompoundName; @@ -1331,6 +1336,53 @@ public class QueryProfileVariantsTestCase { assertEquals("yahoo/alerts", cAlert.get("vertical.custid", toMap("entry=alert", "intl=us", "lang=en-US"))); } + @Test + public void testZoneInfoInContext() { + QueryProfileRegistry registry = new QueryProfileRegistry(); + QueryProfile profile = new QueryProfile("test"); + profile.setDimensions(new String[] { "environment", "region", "instance" }); + profile.set("value", "default", registry); + profile.set("value", "prod-region1-instance1", + toMap("environment=prod", "region=region1", "instance=instance1"), + registry); + profile.set("value", "prod-instance2", + toMap("environment=prod", "instance=instance2"), + registry); + profile.set("value", "prod-region3", + toMap("environment=prod", "region=region3"), + registry); + profile.set("value", "dev", + toMap("environment=dev"), + registry); + registry.register(profile); + + CompiledQueryProfileRegistry cRegistry = registry.compile(); + CompiledQueryProfile cTest = cRegistry.findQueryProfile("test"); + + assertValueForZone("default", ZoneInfo.defaultInfo(), cTest); + assertValueForZone("prod-region1-instance1", + new ZoneInfo(new ApplicationId("tenant1", "application1", "instance1"), + new Zone(Environment.prod, "region1")), + cTest); + assertValueForZone("prod-instance2", + new ZoneInfo(new ApplicationId("tenant2", "application2", "instance2"), + new Zone(Environment.prod, "region1")), + cTest); + assertValueForZone("prod-region3", + new ZoneInfo(new ApplicationId("tenant3", "application3", "instance3"), + new Zone(Environment.prod, "region3")), + cTest); + assertValueForZone("dev", + new ZoneInfo(new ApplicationId("tenant4", "application4", "instance4"), + new Zone(Environment.dev, "region4")), + cTest); + } + + private void assertValueForZone(String expected, ZoneInfo zoneInfo, CompiledQueryProfile cTest) { + assertEquals(expected, + new Query.Builder().setQueryProfile(cTest).setZoneInfo(zoneInfo).build().properties().get("value")); + } + private void assertGet(String expectedValue, String parameter, String[] dimensionValues, QueryProfile profile, CompiledQueryProfile cprofile) { Map<String,String> context=toMap(profile,dimensionValues); assertEquals("Looking up '" + parameter + "' for '" + Arrays.toString(dimensionValues) + "'",expectedValue,cprofile.get(parameter,context)); diff --git a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java index a642d14b368..d6e99ec3a44 100644 --- a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java @@ -5,6 +5,7 @@ import com.yahoo.config.search.IntConfig; import com.yahoo.config.search.StringConfig; import com.yahoo.container.core.config.HandlersConfigurerDi; import com.yahoo.container.core.config.testutil.HandlersConfigurerTestWrapper; +import com.yahoo.lang.MutableInteger; import com.yahoo.search.Query; import com.yahoo.search.Result; import com.yahoo.search.Searcher; @@ -38,6 +39,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThat; @@ -129,7 +131,7 @@ public class SearchChainConfigurerTestCase { @Test public void testConfigurableSearcher() { - HandlersConfigurerTestWrapper configurer=new HandlersConfigurerTestWrapper("dir:" + testDir); + HandlersConfigurerTestWrapper configurer = new HandlersConfigurerTestWrapper("dir:" + testDir); SearchChain configurable = getSearchChainRegistryFrom(configurer).getComponent("configurable"); assertNotNull(configurable); @@ -137,9 +139,8 @@ public class SearchChainConfigurerTestCase { Searcher s = configurable.searchers().get(0); assertThat(s, instanceOf(ConfigurableSearcher.class)); ConfigurableSearcher searcher = (ConfigurableSearcher)s; - assertThat("Value from int.cfg file", searcher.intConfig.intVal(), is(7)); - assertThat("Value from string.cfg file", searcher.stringConfig.stringVal(), - is("com.yahoo.search.searchchain.config.test")); + assertEquals("Value from int.cfg file", 7, searcher.intConfig.intVal()); + assertEquals("Value from string.cfg file", "com.yahoo.search.searchchain.config.test", searcher.stringConfig.stringVal()); configurer.shutdown(); } @@ -342,7 +343,7 @@ public class SearchChainConfigurerTestCase { if (append) { Pattern p = Pattern.compile("^[a-z]+" + "\\[\\d+\\]\\.id (.+)"); BufferedReader reader = new BufferedReader(new InputStreamReader( - new FileInputStream(new File(componentsFile)), StandardCharsets.UTF_8)); + new FileInputStream(componentsFile), StandardCharsets.UTF_8)); while ((line = reader.readLine()) != null) { Matcher m = p.matcher(line); if (m.matches() && !m.group(1).equals(HandlersConfigurerDi.RegistriesHack.class.getName())) { @@ -353,7 +354,7 @@ public class SearchChainConfigurerTestCase { reader.close(); } BufferedReader reader = new BufferedReader(new InputStreamReader( - new FileInputStream(new File(configFile)), StandardCharsets.UTF_8)); + new FileInputStream(configFile), StandardCharsets.UTF_8)); Pattern component = Pattern.compile("^" + componentType + "\\[\\d+\\]\\.id (.+)"); while ((line = reader.readLine()) != null) { Matcher m = component.matcher(line); @@ -366,13 +367,22 @@ public class SearchChainConfigurerTestCase { buf.append("components[").append(i++).append("].id ").append(HandlersConfigurerDi.RegistriesHack.class.getName()).append("\n"); if (componentType.equals("components")) - buf.append("components[").append(i++).append("].id ").append(ExecutionFactory.class.getName()).append("\n"); + i = addStandardComponents(i, buf); buf.insert(0, "components["+i+"]\n"); - Writer writer = new OutputStreamWriter(new FileOutputStream(new File(componentsFile)), StandardCharsets.UTF_8); + Writer writer = new OutputStreamWriter(new FileOutputStream(componentsFile), StandardCharsets.UTF_8); writer.write(buf.toString()); writer.flush(); writer.close(); } + private static int addStandardComponents(int i, StringBuilder builder) { + addComponent(ExecutionFactory.class.getName(), i++, builder); + return i; + } + + private static void addComponent(String component, int i, StringBuilder builder) { + builder.append("components[").append(i).append("].id ").append(component).append("\n"); + } + } |