summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model/pom.xml6
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/ConsumersConfigGenerator.java88
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java97
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java162
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/VespaServicesConfigGenerator.java46
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/Container.java13
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleMapper.java17
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java193
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java146
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java103
-rw-r--r--configd/src/apps/sentinel/config-handler.cpp26
-rw-r--r--configd/src/apps/sentinel/config-handler.h2
-rw-r--r--configd/src/apps/sentinel/service.cpp96
-rw-r--r--configd/src/apps/sentinel/service.h7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java54
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java43
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java14
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java15
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java31
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java37
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java4
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java6
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java146
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java67
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java21
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java8
-rw-r--r--metrics-proxy/OWNERS1
-rw-r--r--metrics-proxy/README1
-rw-r--r--metrics-proxy/pom.xml157
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsConsumers.java80
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsManager.java160
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/VespaMetrics.java307
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/package-info.java8
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/AggregationKey.java45
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java99
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/HealthMetric.java56
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metric.java124
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metrics.java111
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/MetricsFormatter.java71
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/ApplicationDimensions.java30
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/NodeDimensions.java30
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/package-info.java8
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ConsumerId.java38
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/DimensionId.java38
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricId.java39
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricsPacket.java182
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ServiceId.java39
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonUtil.java130
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasArrayJsonModel.java80
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModel.java130
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/package-info.java8
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcConnector.java62
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcServer.java213
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ConfigSentinelClient.java176
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/CpuJiffies.java43
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyHealthMetricFetcher.java34
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyMetricsFetcher.java30
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/HttpMetricFetcher.java94
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java77
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java129
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ServiceListener.java14
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPoller.java259
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPollerProvider.java33
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java216
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaServices.java123
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/package-info.java8
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/application-dimensions.def5
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/consumers.def10
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/monitoring.def8
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/node-dimensions.def5
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/rpc-connector.def4
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/vespa-services.def10
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java27
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java244
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/ExternalMetricsTest.java73
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/MetricsTest.java99
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/MetricsPacketTest.java111
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonUtilTest.java63
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java94
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/IntegrationTester.java144
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcHealthMetricsTest.java97
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java208
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelClientTest.java104
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelDummy.java61
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java67
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/DummyService.java36
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MetricsFetcherTest.java90
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockConfigSentinelClient.java50
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockHttpServer.java56
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/SystemPollerTest.java45
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServiceTest.java77
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServicesTest.java42
-rw-r--r--metrics-proxy/src/test/resources/health-check-failed.response.json7
-rw-r--r--metrics-proxy/src/test/resources/health-check.response.json6
-rw-r--r--metrics-proxy/src/test/resources/metrics-container-state-multi-chain.json281
-rw-r--r--metrics-proxy/src/test/resources/metrics-state.json42
-rw-r--r--metrics-proxy/src/test/resources/metrics-storage-simple.json38
-rw-r--r--metrics-proxy/src/test/resources/rpc-json-output-check.json1
-rw-r--r--metrics-proxy/src/test/resources/yamas-array-no-routing.json19
-rw-r--r--metrics-proxy/src/test/resources/yamas-array.json26
-rw-r--r--metrics/src/tests/CMakeLists.txt4
-rw-r--r--metrics/src/tests/loadmetrictest.cpp53
-rw-r--r--metrics/src/tests/summetrictest.cpp65
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/GraphImporter.java52
-rw-r--r--model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/SimpleImportTestCase.java41
-rw-r--r--model-integration/src/test/models/onnx/simple/simple.onnx23
-rwxr-xr-xmodel-integration/src/test/models/onnx/simple/simple.py32
-rw-r--r--pom.xml1
-rw-r--r--searchlib/src/tests/diskindex/fieldwriter/fieldwriter_test.cpp34
-rw-r--r--searchlib/src/tests/features/prod_features.cpp4
-rw-r--r--searchlib/src/tests/postinglistbm/CMakeLists.txt11
-rw-r--r--searchlib/src/tests/postinglistbm/andstress.cpp8
-rw-r--r--searchlib/src/tests/postinglistbm/posting_list_test.cpp173
-rw-r--r--searchlib/src/tests/postinglistbm/postinglistbm.cpp167
-rw-r--r--searchlib/src/vespa/searchlib/engine/proto_rpc_adapter.cpp19
-rw-r--r--searchlib/src/vespa/searchlib/features/dotproductfeature.cpp144
-rw-r--r--searchlib/src/vespa/searchlib/features/dotproductfeature.h71
-rw-r--r--searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp35
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fakeword.h2
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fakewordset.cpp80
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fakewordset.h72
-rw-r--r--searchlib/src/vespa/searchlib/test/fakedata/fpfactory.cpp9
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java78
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/CompileVersionMojo.java18
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java58
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java68
134 files changed, 1348 insertions, 7213 deletions
diff --git a/config-model/pom.xml b/config-model/pom.xml
index 2c45e292d01..715718f8bb5 100644
--- a/config-model/pom.xml
+++ b/config-model/pom.xml
@@ -96,12 +96,6 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>metrics-proxy</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
<artifactId>container-disc</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java b/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
index 2f41b172ab6..d3da9fe1f03 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
@@ -1,4 +1,9 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/*
+ * // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ *
+ *
+ */
package com.yahoo.searchdefinition;
import com.yahoo.searchlib.rankingexpression.Reference;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
index e0956097c84..dd27dd176e0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
@@ -207,7 +207,7 @@ public class Admin extends AbstractConfigProducer implements Serializable {
var metricsProxyCluster = new MetricsProxyContainerCluster(this, "metrics", deployState);
int index = 0;
for (var host : hosts) {
- var container = new MetricsProxyContainer(metricsProxyCluster, index++, deployState.isHosted());
+ var container = new MetricsProxyContainer(metricsProxyCluster, index++);
addAndInitializeService(deployState.getDeployLogger(), host, container);
metricsProxyCluster.addContainer(container);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/ConsumersConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/ConsumersConfigGenerator.java
deleted file mode 100644
index 2df4008214a..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/ConsumersConfigGenerator.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package com.yahoo.vespa.model.admin.metricsproxy;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
-import com.yahoo.vespa.model.admin.monitoring.Metric;
-import com.yahoo.vespa.model.admin.monitoring.MetricSet;
-import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
-
-import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import static com.yahoo.vespa.model.admin.monitoring.DefaultMetricsConsumer.VESPA_CONSUMER_ID;
-import static com.yahoo.vespa.model.admin.monitoring.DefaultMetricsConsumer.getDefaultMetricsConsumer;
-
-/**
- * Helper class to generate config for metrics consumers.
- *
- * @author gjoranv
- */
-class ConsumersConfigGenerator {
-
- /**
- * @param userConsumers The consumers set up by the user in services.xml
- * @return A list of consumer builders (a mapping from consumer to its metrics)
- */
- static List<Consumer.Builder> generate(Map<String, MetricsConsumer> userConsumers) {
- // Normally, the user given consumers should not contain VESPA_CONSUMER_ID, but it's allowed for some internally used applications.
- var allConsumers = new LinkedHashMap<>(userConsumers);
- allConsumers.put(VESPA_CONSUMER_ID, combineConsumers(getDefaultMetricsConsumer(), allConsumers.get(VESPA_CONSUMER_ID)));
-
- return allConsumers.values().stream()
- .map(ConsumersConfigGenerator::toConsumerBuilder)
- .collect(Collectors.toList());
- }
-
- /*
- * Returns a new consumer that is a combination of the two given consumers
- * (ignoring the id of the consumers' metric sets).
- * If a metric with the same id exists in both consumers, output name and
- * dimensions from the 'overriding' consumer is used, but dimensions from 'original'
- * are added if they don't exist in 'overriding'.
- */
- private static MetricsConsumer combineConsumers(MetricsConsumer original, MetricsConsumer overriding) {
- if (overriding == null) return original;
- return addMetrics(original, overriding.getMetrics());
- }
-
- private static MetricsConsumer addMetrics(MetricsConsumer original, Map<String, Metric> metrics) {
- Map<String, Metric> combinedMetrics = new LinkedHashMap<>(original.getMetrics());
- metrics.forEach((name, newMetric) ->
- combinedMetrics.put(name, combineMetrics(original.getMetrics().get(name), newMetric)));
-
- return new MetricsConsumer(original.getId(),
- new MetricSet(original.getMetricSet().getId(), combinedMetrics.values()));
- }
-
- private static Metric combineMetrics(@Nullable Metric original, Metric newMetric) {
- return original != null ? newMetric.addDimensionsFrom(original) : newMetric;
- }
-
- private static Consumer.Builder toConsumerBuilder(MetricsConsumer consumer) {
- Consumer.Builder builder = new Consumer.Builder().name(consumer.getId());
- consumer.getMetrics().values().forEach(metric -> builder.metric(toConsumerMetricBuilder(metric)));
- return builder;
- }
-
- private static Consumer.Metric.Builder toConsumerMetricBuilder(Metric metric) {
- Consumer.Metric.Builder builder = new Consumer.Metric.Builder().name(metric.name)
- .outputname(metric.outputName)
- .description(metric.description);
- metric.dimensions.forEach((name, value) -> builder.dimension(toMetricDimensionBuilder(name, value)));
- return builder;
- }
-
- private static Consumer.Metric.Dimension.Builder toMetricDimensionBuilder(String name, String value) {
- return new Consumer.Metric.Dimension.Builder()
- .key(name)
- .value(value);
- }
-
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
index 1a66fa72c93..742196c91d1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
@@ -1,66 +1,22 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
package com.yahoo.vespa.model.admin.metricsproxy;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
-import ai.vespa.metricsproxy.rpc.RpcConnector;
-import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
-import ai.vespa.metricsproxy.service.VespaServices;
-import ai.vespa.metricsproxy.service.VespaServicesConfig;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.producer.AbstractConfigProducer;
-import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.vespa.model.container.Container;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer.NodeDimensionNames.CANONICAL_FLAVOR;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer.NodeDimensionNames.CLUSTER_ID;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer.NodeDimensionNames.CLUSTER_TYPE;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer.NodeDimensionNames.FLAVOR;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.METRICS_PROXY_BUNDLE_NAME;
/**
* Container running a metrics proxy.
*
* @author gjoranv
*/
-public class MetricsProxyContainer extends Container implements
- NodeDimensionsConfig.Producer,
- RpcConnectorConfig.Producer,
- VespaServicesConfig.Producer
-{
-
- static final class NodeDimensionNames {
- static final String FLAVOR = "flavor";
- static final String CANONICAL_FLAVOR = "canonicalFlavor";
- static final String CLUSTER_TYPE = "clustertype";
- static final String CLUSTER_ID = "clusterid";
- }
-
- private final boolean isHostedVespa;
+public class MetricsProxyContainer extends Container {
- public MetricsProxyContainer(AbstractConfigProducer parent, int index, boolean isHostedVespa) {
+ public MetricsProxyContainer(AbstractConfigProducer parent, int index) {
super(parent, "" + index, index);
- this.isHostedVespa = isHostedVespa;
setProp("clustertype", "admin");
setProp("index", String.valueOf(index));
- addNodeSpecificComponents();
- }
-
- private void addNodeSpecificComponents() {
- addMetricsProxyComponent(NodeDimensions.class);
- addMetricsProxyComponent(RpcConnector.class);
- addMetricsProxyComponent(VespaServices.class);
- }
-
- int metricsRpcPortOffset() {
- return numHttpServerPorts;
}
@Override
@@ -70,7 +26,7 @@ public class MetricsProxyContainer extends Container implements
@Override
public int getWantedPort() {
- return 19092;
+ return 19092; // TODO: current metrics-proxy uses 19091 as rpc port, will now get 19093.
}
@Override
@@ -78,15 +34,9 @@ public class MetricsProxyContainer extends Container implements
return true;
}
- // Must have predictable ports for both http and rpc.
- @Override
- public boolean requiresConsecutivePorts() {
- return true;
- }
-
@Override
public int getPortCount() {
- return metricsRpcPortOffset() + 1;
+ return super.getPortCount() + 1;
}
@Override
@@ -95,43 +45,4 @@ public class MetricsProxyContainer extends Container implements
portsMeta.on(numHttpServerPorts).tag("rpc").tag("metrics");
}
- @Override
- public String[] getPortSuffixes() {
- var suffixes = super.getPortSuffixes();
- suffixes[metricsRpcPortOffset()] = "rpc/metrics";
- return suffixes;
- }
-
- @Override
- public void getConfig(RpcConnectorConfig.Builder builder) {
- builder.port(getRelativePort(metricsRpcPortOffset()));
- }
-
- @Override
- public void getConfig(VespaServicesConfig.Builder builder) {
- builder.service.addAll(VespaServicesConfigGenerator.generate(getHostResource().getServices()));
- }
-
- @Override
- public void getConfig(NodeDimensionsConfig.Builder builder) {
- Map<String, String> dimensions = new LinkedHashMap<>();
- if (isHostedVespa) {
- getHostResource().getFlavor().ifPresent(flavor -> {
- dimensions.put(FLAVOR, flavor.name());
- dimensions.put(CANONICAL_FLAVOR, flavor.canonicalName());
- });
-
- getHostResource().primaryClusterMembership().map(ClusterMembership::cluster).ifPresent(cluster -> {
- dimensions.put(CLUSTER_TYPE, cluster.type().name());
- dimensions.put(CLUSTER_ID, cluster.id().value());
- });
-
- builder.dimensions(dimensions);
- }
- }
-
- private void addMetricsProxyComponent(Class<?> componentClass) {
- addSimpleComponent(componentClass.getName(), null, METRICS_PROXY_BUNDLE_NAME);
- }
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
index d9a969ab7c9..437df42d531 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
@@ -1,183 +1,23 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
package com.yahoo.vespa.model.admin.metricsproxy;
-import ai.vespa.metricsproxy.core.MetricsConsumers;
-import ai.vespa.metricsproxy.core.ConsumersConfig;
-import ai.vespa.metricsproxy.core.MetricsManager;
-import ai.vespa.metricsproxy.metric.ExternalMetrics;
-import ai.vespa.metricsproxy.core.VespaMetrics;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
-import ai.vespa.metricsproxy.rpc.RpcServer;
-import ai.vespa.metricsproxy.core.MonitoringConfig;
-import ai.vespa.metricsproxy.service.SystemPollerProvider;
-import ai.vespa.metricsproxy.service.ConfigSentinelClient;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
-import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.vespa.model.VespaModel;
-import com.yahoo.vespa.model.admin.Admin;
-import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
-import com.yahoo.vespa.model.admin.monitoring.Monitoring;
-import com.yahoo.vespa.model.admin.monitoring.builder.Metrics;
import com.yahoo.vespa.model.container.ContainerCluster;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.APPLICATION;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.APPLICATION_ID;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.INSTANCE;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.LEGACY_APPLICATION;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.TENANT;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.ZONE;
-import static com.yahoo.vespa.model.container.xml.BundleMapper.JarSuffix.JAR_WITH_DEPS;
-import static com.yahoo.vespa.model.container.xml.BundleMapper.bundlePathFromName;
-
/**
* Container cluster for metrics proxy containers.
*
* @author gjoranv
*/
-public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyContainer> implements
- ApplicationDimensionsConfig.Producer,
- ConsumersConfig.Producer,
- MonitoringConfig.Producer
-{
- public static final Logger log = Logger.getLogger(MetricsProxyContainerCluster.class.getName());
-
- private static final String METRICS_PROXY_NAME = "metrics-proxy";
- private static final Path METRICS_PROXY_BUNDLE_FILE = bundlePathFromName(METRICS_PROXY_NAME, JAR_WITH_DEPS);
- static final String METRICS_PROXY_BUNDLE_NAME = "com.yahoo.vespa." + METRICS_PROXY_NAME;
-
- static final String DEFAULT_NAME_IN_MONITORING_SYSTEM = "vespa";
- static final int DEFAULT_MONITORING_INTERVAL = 1;
-
- static final class AppDimensionNames {
- static final String ZONE = "zone";
- static final String APPLICATION_ID = "applicationId"; // tenant.app.instance
- static final String TENANT = "tenantName";
- static final String APPLICATION = "applicationName";
- static final String INSTANCE = "instanceName";
- static final String LEGACY_APPLICATION = "app"; // app.instance
- }
-
- private final AbstractConfigProducer<?> parent;
- private final ApplicationId applicationId;
+public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyContainer> {
public MetricsProxyContainerCluster(AbstractConfigProducer<?> parent, String name, DeployState deployState) {
super(parent, name, name, deployState);
- this.parent = parent;
- applicationId = deployState.getProperties().applicationId();
-
setRpcServerEnabled(false);
addDefaultHandlersExceptStatus();
-
- addPlatformBundle(METRICS_PROXY_BUNDLE_FILE);
- addClusterComponents();
- }
-
- private void addClusterComponents() {
- addMetricsProxyComponent(ApplicationDimensions.class);
- addMetricsProxyComponent(ConfigSentinelClient.class);
- addMetricsProxyComponent(ExternalMetrics.class);
- addMetricsProxyComponent(MetricsConsumers.class);
- addMetricsProxyComponent(MetricsManager.class);
- addMetricsProxyComponent(RpcServer.class);
- addMetricsProxyComponent(SystemPollerProvider.class);
- addMetricsProxyComponent(VespaMetrics.class);
}
@Override
protected void doPrepare(DeployState deployState) { }
- @Override
- public void getConfig(MonitoringConfig.Builder builder) {
- builder.systemName(getSystemName())
- .intervalMinutes(getIntervalMinutes());
- }
-
- @Override
- public void getConfig(ConsumersConfig.Builder builder) {
- builder.consumer.addAll(ConsumersConfigGenerator.generate(getUserMetricsConsumers()));
- }
-
- @Override
- public void getConfig(ApplicationDimensionsConfig.Builder builder) {
- if (isHostedVespa()) {
- builder.dimensions(applicationDimensions());
- }
- }
-
- // Returns the metricConsumers from services.xml
- private Map<String, MetricsConsumer> getUserMetricsConsumers() {
- return getAdmin()
- .map(this::consumersInAdmin)
- .orElse(Collections.emptyMap());
- }
-
- private Map<String, MetricsConsumer> consumersInAdmin(Admin admin) {
- Metrics metrics = admin.getUserMetrics();
- return metrics.getConsumers();
- }
-
- private Optional<Admin> getAdmin() {
- if (parent != null) {
- AbstractConfigProducerRoot r = parent.getRoot();
- if (r instanceof VespaModel) {
- VespaModel model = (VespaModel) r;
- return Optional.ofNullable(model.getAdmin());
- }
- }
- return Optional.empty();
- }
-
- private String getSystemName() {
- Monitoring monitoring = getMonitoringService();
- if (monitoring != null && ! monitoring.getClustername().equals(""))
- return monitoring.getClustername();
- return DEFAULT_NAME_IN_MONITORING_SYSTEM;
- }
-
- private int getIntervalMinutes() {
- Monitoring monitoring = getMonitoringService();
- if (monitoring != null && monitoring.getInterval() != null) {
- return monitoring.getInterval();
- }
- return DEFAULT_MONITORING_INTERVAL;
- }
-
- private void addMetricsProxyComponent(Class<?> componentClass) {
- addSimpleComponent(componentClass.getName(), null, METRICS_PROXY_BUNDLE_NAME);
- }
-
- private Map<String, String> applicationDimensions() {
- Map<String, String> dimensions = new LinkedHashMap<>();
- dimensions.put(ZONE, zoneString(getZone()));
- dimensions.put(APPLICATION_ID, serializeWithDots(applicationId));
- dimensions.put(TENANT, applicationId.tenant().value());
- dimensions.put(APPLICATION, applicationId.application().value());
- dimensions.put(INSTANCE, applicationId.instance().value());
- dimensions.put(LEGACY_APPLICATION, applicationId.application().value() + "." + applicationId.instance().value());
- return dimensions;
- }
-
- // ApplicationId uses ':' as separator.
- private static String serializeWithDots(ApplicationId applicationId) {
- return applicationId.serializedForm().replace(':', '.');
- }
-
- static String zoneString(Zone zone) {
- return zone.environment().value() + "." + zone.region().value();
- }
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/VespaServicesConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/VespaServicesConfigGenerator.java
deleted file mode 100644
index 08a81fad0a3..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/VespaServicesConfigGenerator.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package com.yahoo.vespa.model.admin.metricsproxy;
-
-import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import com.yahoo.vespa.model.Service;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @author gjoranv
- */
-public class VespaServicesConfigGenerator {
-
- public static List<VespaServicesConfig.Service.Builder> generate(List<Service> services) {
- return services.stream()
- .filter(VespaServicesConfigGenerator::doIncludeServiceMetrics)
- .map(VespaServicesConfigGenerator::toServiceBuilder)
- .collect(Collectors.toList());
- }
-
- private static boolean doIncludeServiceMetrics(Service s) {
- return s.getStartupCommand() != null || s.getServiceType().equals("configserver") || s.getServiceType().equals("config-sentinel");
- }
-
- private static VespaServicesConfig.Service.Builder toServiceBuilder(Service service) {
- VespaServicesConfig.Service.Builder builder = new VespaServicesConfig.Service.Builder()
- .configId(service.getConfigId())
- .name(service.getServiceName())
- .port(service.getHealthPort())
- .healthport(service.getHealthPort());
-
- service.getDefaultMetricDimensions().forEach((name, value) -> builder.dimension(toServiceDimensionBuilder(name, value)));
- return builder;
- }
-
- private static VespaServicesConfig.Service.Dimension.Builder toServiceDimensionBuilder(String name, String value) {
- return new VespaServicesConfig.Service.Dimension.Builder()
- .key(name)
- .value(value);
- }
-
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
index f2bd67cdd81..f449feb5fc1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
@@ -10,7 +10,6 @@ import com.yahoo.container.QrConfig;
import com.yahoo.container.core.ContainerHttpConfig;
import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
-import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.model.AbstractService;
@@ -37,10 +36,6 @@ import static com.yahoo.container.QrConfig.Filedistributor;
import static com.yahoo.container.QrConfig.Rpc;
/**
- * Note about components: In general, all components should belong to the cluster and not the container. However,
- * components that need node specific config must be added at the container level, along with the node-specific
- * parts of the config generation (getConfig).
- *
* @author gjoranv
* @author Einar M R Rosenvinge
* @author Tony Vaagenes
@@ -112,15 +107,11 @@ public abstract class Container extends AbstractService implements
return components;
}
- public final void addComponent(Component c) {
+ public void addComponent(Component c) {
components.addComponent(c);
}
- public final void addSimpleComponent(String idSpec, String classSpec, String bundleSpec) {
- addComponent(new SimpleComponent(new ComponentModel(idSpec, classSpec, bundleSpec)));
- }
-
- public final void addHandler(Handler h) {
+ public void addHandler(Handler h) {
handlers.addComponent(h);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index 1f0c3d6f84e..ad087288799 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -200,7 +200,7 @@ public abstract class ContainerCluster<CONTAINER extends Container>
addVipHandler();
}
- public final void addDefaultHandlersExceptStatus() {
+ public void addDefaultHandlersExceptStatus() {
addDefaultRootHandler();
addMetricStateHandler();
addApplicationStatusHandler();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleMapper.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleMapper.java
index 4c5c14dad87..11d467a61d5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleMapper.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleMapper.java
@@ -15,20 +15,7 @@ import java.util.Optional;
*/
public class BundleMapper {
- public enum JarSuffix {
- JAR_WITH_DEPS("-jar-with-dependencies.jar"),
- DEPLOY("-deploy.jar");
-
- private final String suffix;
-
- JarSuffix(String suffix) {
- this.suffix = suffix;
- }
- }
-
public static final Path LIBRARY_PATH = Paths.get(Defaults.getDefaults().underVespaHome("lib/jars"));
-
-
public static final String searchAndDocprocBundle = "container-search-and-docproc";
private static final Map<String, String> bundleFromClass;
@@ -47,10 +34,6 @@ public class BundleMapper {
return LIBRARY_PATH.resolve(fileName);
}
- public static Path bundlePathFromName(String name, JarSuffix suffix) {
- return Paths.get(Defaults.getDefaults().underVespaHome(LIBRARY_PATH + name + suffix.suffix));
- }
-
/**
* TODO: This is a temporary hack to ensure that users can use our internal components without
* specifying the bundle in which the components reside. Ideally, this information
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
index 59a72aedaaf..efd13e82361 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
@@ -1,186 +1,45 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
package com.yahoo.vespa.model.admin.metricsproxy;
-import ai.vespa.metricsproxy.core.ConsumersConfig;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
-import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.model.VespaModel;
-import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames;
-import com.yahoo.vespa.model.admin.monitoring.Metric;
-import org.junit.Rule;
+import com.yahoo.vespa.model.test.VespaModelTester;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.zoneString;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.MY_APPLICATION;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.MY_INSTANCE;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.MY_TENANT;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.checkMetric;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getApplicationDimensionsConfig;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getConsumersConfig;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getCustomConsumer;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getHostedModel;
-import static com.yahoo.vespa.model.admin.monitoring.DefaultMetricsConsumer.VESPA_CONSUMER_ID;
-import static com.yahoo.vespa.model.admin.monitoring.DefaultVespaMetrics.defaultVespaMetricSet;
-import static com.yahoo.vespa.model.admin.monitoring.NetworkMetrics.networkMetricSet;
-import static com.yahoo.vespa.model.admin.monitoring.SystemMetrics.systemMetricSet;
-import static com.yahoo.vespa.model.admin.monitoring.VespaMetricSet.vespaMetricSet;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
/**
* @author gjoranv
*/
public class MetricsProxyContainerClusterTest {
- private static int numDefaultVespaMetrics = defaultVespaMetricSet.getMetrics().size();
- private static int numVespaMetrics = vespaMetricSet.getMetrics().size();
- private static int numSystemMetrics = systemMetricSet.getMetrics().size();
- private static int numNetworkMetrics = networkMetricSet.getMetrics().size();
- private static int numMetricsForDefaultConsumer = numVespaMetrics + numSystemMetrics + numNetworkMetrics;
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
-
- @Test
- public void default_consumer_is_always_present_and_has_all_vespa_metrics_and_all_system_metrics() {
- ConsumersConfig consumersConfig = getConsumersConfig(servicesWithAdminOnly());
- assertEquals(consumersConfig.consumer(0).name(), VESPA_CONSUMER_ID);
- assertEquals(numMetricsForDefaultConsumer, consumersConfig.consumer(0).metric().size());
- }
-
- @Test
- public void vespa_is_a_reserved_consumer_id() {
- String services = String.join("\n",
- "<services>",
- " <admin version='2.0'>",
- " <adminserver hostalias='node1'/>",
- " <metrics>",
- " <consumer id='vespa'/>",
- " </metrics>",
- " </admin>",
- "</services>"
- );
- thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("'Vespa' is not allowed as metrics consumer id");
- getConsumersConfig(services);
- }
-
- @Test
- public void vespa_consumer_id_is_allowed_for_hosted_infrastructure_applications() {
- String services = String.join("\n",
- "<services application-type='hosted-infrastructure'>",
- " <admin version='4.0'>",
- " <adminserver hostalias='node1'/>",
- " <metrics>",
- " <consumer id='Vespa'>",
- " <metric id='custom.metric1'/>",
- " </consumer>",
- " </metrics>",
- " </admin>",
- "</services>"
- );
- ConsumersConfig config = getConsumersConfig(services);
- assertEquals(1, config.consumer().size());
-
- // All default metrics are retained
- ConsumersConfig.Consumer vespaConsumer = config.consumer(0);
- assertEquals(numMetricsForDefaultConsumer + 1, vespaConsumer.metric().size());
-
- Metric customMetric1 = new Metric("custom.metric1");
- assertTrue("Did not contain metric: " + customMetric1, checkMetric(vespaConsumer, customMetric1));
- }
-
- @Test
- public void consumer_id_is_case_insensitive() {
- String services = String.join("\n",
- "<services>",
- " <admin version='2.0'>",
- " <adminserver hostalias='node1'/>",
- " <metrics>",
- " <consumer id='A'/>",
- " <consumer id='a'/>",
- " </metrics>",
- " </admin>",
- "</services>"
- );
- thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("'a' is used as id for two metrics consumers");
- getConsumersConfig(services);
- }
-
- @Test
- public void consumer_with_no_metric_set_has_its_own_metrics_plus_system_metrics_plus_default_vespa_metrics() {
- String services = String.join("\n",
- "<services>",
- " <admin version='2.0'>",
- " <adminserver hostalias='node1'/>",
- " <metrics>",
- " <consumer id='consumer-with-metrics-only'>",
- " <metric id='custom.metric1'/>",
- " <metric id='custom.metric2'/>",
- " </consumer>",
- " </metrics>",
- " </admin>",
- "</services>"
- );
- ConsumersConfig.Consumer consumer = getCustomConsumer(services);
-
- assertEquals(numSystemMetrics + numDefaultVespaMetrics + 2, consumer.metric().size());
-
- Metric customMetric1 = new Metric("custom.metric1");
- Metric customMetric2 = new Metric("custom.metric2");
- assertTrue("Did not contain metric: " + customMetric1, checkMetric(consumer, customMetric1));
- assertTrue("Did not contain metric: " + customMetric2, checkMetric(consumer, customMetric2));
- }
-
@Test
- public void consumer_with_vespa_metric_set_has_all_vespa_metrics_plus_all_system_metrics_plus_its_own() {
- String services = String.join("\n",
- "<services>",
- " <admin version='2.0'>",
- " <adminserver hostalias='node1'/>",
- " <metrics>",
- " <consumer id='consumer-with-vespa-set'>",
- " <metric-set id='vespa'/>",
- " <metric id='my.extra.metric'/>",
- " </consumer>",
- " </metrics>",
- " </admin>",
- "</services>"
- );
- ConsumersConfig.Consumer consumer = getCustomConsumer(services);
- assertEquals(numVespaMetrics + numSystemMetrics + 1, consumer.metric().size());
+ public void one_metrics_proxy_container_is_added_to_every_node() {
+ var numberOfHosts = 4;
+ var tester = new VespaModelTester();
+ tester.enableMetricsProxyContainer(true);
+ tester.addHosts(4);
- Metric customMetric = new Metric("my.extra.metric");
- assertTrue("Did not contain metric: " + customMetric, checkMetric(consumer, customMetric));
- }
+ VespaModel model = tester.createModel(servicesXml(), true);
+ assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
- @Test
- public void hosted_application_propagates_application_dimensions() {
- VespaModel hostedModel = getHostedModel(servicesWithAdminOnly());
- ApplicationDimensionsConfig config = getApplicationDimensionsConfig(hostedModel);
+ for (var host : model.getHostSystem().getHosts()) {
+ assertThat(host.getService(METRICS_PROXY_CONTAINER.serviceName), notNullValue());
+ }
- assertEquals(zoneString(Zone.defaultZone()), config.dimensions(AppDimensionNames.ZONE));
- assertEquals(MY_TENANT, config.dimensions(AppDimensionNames.TENANT));
- assertEquals(MY_APPLICATION, config.dimensions(AppDimensionNames.APPLICATION));
- assertEquals(MY_INSTANCE, config.dimensions(AppDimensionNames.INSTANCE));
- assertEquals(MY_TENANT + "." + MY_APPLICATION + "." + MY_INSTANCE, config.dimensions(AppDimensionNames.APPLICATION_ID));
- assertEquals(MY_APPLICATION + "." + MY_INSTANCE, config.dimensions(AppDimensionNames.LEGACY_APPLICATION));
}
-
- private String servicesWithAdminOnly() {
- return String.join("\n", "<services>",
- " <admin version='4.0'>",
- " <adminserver hostalias='node1'/>",
- " </admin>",
- "</services>"
- );
+ private String servicesXml() {
+ return String.join("\n", "<?xml version='1.0' encoding='utf-8' ?>",
+ "<services>",
+ " <container version='1.0' id='foo'>",
+ " <nodes count='2'/>",
+ " </container>",
+ " <content id='my-content' version='1.0'>",
+ " <documents />",
+ " <nodes count='2'/>",
+ " </content>",
+ "</services>");
}
-
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java
deleted file mode 100644
index ff486c6a437..00000000000
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.yahoo.vespa.model.admin.metricsproxy;
-
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
-import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
-import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import com.yahoo.vespa.model.VespaModel;
-import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer.NodeDimensionNames;
-import com.yahoo.vespa.model.test.VespaModelTester;
-import org.junit.Test;
-
-import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.CONTAINER_CONFIG_ID;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.MY_FLAVOR;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getHostedModel;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getModel;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getNodeDimensionsConfig;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getRpcConnectorConfig;
-import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyModelTester.getVespaServicesConfig;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author gjoranv
- */
-public class MetricsProxyContainerTest {
-
- @Test
- public void one_metrics_proxy_container_is_added_to_every_node() {
- var numberOfHosts = 4;
- var tester = new VespaModelTester();
- tester.enableMetricsProxyContainer(true);
- tester.addHosts(numberOfHosts);
-
- VespaModel model = tester.createModel(servicesWithManyNodes(), true);
- assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
-
- for (var host : model.getHostSystem().getHosts()) {
- assertThat(host.getService(METRICS_PROXY_CONTAINER.serviceName), notNullValue());
-
- long metricsProxies = host.getServices().stream()
- .filter(s -> s.getClass().equals(MetricsProxyContainer.class))
- .count();
- assertThat(metricsProxies, is(1L));
- }
- }
-
- @Test
- public void http_server_is_running_on_expected_port() {
- VespaModel model = getModel(servicesWithContent());
- MetricsProxyContainer container = (MetricsProxyContainer)model.id2producer().get(CONTAINER_CONFIG_ID);
- assertEquals(19092, container.getSearchPort());
- assertEquals(19092, container.getHealthPort());
- assertEquals("http", container.getPortSuffixes()[0]);
-
- assertTrue(container.getPortsMeta().getTagsAt(0).contains("http"));
- assertTrue(container.getPortsMeta().getTagsAt(0).contains("state"));
- }
-
- @Test
- public void rpc_server_is_running_on_expected_port() {
- VespaModel model = getModel(servicesWithContent());
-
- MetricsProxyContainer container = (MetricsProxyContainer)model.id2producer().get(CONTAINER_CONFIG_ID);
-
- int rpcPort = container.metricsRpcPortOffset();
- assertTrue(container.getPortsMeta().getTagsAt(rpcPort).contains("rpc"));
- assertTrue(container.getPortsMeta().getTagsAt(rpcPort).contains("metrics"));
-
- assertEquals("rpc/metrics", container.getPortSuffixes()[rpcPort]);
-
- RpcConnectorConfig config = getRpcConnectorConfig(model);
- assertEquals(19094, config.port());
- }
-
- @Test
- public void hosted_application_propagates_node_dimensions() {
- String services = servicesWithContent();
- VespaModel hostedModel = getHostedModel(services);
- NodeDimensionsConfig config = getNodeDimensionsConfig(hostedModel);
-
- assertEquals("content", config.dimensions(NodeDimensionNames.CLUSTER_TYPE));
- assertEquals("my-content", config.dimensions(NodeDimensionNames.CLUSTER_ID));
- assertEquals(MY_FLAVOR, config.dimensions(NodeDimensionNames.FLAVOR));
- assertEquals(MY_FLAVOR, config.dimensions(NodeDimensionNames.CANONICAL_FLAVOR));
- }
-
-
- @Test
- public void vespa_services_config_has_all_services() {
- VespaServicesConfig vespaServicesConfig = getVespaServicesConfig(servicesWithContent());
- assertEquals(6, vespaServicesConfig.service().size());
-
- for (var service : vespaServicesConfig.service()) {
- if (service.configId().equals("admin/cluster-controllers/0")) {
- assertEquals("Wrong service name", "container-clustercontroller", service.name());
- assertEquals(1, service.dimension().size());
- assertEquals("clustername", service.dimension(0).key());
- assertEquals("cluster-controllers", service.dimension(0).value());
- }
- }
- }
-
- @Test
- public void vespa_services_config_has_service_dimensions() {
- VespaServicesConfig vespaServicesConfig = getVespaServicesConfig(servicesWithContent());
- for (var service : vespaServicesConfig.service()) {
- if (service.configId().equals("admin/cluster-controllers/0")) {
- assertEquals(1, service.dimension().size());
- assertEquals("clustername", service.dimension(0).key());
- assertEquals("cluster-controllers", service.dimension(0).value());
- }
- }
- }
-
-
- private String servicesWithManyNodes() {
- return String.join("\n",
- "<services>",
- " <container version='1.0' id='foo'>",
- " <nodes count='2'/>",
- " </container>",
- " <content id='my-content' version='1.0'>",
- " <documents />",
- " <nodes count='2'/>",
- " </content>",
- "</services>");
- }
-
- private String servicesWithContent() {
- return String.join("\n",
- "<services>",
- " <admin version='4.0'>",
- " <adminserver hostalias='node1'/>",
- " </admin>",
- " <content version='1.0' id='my-content'>",
- " <documents />",
- " <nodes count='1' flavor='" + MY_FLAVOR + "' />",
- " </content>",
- "</services>"
- );
- }
-
-}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java
deleted file mode 100644
index 3e5c8a6ef0d..00000000000
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyModelTester.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package com.yahoo.vespa.model.admin.metricsproxy;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
-import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
-import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import com.yahoo.config.provision.Flavor;
-import com.yahoo.config.provisioning.FlavorsConfig;
-import com.yahoo.vespa.model.VespaModel;
-import com.yahoo.vespa.model.admin.monitoring.Metric;
-import com.yahoo.vespa.model.test.VespaModelTester;
-
-import static com.yahoo.vespa.model.admin.monitoring.DefaultMetricsConsumer.VESPA_CONSUMER_ID;
-import static org.junit.Assert.assertEquals;
-
-/**
- * @author gjoranv
- */
-class MetricsProxyModelTester {
-
- static final String MY_TENANT = "mytenant";
- static final String MY_APPLICATION = "myapp";
- static final String MY_INSTANCE = "myinstance";
- static final String MY_FLAVOR = "myflavor";
-
- // Used for all configs that are produced by the container, not the cluster.
- static final String CONTAINER_CONFIG_ID = "admin/metrics/0";
-
- static VespaModel getModel(String servicesXml) {
- var numberOfHosts = 1;
- var tester = new VespaModelTester();
- tester.enableMetricsProxyContainer(true);
- tester.addHosts(numberOfHosts);
- tester.setHosted(false);
- return tester.createModel(servicesXml, true);
- }
-
- static VespaModel getHostedModel(String servicesXml) {
- var numberOfHosts = 2;
- var tester = new VespaModelTester();
- tester.enableMetricsProxyContainer(true);
- tester.addHosts(flavorFromString(MY_FLAVOR), numberOfHosts);
- tester.setHosted(true);
- tester.setApplicationId(MY_TENANT, MY_APPLICATION, MY_INSTANCE);
- return tester.createModel(servicesXml, true);
- }
-
- static boolean checkMetric(ConsumersConfig.Consumer consumer, Metric metric) {
- for (ConsumersConfig.Consumer.Metric m : consumer.metric()) {
- if (metric.name.equals(m.name()) && metric.outputName.equals(m.outputname()))
- return true;
- }
- return false;
- }
-
- static ConsumersConfig.Consumer getCustomConsumer(String servicesXml) {
- ConsumersConfig config = getConsumersConfig(servicesXml);
- assertEquals(2, config.consumer().size());
- for (ConsumersConfig.Consumer consumer : config.consumer()) {
- if (! consumer.name().equals(VESPA_CONSUMER_ID))
- return consumer;
- }
- throw new RuntimeException("Two consumers with the reserved id - this cannot happen.");
- }
-
- static ConsumersConfig getConsumersConfig(String servicesXml) {
- return getConsumersConfig(getModel(servicesXml));
- }
-
- private static ConsumersConfig getConsumersConfig(VespaModel model) {
- String configId = "admin/metrics";
- return new ConsumersConfig((ConsumersConfig.Builder) model.getConfig(new ConsumersConfig.Builder(), configId));
- }
-
- static ApplicationDimensionsConfig getApplicationDimensionsConfig(VespaModel model) {
- String configId = "admin/metrics";
- return new ApplicationDimensionsConfig((ApplicationDimensionsConfig.Builder) model.getConfig(new ApplicationDimensionsConfig.Builder(), configId));
- }
-
- static NodeDimensionsConfig getNodeDimensionsConfig(VespaModel model) {
- return new NodeDimensionsConfig((NodeDimensionsConfig.Builder) model.getConfig(new NodeDimensionsConfig.Builder(), CONTAINER_CONFIG_ID));
- }
-
- static VespaServicesConfig getVespaServicesConfig(String servicesXml) {
- VespaModel model = getModel(servicesXml);
- return new VespaServicesConfig((VespaServicesConfig.Builder) model.getConfig(new VespaServicesConfig.Builder(), CONTAINER_CONFIG_ID));
- }
-
- static RpcConnectorConfig getRpcConnectorConfig(VespaModel model) {
- return new RpcConnectorConfig((RpcConnectorConfig.Builder) model.getConfig(new RpcConnectorConfig.Builder(), CONTAINER_CONFIG_ID));
- }
-
- private static Flavor flavorFromString(String name) {
- return new Flavor(new FlavorsConfig.Flavor(new FlavorsConfig.Flavor.Builder().
- name(name)));
- }
-
-}
diff --git a/configd/src/apps/sentinel/config-handler.cpp b/configd/src/apps/sentinel/config-handler.cpp
index a434835c656..0f624da2bf0 100644
--- a/configd/src/apps/sentinel/config-handler.cpp
+++ b/configd/src/apps/sentinel/config-handler.cpp
@@ -155,6 +155,13 @@ ConfigHandler::doConfigure()
}
}
_services.swap(services);
+ for (auto & entry : services) {
+ Service::UP svc = std::move(entry.second);
+ if (svc && svc->isRunning()) {
+ svc->remove();
+ _orphans[entry.first] = std::move(svc);
+ }
+ }
vespalib::ComponentConfigProducer::Config current("sentinel", _subscriber.getGeneration(), "ok");
_stateApi.myComponents.addConfig(current);
}
@@ -168,7 +175,7 @@ ConfigHandler::doWork()
if (_subscriber.nextGeneration(0)) {
doConfigure();
}
-
+ handleRestarts();
handleCommands();
handleOutputs();
handleChildDeaths();
@@ -183,6 +190,16 @@ ConfigHandler::doWork()
return false;
}
+void
+ConfigHandler::handleRestarts()
+{
+ for (const auto & entry : _services) {
+ Service & svc = *(entry.second);
+ if (svc.wantsRestart()) {
+ svc.start();
+ }
+ }
+}
void
ConfigHandler::handleChildDeaths()
@@ -198,6 +215,7 @@ ConfigHandler::handleChildDeaths()
LOG(debug, "pid %d finished, Service:%s", (int)pid,
service->name().c_str());
service->youExited(status);
+ _orphans.erase(service->name());
} else {
LOG(warning, "Unknown child pid %d exited (wait-status = %d)",
(int)pid, status);
@@ -268,6 +286,12 @@ ConfigHandler::serviceByPid(pid_t pid)
return service;
}
}
+ for (const auto & it : _orphans) {
+ Service *service = it.second.get();
+ if (service->pid() == pid) {
+ return service;
+ }
+ }
return nullptr;
}
diff --git a/configd/src/apps/sentinel/config-handler.h b/configd/src/apps/sentinel/config-handler.h
index a1ae054f888..0af19a6363a 100644
--- a/configd/src/apps/sentinel/config-handler.h
+++ b/configd/src/apps/sentinel/config-handler.h
@@ -29,6 +29,7 @@ private:
ConfigSubscriber _subscriber;
ConfigHandle<SentinelConfig>::UP _sentinelHandle;
ServiceMap _services;
+ ServiceMap _orphans;
std::list<OutputConnection *> _outputConnections;
CommandQueue _cmdQ;
std::unique_ptr<RpcServer> _rpcServer;
@@ -46,6 +47,7 @@ private:
void handleCmd(const Cmd& cmd);
void handleOutputs();
void handleChildDeaths();
+ void handleRestarts();
static int listen(int port);
void configure_port(int port);
diff --git a/configd/src/apps/sentinel/service.cpp b/configd/src/apps/sentinel/service.cpp
index 20c2a6c9b00..941ea690024 100644
--- a/configd/src/apps/sentinel/service.cpp
+++ b/configd/src/apps/sentinel/service.cpp
@@ -72,11 +72,12 @@ Service::reconfigure(const SentinelConfig::Service& config)
delete _config;
_config = new SentinelConfig::Service(config);
- if (_isAutomatic
- && ((_state == READY) || (_state == FINISHED)))
- {
- LOG(debug, "%s: Restarting due to new config", name().c_str());
- start();
+ if ((_state == READY) || (_state == FINISHED) || (_state == RESTARTING)) {
+ resetRestartPenalty();
+ if (_isAutomatic) {
+ LOG(debug, "%s: Restarting due to new config", name().c_str());
+ start();
+ }
}
}
@@ -148,19 +149,14 @@ Service::runCommand(const std::string & command)
}
}
-int
+void
Service::start()
{
- // make sure the service does not restart in a tight loop:
- time_t now = time(0);
- int diff = now - _last_start;
- if (diff < MAX_RESTART_PENALTY) {
- incrementRestartPenalty();
+ if (_state == REMOVING) {
+ LOG(warning, "tried to start '%s' in REMOVING state", name().c_str());
+ return;
}
- if (diff > 10 * MAX_RESTART_PENALTY) {
- resetRestartPenalty();
- }
- now += _restartPenalty; // will delay start this much
+ time_t now = time(0);
_last_start = now;
// make a pipe, close the good ends of it, mark it close-on-exec
@@ -181,7 +177,7 @@ Service::start()
LOG(error, "%s: Attempted to start, but pipe() failed: %s", name().c_str(),
strerror(errno));
setState(FAILED);
- return -1;
+ return;
}
fflush(nullptr);
@@ -196,7 +192,7 @@ Service::start()
close(stdoutpipes[1]);
close(stderrpipes[0]);
close(stderrpipes[1]);
- return -1;
+ return;
}
if (_pid == 0) {
@@ -219,11 +215,6 @@ Service::start()
if (stop()) {
kill(getpid(), SIGTERM);
}
- if (_restartPenalty > 0) {
- LOG(info, "%s: Applying %u sec restart penalty", name().c_str(),
- _restartPenalty);
- sleep(_restartPenalty);
- }
EV_STARTING(name().c_str());
runChild(pipes); // This function should not return.
_exit(EXIT_FAILURE);
@@ -260,8 +251,15 @@ Service::start()
fcntl(stderrpipes[0], F_GETFL) | O_NONBLOCK);
c = new OutputConnection(stderrpipes[0], p);
_outputConnections.push_back(c);
+}
- return (_state == RUNNING) ? 0 : -1;
+void
+Service::remove()
+{
+ LOG(info, "%s: removed from config", name().c_str());
+ setAutomatic(false);
+ terminate(false, false);
+ setState(REMOVING);
}
@@ -290,18 +288,24 @@ Service::youExited(int status)
{
// Someone did a waitpid() and figured out that we exited.
_exitStatus = status;
+ bool expectedDeath = (_state == KILLING || _state == TERMINATING
+ || _state == REMOVING
+ || _state == KILLED || _state == TERMINATED);
if (WIFEXITED(status)) {
LOG(debug, "%s: Exited with exit code %d", name().c_str(),
WEXITSTATUS(status));
EV_STOPPED(name().c_str(), _pid, WEXITSTATUS(status));
setState(FINISHED);
} else if (WIFSIGNALED(status)) {
- bool expectedDeath = (_state == KILLING || _state == TERMINATING
- || _state == KILLED || _state == TERMINATED);
if (expectedDeath) {
EV_STOPPED(name().c_str(), _pid, WTERMSIG(status));
LOG(debug, "%s: Exited expectedly by signal %d", name().c_str(),
WTERMSIG(status));
+ if (_state == TERMINATING) {
+ setState(TERMINATED);
+ } else if (_state == KILLING) {
+ setState(KILLED);
+ }
} else {
EV_CRASH(name().c_str(), _pid, WTERMSIG(status));
setState(FAILED);
@@ -316,18 +320,26 @@ Service::youExited(int status)
_metrics.currentlyRunningServices--;
_metrics.sentinel_running.sample(_metrics.currentlyRunningServices);
- if (_state == TERMINATING) {
- setState(TERMINATED);
- } else if (_state == KILLING) {
- setState(KILLED);
+ if (! expectedDeath) {
+ // make sure the service does not restart in a tight loop:
+ time_t now = time(0);
+ unsigned int diff = now - _last_start;
+ if (diff < MAX_RESTART_PENALTY) {
+ incrementRestartPenalty();
+ }
+ if (diff > 10 * MAX_RESTART_PENALTY) {
+ resetRestartPenalty();
+ }
+ if (diff < _restartPenalty) {
+ LOG(info, "%s: will delay start by %u seconds", name().c_str(), _restartPenalty - diff);
+ }
}
if (_isAutomatic && !stop()) {
// ### Implement some rate limiting here maybe?
LOG(debug, "%s: Restarting.", name().c_str());
- setState(READY);
+ setState(RESTARTING);
_metrics.totalRestartsCounter++;
_metrics.sentinel_restarts.add();
- start();
}
}
@@ -391,6 +403,8 @@ Service::isRunning() const
case KILLED:
case TERMINATED:
case FAILED:
+ case RESTARTING:
+ case REMOVING:
return false;
case STARTING:
@@ -402,6 +416,19 @@ Service::isRunning() const
return true; // this will not be reached
}
+bool
+Service::wantsRestart() const
+{
+ if (_state == RESTARTING) {
+ time_t now = time(0);
+ if (now > _last_start + _restartPenalty) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
void
Service::setAutomatic(bool autoStatus)
{
@@ -418,12 +445,17 @@ Service::incrementRestartPenalty()
if (_restartPenalty > MAX_RESTART_PENALTY) {
_restartPenalty = MAX_RESTART_PENALTY;
}
+ LOG(info, "%s: incremented restart penalty to %u seconds", name().c_str(), _restartPenalty);
}
void
Service::setState(ServiceState state)
{
+ if (_state == REMOVING) {
+ // ignore further changes
+ return;
+ }
if (state != _state) {
LOG(debug, "%s: %s->%s", name().c_str(), stateName(_state), stateName(state));
_rawState = state;
@@ -448,6 +480,8 @@ Service::stateName(ServiceState state) const
case TERMINATED: return "TERMINATED";
case KILLED: return "KILLED";
case FAILED: return "FAILED";
+ case RESTARTING: return "RESTARTING";
+ case REMOVING: return "REMOVING";
}
return "--BAD--";
}
diff --git a/configd/src/apps/sentinel/service.h b/configd/src/apps/sentinel/service.h
index 54bf1105a77..5a188f217ff 100644
--- a/configd/src/apps/sentinel/service.h
+++ b/configd/src/apps/sentinel/service.h
@@ -20,13 +20,14 @@ private:
pid_t _pid;
enum ServiceState { READY, STARTING, RUNNING, TERMINATING, KILLING,
+ RESTARTING, REMOVING,
FINISHED, TERMINATED, KILLED, FAILED } _rawState;
const enum ServiceState& _state;
int _exitStatus;
SentinelConfig::Service *_config;
bool _isAutomatic;
- static const int MAX_RESTART_PENALTY = 1800;
+ static const unsigned int MAX_RESTART_PENALTY = 1800;
unsigned int _restartPenalty;
time_t _last_start;
@@ -55,11 +56,13 @@ public:
int terminate() {
return terminate(true, false);
}
- int start();
+ void start();
+ void remove();
void youExited(int status); // Call this if waitpid says it exited
const vespalib::string & name() const;
const char *stateName() const { return stateName(_state); }
bool isRunning() const;
+ bool wantsRestart() const;
int exitStatus() const { return _exitStatus; }
const SentinelConfig::Service& serviceConfig() const { return *_config; }
void setAutomatic(bool autoStatus);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index 3f2e2e4d36c..55f35de2443 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -53,6 +53,7 @@ import com.yahoo.vespa.config.server.session.SilentDeployLogger;
import com.yahoo.vespa.config.server.tenant.Rotations;
import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.orchestrator.Orchestrator;
import java.io.File;
@@ -300,34 +301,35 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
if (tenant == null) return false;
TenantApplications tenantApplications = tenant.getApplicationRepo();
- if (!tenantApplications.activeApplications().contains(applicationId)) return false;
-
- // Deleting an application is done by deleting the remote session and waiting
- // until the config server where the deployment happened picks it up and deletes
- // the local session
- long sessionId = tenantApplications.requireActiveSessionOf(applicationId);
- RemoteSession remoteSession = getRemoteSession(tenant, sessionId);
- remoteSession.createDeleteTransaction().commit();
-
- log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Waiting for session " + sessionId + " to be deleted");
- // TODO: Add support for timeout in request
- Duration waitTime = Duration.ofSeconds(60);
- if (localSessionHasBeenDeleted(applicationId, sessionId, waitTime)) {
- log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Session " + sessionId + " deleted");
- } else {
- log.log(LogLevel.ERROR, TenantRepository.logPre(applicationId) + "Session " + sessionId + " was not deleted (waited " + waitTime + ")");
- return false;
- }
+ try (Lock lock = tenantApplications.lock(applicationId)) {
+ if ( ! tenantApplications.exists(applicationId)) return false;
+ // Deleting an application is done by deleting the remote session and waiting
+ // until the config server where the deployment happened picks it up and deletes
+ // the local session
+ long sessionId = tenantApplications.requireActiveSessionOf(applicationId);
+ RemoteSession remoteSession = getRemoteSession(tenant, sessionId);
+ remoteSession.createDeleteTransaction().commit();
+
+ log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Waiting for session " + sessionId + " to be deleted");
+ // TODO: Add support for timeout in request
+ Duration waitTime = Duration.ofSeconds(60);
+ if (localSessionHasBeenDeleted(applicationId, sessionId, waitTime)) {
+ log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Session " + sessionId + " deleted");
+ } else {
+ log.log(LogLevel.ERROR, TenantRepository.logPre(applicationId) + "Session " + sessionId + " was not deleted (waited " + waitTime + ")");
+ return false;
+ }
- NestedTransaction transaction = new NestedTransaction();
- transaction.add(new Rotations(tenant.getCurator(), tenant.getPath()).delete(applicationId)); // TODO: Not unit tested
- // (When rotations are updated in zk, we need to redeploy the zone app, on the right config server
- // this is done asynchronously in application maintenance by the node repository)
- transaction.add(tenantApplications.createDeleteTransaction(applicationId));
+ NestedTransaction transaction = new NestedTransaction();
+ transaction.add(new Rotations(tenant.getCurator(), tenant.getPath()).delete(applicationId)); // TODO: Not unit tested
+ // (When rotations are updated in zk, we need to redeploy the zone app, on the right config server
+ // this is done asynchronously in application maintenance by the node repository)
+ transaction.add(tenantApplications.createDeleteTransaction(applicationId));
- hostProvisioner.ifPresent(provisioner -> provisioner.remove(transaction, applicationId));
- transaction.onCommitted(() -> log.log(LogLevel.INFO, "Deleted " + applicationId));
- transaction.commit();
+ hostProvisioner.ifPresent(provisioner -> provisioner.remove(transaction, applicationId));
+ transaction.onCommitted(() -> log.log(LogLevel.INFO, "Deleted " + applicationId));
+ transaction.commit();
+ }
return true;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
index 1f301f06714..4be87253ceb 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
@@ -11,14 +11,18 @@ import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.curator.transaction.CuratorOperations;
import com.yahoo.vespa.curator.transaction.CuratorTransaction;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import java.time.Duration;
import java.util.List;
+import java.util.Map;
import java.util.OptionalLong;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
@@ -27,9 +31,9 @@ import java.util.stream.Collectors;
/**
* The applications of a tenant, backed by ZooKeeper.
*
- * Each application is stored under /config/v2/tenants/&lt;tenant&gt;/applications/&lt;applications&gt;,
- * the root contains the currently active session, if any, and sub-paths /preparing contains the session id
- * of whatever session may be activated next, if any, and /lock is used for synchronizing writes to all these paths.
+ * Each application is stored under /config/v2/tenants/&lt;tenant&gt;/applications/&lt;application&gt;,
+ * the root contains the currently active session, if any. Locks for synchronising writes to these paths, and changes
+ * to the config of this application, are found under /config/v2/tenants/&lt;tenant&gt;/locks/&lt;application&gt;.
*
* @author Ulf Lilleengen
* @author jonmv
@@ -40,16 +44,19 @@ public class TenantApplications {
private final Curator curator;
private final Path applicationsPath;
+ private final Path locksPath;
// One thread pool for all instances of this class
private static final ExecutorService pathChildrenExecutor =
Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory(TenantApplications.class.getName()));
private final Curator.DirectoryCache directoryCache;
private final ReloadHandler reloadHandler;
+ private final Map<ApplicationId, Lock> locks;
- private TenantApplications(Curator curator, Path applicationsPath, ReloadHandler reloadHandler) {
+ private TenantApplications(Curator curator, ReloadHandler reloadHandler, TenantName tenant) {
this.curator = curator;
- this.applicationsPath = applicationsPath;
- curator.create(applicationsPath);
+ this.applicationsPath = TenantRepository.getApplicationsPath(tenant);
+ this.locksPath = TenantRepository.getLocksPath(tenant);
+ this.locks = new ConcurrentHashMap<>(2);
this.reloadHandler = reloadHandler;
this.directoryCache = curator.createDirectoryCache(applicationsPath.getAbsolute(), false, false, pathChildrenExecutor);
this.directoryCache.start();
@@ -57,7 +64,7 @@ public class TenantApplications {
}
public static TenantApplications create(Curator curator, ReloadHandler reloadHandler, TenantName tenant) {
- return new TenantApplications(curator, TenantRepository.getApplicationsPath(tenant), reloadHandler);
+ return new TenantApplications(curator, reloadHandler, tenant);
}
/**
@@ -73,6 +80,10 @@ public class TenantApplications {
.collect(Collectors.toUnmodifiableList());
}
+ public boolean exists(ApplicationId id) {
+ return curator.exists(applicationPath(id));
+ }
+
/** Returns the id of the currently active session for the given application, if any. Throws on unknown applications. */
private OptionalLong activeSessionOf(ApplicationId id) {
String data = curator.getData(applicationPath(id)).map(Utf8::toString)
@@ -94,7 +105,9 @@ public class TenantApplications {
* Creates a node for the given application, marking its existence.
*/
public void createApplication(ApplicationId id) {
- curator.create(applicationPath(id));
+ try (Lock lock = lock(id)) {
+ curator.create(applicationPath(id));
+ }
}
/**
@@ -113,7 +126,7 @@ public class TenantApplications {
* Returns a transaction which deletes this application.
*/
public CuratorTransaction createDeleteTransaction(ApplicationId applicationId) {
- return CuratorTransaction.from(CuratorOperations.delete(applicationPath(applicationId).getAbsolute()), curator);
+ return CuratorTransaction.from(CuratorOperations.deleteAll(applicationPath(applicationId).getAbsolute(), curator), curator);
}
/**
@@ -130,6 +143,14 @@ public class TenantApplications {
directoryCache.close();
}
+ /** Returns the lock for changing the session status of the given application. */
+ public Lock lock(ApplicationId id) {
+ curator.create(lockPath(id));
+ Lock lock = locks.computeIfAbsent(id, __ -> new Lock(lockPath(id).getAbsolute(), curator));
+ lock.acquire(Duration.ofMinutes(1)); // These locks shouldn't be held for very long.
+ return lock;
+ }
+
private void childEvent(CuratorFramework client, PathChildrenCacheEvent event) {
switch (event.getType()) {
case CHILD_ADDED:
@@ -163,4 +184,8 @@ public class TenantApplications {
return applicationsPath.append(id.serializedForm());
}
+ private Path lockPath(ApplicationId id) {
+ return locksPath.append(id.serializedForm());
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
index 01bb4e2dc76..b6e1d1873c9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
@@ -119,9 +119,9 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
prepare();
TimeoutBudget timeoutBudget = new TimeoutBudget(clock, timeout);
- long sessionId = session.getSessionId();
- validateSessionStatus(session);
- try (Lock lock = tenant.getSessionLock(timeout)) {
+
+ try (Lock lock = tenant.getApplicationRepo().lock(session.getApplicationId())) {
+ validateSessionStatus(session);
NestedTransaction transaction = new NestedTransaction();
transaction.add(deactivateCurrentActivateNew(applicationRepository.getActiveSession(session.getApplicationId()), session, ignoreSessionStaleFailure));
@@ -129,13 +129,15 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
hostProvisioner.get().activate(transaction, session.getApplicationId(), session.getAllocatedHosts().getHosts());
}
transaction.commit();
- session.waitUntilActivated(timeoutBudget);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new InternalServerException("Error activating application", e);
}
- log.log(LogLevel.INFO, session.logPre() + "Session " + sessionId +
+
+ session.waitUntilActivated(timeoutBudget);
+
+ log.log(LogLevel.INFO, session.logPre() + "Session " + session.getSessionId() +
" activated successfully using " +
( hostProvisioner.isPresent() ? hostProvisioner.get() : "no host provisioner" ) +
". Config generation " + session.getMetaData().getGeneration());
@@ -153,7 +155,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
/** Exposes the session of this for testing only */
public LocalSession session() { return session; }
-
+
private long validateSessionStatus(LocalSession localSession) {
long sessionId = localSession.getSessionId();
if (Session.Status.NEW.equals(localSession.getStatus())) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
index 8838daeb32e..26f437920ad 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
@@ -70,10 +70,7 @@ public class RemoteSession extends Session {
}
public synchronized ApplicationSet ensureApplicationLoaded() {
- if (applicationSet == null) {
- applicationSet = loadApplication();
- }
- return applicationSet;
+ return applicationSet == null ? applicationSet = loadApplication() : applicationSet;
}
public Session.Status getStatus() {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
index a68f4a396cd..88e71d7ddd1 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
@@ -11,10 +11,8 @@ import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
import com.yahoo.vespa.config.server.session.SessionFactory;
import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.curator.Lock;
import org.apache.zookeeper.data.Stat;
-import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
@@ -30,7 +28,7 @@ public class Tenant implements TenantHandlerProvider {
static final String SESSIONS = "sessions";
static final String APPLICATIONS = "applications";
- static final String SESSION_LOCK_PATH = "activateLock";
+ static final String LOCKS = "locks";
private final TenantName name;
private final RemoteSessionRepo remoteSessionRepo;
@@ -38,7 +36,6 @@ public class Tenant implements TenantHandlerProvider {
private final SessionFactory sessionFactory;
private final LocalSessionRepo localSessionRepo;
private final TenantApplications applicationRepo;
- private final Lock sessionLock;
private final RequestHandler requestHandler;
private final ReloadHandler reloadHandler;
private final TenantFileSystemDirs tenantFileSystemDirs;
@@ -61,7 +58,6 @@ public class Tenant implements TenantHandlerProvider {
this.remoteSessionRepo = remoteSessionRepo;
this.sessionFactory = sessionFactory;
this.localSessionRepo = localSessionRepo;
- this.sessionLock = createLock(curator, path);
this.applicationRepo = applicationRepo;
this.tenantFileSystemDirs = tenantFileSystemDirs;
this.curator = curator;
@@ -110,14 +106,6 @@ public class Tenant implements TenantHandlerProvider {
return localSessionRepo;
}
- /**
- * This lock allows activation and deactivation of sessions under this tenant.
- */
- public Lock getSessionLock(Duration timeout) {
- sessionLock.acquire(timeout);
- return sessionLock;
- }
-
@Override
public String toString() {
return getName().value();
@@ -165,10 +153,4 @@ public class Tenant implements TenantHandlerProvider {
curator.delete(path);
}
- private static Lock createLock(Curator curator, Path tenantPath) {
- Path lockPath = tenantPath.append(SESSION_LOCK_PATH);
- curator.create(lockPath);
- return new Lock(lockPath.getAbsolute(), curator);
- }
-
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
index 9c74c9c1e67..53d01fdf933 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
@@ -62,6 +62,7 @@ public class TenantRepository {
private static final TenantName DEFAULT_TENANT = TenantName.defaultName();
private static final Path tenantsPath = Path.fromString("/config/v2/tenants/");
+ private static final Path locksPath = Path.fromString("/config/v2/locks/");
private static final Path vespaPath = Path.fromString("/vespa");
private static final Duration checkForRemovedApplicationsInterval = Duration.ofMinutes(1);
private static final Logger log = Logger.getLogger(TenantRepository.class.getName());
@@ -106,6 +107,7 @@ public class TenantRepository {
curator.framework().getConnectionStateListenable().addListener(this::stateChanged);
curator.create(tenantsPath);
+ curator.create(locksPath);
createSystemTenants(configserverConfig);
curator.create(vespaPath);
@@ -261,8 +263,10 @@ public class TenantRepository {
* @param name name of the tenant
*/
private synchronized void writeTenantPath(TenantName name) {
- Path tenantPath = getTenantPath(name);
- curator.createAtomically(tenantPath, tenantPath.append(Tenant.SESSIONS), tenantPath.append(Tenant.APPLICATIONS));
+ curator.createAtomically(TenantRepository.getTenantPath(name),
+ TenantRepository.getSessionsPath(name),
+ TenantRepository.getApplicationsPath(name),
+ TenantRepository.getLocksPath(name));
}
/**
@@ -406,4 +410,11 @@ public class TenantRepository {
return getTenantPath(tenantName).append(Tenant.APPLICATIONS);
}
+ /**
+ * Gets zookeeper path for locks for a tenant's applications. This is never cleaned, but shouldn't be a problem.
+ */
+ public static Path getLocksPath(TenantName tenantName) {
+ return locksPath.append(tenantName.value());
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
index fe34e6c361d..1430475e486 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandler.java
@@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
+import java.util.OptionalLong;
import java.util.Set;
import com.yahoo.component.Version;
@@ -31,6 +32,7 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.monitoring.Metrics;
import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.Lock;
/**
* A per tenant request handler, for handling reload (activate application) and getConfig requests for
@@ -99,18 +101,31 @@ public class TenantRequestHandler implements RequestHandler, ReloadHandler, Host
*/
@Override
public void reloadConfig(ApplicationSet applicationSet) {
- setLiveApp(applicationSet);
- notifyReloadListeners(applicationSet);
+ ApplicationId id = applicationSet.getId();
+ try (Lock lock = applications.lock(id)) {
+ if ( ! applications.exists(id))
+ return; // Application was deleted before activation.
+ if (applicationSet.getApplicationGeneration() != applications.requireActiveSessionOf(id))
+ return; // Application activated a new session before we got here.
+
+ setLiveApp(applicationSet);
+ notifyReloadListeners(applicationSet);
+ }
}
@Override
public void removeApplication(ApplicationId applicationId) {
- if (applicationMapper.hasApplication(applicationId, clock.instant())) {
- applicationMapper.remove(applicationId);
- hostRegistry.removeHostsForKey(applicationId);
- reloadListenersOnRemove(applicationId);
- tenantMetricUpdater.setApplications(applicationMapper.numApplications());
- metrics.removeMetricUpdater(Metrics.createDimensions(applicationId));
+ try (Lock lock = applications.lock(applicationId)) {
+ if (applications.exists(applicationId))
+ return; // Application was deployed again.
+
+ if (applicationMapper.hasApplication(applicationId, clock.instant())) {
+ applicationMapper.remove(applicationId);
+ hostRegistry.removeHostsForKey(applicationId);
+ reloadListenersOnRemove(applicationId);
+ tenantMetricUpdater.setApplications(applicationMapper.numApplications());
+ metrics.removeMetricUpdater(Metrics.createDimensions(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 4046384005d..9bd5c5f1614 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
@@ -81,6 +81,8 @@ public class TenantRepositoryTest {
@Test
public void testListenersAdded() throws IOException, SAXException {
+ tenantRepository.getTenant(tenant1).getApplicationRepo().createApplication(ApplicationId.defaultId());
+ tenantRepository.getTenant(tenant1).getApplicationRepo().createPutTransaction(ApplicationId.defaultId(), 4).commit();
tenantRepository.getTenant(tenant1).getReloadHandler().reloadConfig(ApplicationSet.fromSingle(
new Application(new VespaModel(MockApplicationPackage.createEmpty()),
new ServerCache(),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
index 96fee482092..6aa5aa7cd70 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java
@@ -176,6 +176,8 @@ public class TenantRequestHandlerTest {
public void testReloadConfig() throws IOException {
ApplicationId applicationId = new ApplicationId.Builder().applicationName(ApplicationName.defaultName()).tenant(tenant).build();
+ server.applications().createApplication(applicationId);
+ server.applications().createPutTransaction(applicationId, 1).commit();
server.reloadConfig(reloadConfig(1));
assertThat(listener.reloaded.get(), is(1));
// Using only payload list for this simple test
@@ -195,6 +197,7 @@ public class TenantRequestHandlerTest {
listener.reloaded.set(0);
feedApp(app2, 2, defaultApp(), true);
+ server.applications().createPutTransaction(applicationId, 2).commit();
server.reloadConfig(reloadConfig(2L));
configResponse = getConfigResponse(SimpletypesConfig.class, server, defaultApp(), vespaVersion, "");
assertTrue(configResponse.isInternalRedeploy());
@@ -206,19 +209,34 @@ public class TenantRequestHandlerTest {
@Test
public void testRemoveApplication() {
+ ApplicationId appId = ApplicationId.from(tenant.value(), "default", "default");
server.reloadConfig(reloadConfig(1));
+ assertThat(listener.reloaded.get(), is(0));
+
+ server.applications().createApplication(appId);
+ server.applications().createPutTransaction(appId, 1).commit();
+ server.reloadConfig(reloadConfig(1));
+ assertThat(listener.reloaded.get(), is(1));
+
+ assertThat(listener.removed.get(), is(0));
+
+ server.removeApplication(appId);
assertThat(listener.removed.get(), is(0));
- server.removeApplication(new ApplicationId.Builder().applicationName(ApplicationName.defaultName()).tenant(tenant).build());
+
+ server.applications().createDeleteTransaction(appId).commit();
+ server.removeApplication(appId);
assertThat(listener.removed.get(), is(1));
}
@Test
public void testResolveForAppId() {
long id = 1L;
- SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, TenantRepository.getSessionsPath(tenant).append(String.valueOf(id)));
ApplicationId appId = new ApplicationId.Builder()
.tenant(tenant)
.applicationName("myapp").instanceName("myinst").build();
+ server.applications().createApplication(appId);
+ server.applications().createPutTransaction(appId, 1).commit();
+ SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, TenantRepository.getSessionsPath(tenant).append(String.valueOf(id)));
zkc.writeApplicationId(appId);
RemoteSession session = new RemoteSession(appId.tenant(), id, componentRegistry, zkc);
server.reloadConfig(session.ensureApplicationLoaded());
@@ -256,6 +274,8 @@ public class TenantRequestHandlerTest {
}
private void feedAndReloadApp(File appDir, long sessionId, ApplicationId appId) throws IOException {
+ server.applications().createApplication(appId);
+ server.applications().createPutTransaction(appId, sessionId).commit();
feedApp(appDir, sessionId, appId, false);
SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, TenantRepository.getSessionsPath(tenant).append(String.valueOf(sessionId)));
zkc.writeApplicationId(appId);
@@ -291,9 +311,11 @@ public class TenantRequestHandlerTest {
@Test
public void testHasApplication() {
assertdefaultAppNotFound();
+ ApplicationId appId = ApplicationId.from(tenant.value(), "default", "default");
+ server.applications().createApplication(appId);
+ server.applications().createPutTransaction(appId, 1).commit();
server.reloadConfig(reloadConfig(1));
- assertTrue(server.hasApplication(new ApplicationId.Builder().applicationName(ApplicationName.defaultName()).tenant(tenant).build(),
- Optional.of(vespaVersion)));
+ assertTrue(server.hasApplication(appId, Optional.of(vespaVersion)));
}
private void assertdefaultAppNotFound() {
@@ -302,10 +324,13 @@ public class TenantRequestHandlerTest {
@Test
public void testMultipleApplicationsReload() {
+ ApplicationId appId = ApplicationId.from(tenant.value(), "foo", "default");
assertdefaultAppNotFound();
+ server.applications().createApplication(appId);
+ server.applications().createPutTransaction(appId, 1).commit();
server.reloadConfig(reloadConfig(1, "foo"));
assertdefaultAppNotFound();
- assertTrue(server.hasApplication(new ApplicationId.Builder().tenant(tenant).applicationName("foo").build(),
+ assertTrue(server.hasApplication(appId,
Optional.of(vespaVersion)));
assertThat(server.resolveApplicationId("doesnotexist"), is(ApplicationId.defaultId()));
assertThat(server.resolveApplicationId("mytesthost"), is(new ApplicationId.Builder()
@@ -318,6 +343,8 @@ public class TenantRequestHandlerTest {
assertdefaultAppNotFound();
VespaModel model = new VespaModel(FilesApplicationPackage.fromFile(new File("src/test/apps/app")));
+ server.applications().createApplication(ApplicationId.defaultId());
+ server.applications().createPutTransaction(ApplicationId.defaultId(), 1).commit();
server.reloadConfig(ApplicationSet.fromSingle(new Application(model,
new ServerCache(),
1,
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
index c64d1f9f89b..f766e9331e9 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
@@ -70,11 +70,21 @@ enum PathGroup {
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service/{*}",
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/{*}"),
- /** Path used to restart application nodes. */ // TODO move to the above when everyone is on new pipeline.
- applicationRestart(Matcher.tenant,
+ /** Path used to restart development nodes. */
+ developmentRestart(Matcher.tenant,
Matcher.application,
Optional.of("/api"),
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/restart"),
+ "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{ignored}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{ignored}/restart"),
+
+ /** Path used to restart production nodes. */
+ productionRestart(Matcher.tenant,
+ Matcher.application,
+ Optional.of("/api"),
+ "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{ignored}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/test/region/{region}/instance/{ignored}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/staging/region/{region}/instance/{ignored}/restart"),
+
/** Paths used for development deployments. */
developmentDeployment(Matcher.tenant,
Matcher.application,
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java
index 15745d69dc5..290382c6e6c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java
@@ -80,12 +80,12 @@ enum Policy {
/** Full access to application information and settings. */
applicationOperations(Privilege.grant(Action.write())
- .on(PathGroup.applicationInfo, PathGroup.applicationRestart)
+ .on(PathGroup.applicationInfo, PathGroup.productionRestart)
.in(SystemName.all())),
/** Full access to application development deployments. */
developmentDeployment(Privilege.grant(Action.all())
- .on(PathGroup.developmentDeployment)
+ .on(PathGroup.developmentDeployment, PathGroup.developmentRestart)
.in(SystemName.all())),
/** Full access to application production deployments. */
@@ -105,7 +105,7 @@ enum Policy {
/** Full access to the additional tasks needed for continuous deployment. */
deploymentPipeline(Privilege.grant(Action.all()) // TODO remove when everyone is on new pipeline.
- .on(PathGroup.buildService, PathGroup.applicationRestart)
+ .on(PathGroup.buildService, PathGroup.productionRestart)
.in(SystemName.all())),
/** Read access to all information in select systems. */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 16c39a7e601..47fa73c3493 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -964,14 +964,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
* Deploy direct is when we want to redeploy the current application - retrieve version
* info from the application package before deploying
*/
- if(deployDirectly && !applicationPackage.isPresent() && !applicationVersion.isPresent() && !vespaVersion.isPresent()) {
+ if(deployDirectly && applicationPackage.isEmpty() && applicationVersion.isEmpty() && vespaVersion.isEmpty()) {
// Redeploy the existing deployment with the same versions.
Optional<Deployment> deployment = controller.applications().get(applicationId)
.map(Application::deployments)
.flatMap(deployments -> Optional.ofNullable(deployments.get(zone)));
- if(!deployment.isPresent())
+ if(deployment.isEmpty())
throw new IllegalArgumentException("Can't redeploy application, no deployment currently exist");
ApplicationVersion version = deployment.get().applicationVersion();
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 0612cee040c..99a0a8a4d4c 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -161,6 +161,12 @@ public class Flags {
"Takes effect on deployment through controller",
APPLICATION_ID);
+ public static final UnboundStringFlag SILLY_ROUTING = defineStringFlag(
+ "silly-routing", "silly-default",
+ "Enables \"silly routing\" for the named application (in tenant:application:instance form)",
+ "Takes effect on next update to routing config",
+ HOSTNAME);
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
index e76115931a3..ca812f92c49 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
@@ -3,7 +3,9 @@ package ai.vespa.hosted.api;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.security.SslContextBuilder;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
@@ -26,6 +28,8 @@ import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
+import static ai.vespa.hosted.api.Method.DELETE;
+import static ai.vespa.hosted.api.Method.GET;
import static ai.vespa.hosted.api.Method.POST;
import static java.net.http.HttpRequest.BodyPublishers.ofByteArray;
import static java.net.http.HttpRequest.BodyPublishers.ofInputStream;
@@ -60,7 +64,7 @@ public abstract class ControllerHttpClient {
return new MutualTlsControllerHttpClient(endpoint, privateKeyFile, certificateFile);
}
- /** Sends submission to the remote controller and returns the version of the accepted package, or throws if this fails. */
+ /** Sends the given submission to the remote controller and returns the version of the accepted package, or throws if this fails. */
public String submit(Submission submission, TenantName tenant, ApplicationName application) {
return toMessage(send(request(HttpRequest.newBuilder(applicationPath(tenant, application).resolve("submit"))
.timeout(Duration.ofMinutes(30)),
@@ -70,6 +74,37 @@ public abstract class ControllerHttpClient {
.addFile("applicationTestZip", submission.applicationTestZip()))));
}
+ /** Sends the given deployment to the given application in the given zone, or throws if this fails. */
+ public DeploymentResult deploy(Deployment deployment, ApplicationId id, ZoneId zone) {
+ return toDeploymentResult(send(request(HttpRequest.newBuilder(deploymentPath(id, zone))
+ .timeout(Duration.ofMinutes(60)),
+ POST,
+ toDataStream(deployment))));
+ }
+
+ /** Deactivates the deployment of the given application in the given zone. */
+ public String deactivate(ApplicationId id, ZoneId zone) {
+ return asText(send(request(HttpRequest.newBuilder(deploymentPath(id, zone))
+ .timeout(Duration.ofSeconds(10)),
+ DELETE)));
+ }
+
+ /** Returns the default {@link Environment#dev} {@link ZoneId}, to use for development deployments. */
+ public ZoneId devZone() {
+ Inspector rootObject = toInspector(send(request(HttpRequest.newBuilder(defaultRegionPath())
+ .timeout(Duration.ofSeconds(10)),
+ GET)));
+ return ZoneId.from("dev", rootObject.field("name").asString());
+ }
+
+ /** Returns the Vespa version to compile against, for a hosted Vespa application. This is its lowest runtime version. */
+ public String compileVersion(ApplicationId id) {
+ return toInspector(send(request(HttpRequest.newBuilder(applicationPath(id.tenant(), id.application()))
+ .timeout(Duration.ofSeconds(10)),
+ GET)))
+ .field("compileVersion").asString();
+ }
+
protected HttpRequest request(HttpRequest.Builder request, Method method, Supplier<InputStream> data) {
return request.method(method.name(), ofInputStream(data)).build();
}
@@ -86,12 +121,12 @@ public abstract class ControllerHttpClient {
return request(request.setHeader("Content-Type", data.contentType()), method, data::data);
}
- private URI apiPath() {
+ private URI applicationApiPath() {
return concatenated(endpoint, "application", "v4");
}
private URI tenantPath(TenantName tenant) {
- return concatenated(apiPath(), "tenant", tenant.value());
+ return concatenated(applicationApiPath(), "tenant", tenant.value());
}
private URI applicationPath(TenantName tenant, ApplicationName application) {
@@ -102,6 +137,17 @@ public abstract class ControllerHttpClient {
return concatenated(applicationPath(id.tenant(), id.application()), "instance", id.instance().value());
}
+ private URI deploymentPath(ApplicationId id, ZoneId zone) {
+ return concatenated(applicationPath(id.tenant(), id.application()),
+ "environment", zone.environment().value(),
+ "region", zone.region().value(),
+ "instance", id.instance().value());
+ }
+
+ private URI defaultRegionPath() {
+ return concatenated(endpoint, "zone", "v1", "environment", Environment.dev.value(), "default");
+ }
+
private static URI concatenated(URI base, String... parts) {
return base.resolve(String.join("/", parts) + "/");
}
@@ -119,34 +165,77 @@ public abstract class ControllerHttpClient {
}
}
+ /** Returns a JSON representation of the deployment meta data. */
+ private static String metaToJson(Deployment deployment) {
+ Slime slime = new Slime();
+ Cursor rootObject = slime.setObject();
+
+ if (deployment.repository().isPresent()) {
+ Cursor revisionObject = rootObject.setObject("sourceRevision");
+ deployment.repository().ifPresent(repository -> revisionObject.setString("repository", repository));
+ deployment.branch().ifPresent(branch -> revisionObject.setString("branch", branch));
+ deployment.commit().ifPresent(commit -> revisionObject.setString("commit", commit));
+ deployment.build().ifPresent(build -> rootObject.setLong("buildNumber", build));
+ }
+
+ deployment.version().ifPresent(version -> rootObject.setString("vespaVersion", version));
+
+ if (deployment.ignoreValidationErrors()) rootObject.setBool("ignoreValidationErrors", true);
+ rootObject.setBool("deployDirectly", true);
+
+ return toJson(slime);
+ }
+
/** Returns a JSON representation of the submission meta data. */
private static String metaToJson(Submission submission) {
- try {
- Slime slime = new Slime();
- Cursor rootObject = slime.setObject();
- rootObject.setString("repository", submission.repository());
- rootObject.setString("branch", submission.branch());
- rootObject.setString("commit", submission.commit());
- rootObject.setString("authorEmail", submission.authorEmail());
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- new JsonFormat(true).encode(buffer, slime);
- return buffer.toString(UTF_8);
- }
- catch (IOException e) {
- throw new UncheckedIOException(e);
- }
+ Slime slime = new Slime();
+ Cursor rootObject = slime.setObject();
+ rootObject.setString("repository", submission.repository());
+ rootObject.setString("branch", submission.branch());
+ rootObject.setString("commit", submission.commit());
+ rootObject.setString("authorEmail", submission.authorEmail());
+ submission.projectId().ifPresent(projectId -> rootObject.setLong("projectId", projectId));
+ return toJson(slime);
}
- /** Returns the "message" element contained in the JSON formatted response, if 2XX status code, or throws otherwise. */
- private static String toMessage(HttpResponse<byte[]> response) {
- Inspector rootObject = toSlime(response.body()).get();
- if (response.statusCode() / 100 == 2)
- return rootObject.field("message").asString();
+ /** Returns a multi part data stream with meta data and, if contained in the deployment, an application package. */
+ private static MultiPartStreamer toDataStream(Deployment deployment) {
+ MultiPartStreamer streamer = new MultiPartStreamer();
+ streamer.addJson("deployOptions", metaToJson(deployment));
+ deployment.applicationZip().ifPresent(zip -> streamer.addFile("applicationZip", zip));
+ return streamer;
+ }
+
+ private static String asText(HttpResponse<byte[]> response) {
+ toInspector(response);
+ return new String(response.body(), UTF_8);
+ }
- else {
+ /** Returns an {@link Inspector} for the assumed JSON formatted response, or throws if the status code is non-2XX. */
+ private static Inspector toInspector(HttpResponse<byte[]> response) {
+ Inspector rootObject = toSlime(response.body()).get();
+ if (response.statusCode() / 100 != 2)
throw new RuntimeException(response.request() + " returned code " + response.statusCode() +
" (" + rootObject.field("error-code").asString() + "): " +
rootObject.field("message").asString());
+
+ return rootObject;
+ }
+
+ /** Returns the "message" element contained in the JSON formatted response, if 2XX status code, or throws otherwise. */
+ private static String toMessage(HttpResponse<byte[]> response) {
+ return toInspector(response).field("message").asString();
+ }
+
+ private static DeploymentResult toDeploymentResult(HttpResponse<byte[]> response) {
+ try {
+ Inspector responseObject = toInspector(response);
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ new JsonFormat(false).encode(buffer, responseObject); // Pretty-print until done properly.
+ return new DeploymentResult(buffer.toString(UTF_8));
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(e);
}
}
@@ -154,6 +243,17 @@ public abstract class ControllerHttpClient {
return new JsonDecoder().decode(new Slime(), data);
}
+ private static String toJson(Slime slime) {
+ try {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ new JsonFormat(true).encode(buffer, slime);
+ return buffer.toString(UTF_8);
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
/** Client that signs requests with a private key whose public part is assigned to an application in the remote controller. */
private static class SigningControllerHttpClient extends ControllerHttpClient {
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java
new file mode 100644
index 00000000000..8f981ca5f05
--- /dev/null
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java
@@ -0,0 +1,67 @@
+package ai.vespa.hosted.api;
+
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.OptionalLong;
+
+/**
+ * A deployment intended for hosted Vespa, containing an application package and some meta data.
+ */
+public class Deployment {
+
+ // Deployment options
+ private final Optional<String> version;
+ private final boolean ignoreValidationErrors;
+
+ // Provide an application package ...
+ private final Optional<Path> applicationZip;
+
+ // ... or reference a previously submitted one.
+ private final Optional<String> repository;
+ private final Optional<String> branch;
+ private final Optional<String> commit;
+ private final OptionalLong build;
+
+ private Deployment(Optional<String> version, boolean ignoreValidationErrors, Optional<Path> applicationZip,
+ Optional<String> repository, Optional<String> branch, Optional<String> commit, OptionalLong build) {
+ this.version = version;
+ this.ignoreValidationErrors = ignoreValidationErrors;
+ this.applicationZip = applicationZip;
+ this.repository = repository;
+ this.branch = branch;
+ this.commit = commit;
+ this.build = build;
+ }
+
+
+ /** Returns a deployment which will use the provided application package. */
+ public static Deployment ofPackage(Path applicationZipFile) {
+ return new Deployment(Optional.empty(), false, Optional.of(applicationZipFile),
+ Optional.empty(), Optional.empty(), Optional.empty(), OptionalLong.empty());
+ }
+
+ /** Returns a deployment which will use the previously submitted package with the given reference. */
+ public static Deployment ofReference(String repository, String branch, String commit, long build) {
+ return new Deployment(Optional.empty(), false, Optional.empty(),
+ Optional.of(repository), Optional.of(branch), Optional.of(commit), OptionalLong.of(build));
+ }
+
+ /** Returns a copy of this which will have the specified Vespa version on its nodes. */
+ public Deployment atVersion(String vespaVersion) {
+ return new Deployment(Optional.of(vespaVersion), ignoreValidationErrors, applicationZip, repository, branch, commit, build);
+ }
+
+ /** Returns a copy of this which will additionally ignore validation errors upon deployment. */
+ public Deployment ignoringValidationErrors() {
+ return new Deployment(version, true, applicationZip, repository, branch, commit, build);
+ }
+
+ public Optional<String> version() { return version; }
+ public boolean ignoreValidationErrors() { return ignoreValidationErrors; }
+ public Optional<Path> applicationZip() { return applicationZip; }
+ public Optional<String> repository() { return repository; }
+ public Optional<String> branch() { return branch; }
+ public Optional<String> commit() { return commit; }
+ public OptionalLong build() { return build; }
+
+}
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java
new file mode 100644
index 00000000000..63142ffbbf1
--- /dev/null
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java
@@ -0,0 +1,21 @@
+package ai.vespa.hosted.api;
+
+
+/**
+ * Contains information about the result of a {@link Deployment} against a {@link ControllerHttpClient}.
+ *
+ * @author jonmv
+ */
+public class DeploymentResult {
+
+ private final String json; // TODO probably do this properly.
+
+ public DeploymentResult(String json) {
+ this.json = json;
+ }
+
+ public String json() {
+ return json;
+ }
+
+}
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java
index f4cb90176da..8205e8a1b9b 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java
@@ -4,9 +4,10 @@ package ai.vespa.hosted.api;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Path;
+import java.util.OptionalLong;
/**
- * A submission intended for hosted Vespa containing an application package with tests and meta data.
+ * A submission intended for hosted Vespa, containing an application package with tests, and meta data.
*
* @author jonmv
*/
@@ -18,14 +19,16 @@ public class Submission {
private final String authorEmail;
private final Path applicationZip;
private final Path applicationTestZip;
+ private final OptionalLong projectId;
- public Submission(String repository, String branch, String commit, String authorEmail, Path applicationZip, Path applicationTestZip) {
+ public Submission(String repository, String branch, String commit, String authorEmail, Path applicationZip, Path applicationTestZip, OptionalLong projectId) {
this.repository = repository;
this.branch = branch;
this.commit = commit;
this.authorEmail = authorEmail;
this.applicationZip = applicationZip;
this.applicationTestZip = applicationTestZip;
+ this.projectId = projectId;
}
public String repository() { return repository; }
@@ -34,5 +37,6 @@ public class Submission {
public String authorEmail() { return authorEmail; }
public Path applicationZip() { return applicationZip; }
public Path applicationTestZip() { return applicationTestZip; }
+ public OptionalLong projectId() { return projectId; }
}
diff --git a/metrics-proxy/OWNERS b/metrics-proxy/OWNERS
deleted file mode 100644
index 3b2ba1ede81..00000000000
--- a/metrics-proxy/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-gjoranv
diff --git a/metrics-proxy/README b/metrics-proxy/README
deleted file mode 100644
index 4008cb1aa5d..00000000000
--- a/metrics-proxy/README
+++ /dev/null
@@ -1 +0,0 @@
-The metrics proxy provides a single point of access for metrics from all Vespa services.
diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml
deleted file mode 100644
index 88c250f31bd..00000000000
--- a/metrics-proxy/pom.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0"?>
-<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>parent</artifactId>
- <version>7-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
- </parent>
- <artifactId>metrics-proxy</artifactId>
- <packaging>container-plugin</packaging>
- <version>7-SNAPSHOT</version>
- <dependencies>
-
- <!-- provided -->
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.google.inject</groupId>
- <artifactId>guice</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>annotations</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>component</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-lib</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-di</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>jrt</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespajlib</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespalog</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>yolean</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.json</groupId>
- <artifactId>json</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <!-- compile scope -->
-
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>http-utils</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- </dependency>
-
- <!-- test scope -->
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-class-plugin</artifactId>
- <version>${project.version}</version>
- <executions>
- <execution>
- <id>config-gen</id>
- <goals>
- <goal>config-gen</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <packagePrefix>ai.vespa.</packagePrefix>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
- </configuration>
- </plugin>
-
- <plugin>
- <!-- Only added to make IntelliJ use correct language level -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsConsumers.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsConsumers.java
deleted file mode 100644
index 564de0806ca..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsConsumers.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.core;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collector;
-
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static com.yahoo.stream.CustomCollectors.toLinkedMap;
-import static java.util.Collections.unmodifiableSet;
-import static java.util.stream.Collectors.collectingAndThen;
-
-/**
- * Contains metrics consumers and their metrics, and mappings between these.
- * All collections are final and immutable.
- *
- * @author gjoranv
- */
-public class MetricsConsumers {
-
- // All metrics for each consumer.
- private final Map<ConsumerId, List<Consumer.Metric>> consumerMetrics;
-
- // All consumers for each metric (more useful than the opposite map).
- private final Map<Consumer.Metric, List<ConsumerId>> consumersByMetric;
-
- public MetricsConsumers(ConsumersConfig config) {
- consumerMetrics = config.consumer().stream().collect(
- toUnmodifiableLinkedMap(consumer -> toConsumerId(consumer.name()), Consumer::metric));
-
- consumersByMetric = createConsumersByMetric(consumerMetrics);
- }
-
- /**
- * @param consumer The consumer
- * @return The metrics for the given consumer.
- */
- public List<Consumer.Metric> getMetricDefinitions(ConsumerId consumer) {
- return consumerMetrics.get(consumer);
- }
-
- public Map<Consumer.Metric, List<ConsumerId>> getConsumersByMetric() {
- return consumersByMetric;
- }
-
- public Set<ConsumerId> getAllConsumers() {
- return unmodifiableSet(consumerMetrics.keySet());
- }
-
- /**
- * Helper function to create mapping from metric to consumers.
- * TODO: consider reversing the mapping in metrics-consumers.def instead: metric{}.consumer[]
- */
- private static Map<Consumer.Metric, List<ConsumerId>>
- createConsumersByMetric(Map<ConsumerId, List<Consumer.Metric>> metricsByConsumer) {
- Map<Consumer.Metric, List<ConsumerId>> consumersByMetric = new LinkedHashMap<>();
- metricsByConsumer.forEach(
- (consumer, metrics) -> metrics.forEach(
- metric -> consumersByMetric.computeIfAbsent(metric, unused -> new ArrayList<>())
- .add(consumer)));
- return Collections.unmodifiableMap(consumersByMetric);
- }
-
- public static <T, K, U> Collector<T, ?, Map<K, U>> toUnmodifiableLinkedMap(Function<? super T, ? extends K> keyMapper,
- Function<? super T, ? extends U> valueMapper) {
- return collectingAndThen(toLinkedMap(keyMapper, valueMapper), Collections::unmodifiableMap);
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsManager.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsManager.java
deleted file mode 100644
index fe823c72127..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/MetricsManager.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.core;
-
-import ai.vespa.metricsproxy.metric.ExternalMetrics;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import ai.vespa.metricsproxy.service.VespaService;
-import ai.vespa.metricsproxy.service.VespaServices;
-import com.yahoo.component.Vtag;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-import static ai.vespa.metricsproxy.metric.ExternalMetrics.extractConfigserverDimensions;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static com.yahoo.log.LogLevel.DEBUG;
-import static java.util.stream.Collectors.toList;
-
-/**
- * Retrieves metrics and performs necessary conversions and additions of metadata.
- *
- * @author gjoranv
- */
-public class MetricsManager {
- private static Logger log = Logger.getLogger(MetricsManager.class.getName());
-
- static final DimensionId VESPA_VERSION = toDimensionId("vespaVersion");
-
- private final VespaServices vespaServices;
- private final VespaMetrics vespaMetrics;
- private final ExternalMetrics externalMetrics;
- private final ApplicationDimensions applicationDimensions;
- private final NodeDimensions nodeDimensions;
-
- private volatile Map<DimensionId, String> extraDimensions = new HashMap<>();
-
- public MetricsManager(VespaServices vespaServices,
- VespaMetrics vespaMetrics,
- ExternalMetrics externalMetrics,
- ApplicationDimensions applicationDimensions,
- NodeDimensions nodeDimensions) {
- this.vespaServices = vespaServices;
- this.vespaMetrics = vespaMetrics;
- this.externalMetrics = externalMetrics;
- this.applicationDimensions = applicationDimensions;
- this.nodeDimensions = nodeDimensions;
- }
-
- /**
- * Returns all metrics for the given service that are whitelisted for the given consumer.
- */
- public String getMetricNamesForServiceAndConsumer(String service, ConsumerId consumer) {
- return vespaMetrics.getMetricNames(vespaServices.getMonitoringServices(service), consumer);
- }
-
- public String getMetricsByConfigId(String configId) {
- List<VespaService> services = vespaServices.getInstancesById(configId);
- vespaServices.updateServices(services);
-
- return vespaMetrics.getMetricsAsString(services);
- }
-
- /**
- * Returns the metrics for the given services. The empty list is returned if no services are given.
- *
- * @param services The services to retrieve metrics for.
- * @return Metrics for all matching services.
- */
- public List<MetricsPacket> getMetrics(List<VespaService> services, Instant startTime) {
- if (services.isEmpty()) return Collections.emptyList();
- vespaServices.updateServices(services);
-
- List<MetricsPacket.Builder> result = vespaMetrics.getMetrics(services);
- log.log(DEBUG, () -> "Got " + result.size() + " metrics packets for vespa services.");
-
- List<MetricsPacket.Builder> externalPackets = externalMetrics.getMetrics().stream()
- .filter(MetricsPacket.Builder::hasMetrics)
- .collect(toList());
- log.log(DEBUG, () -> "Got " + externalPackets.size() + " external metrics packets with whitelisted metrics.");
-
- result.addAll(externalPackets);
-
- return result.stream()
- .map(builder -> builder.putDimensionsIfAbsent(getGlobalDimensions()))
- .map(builder -> builder.putDimensionsIfAbsent(extraDimensions))
- .map(builder -> adjustTimestamp(builder, startTime))
- .map(MetricsPacket.Builder::build)
- .collect(Collectors.toList());
- }
-
- /**
- * Returns a merged map of all global dimensions.
- */
- private Map<DimensionId, String> getGlobalDimensions() {
- Map<DimensionId, String> globalDimensions = new LinkedHashMap<>(applicationDimensions.getDimensions());
- globalDimensions.putAll(nodeDimensions.getDimensions());
- globalDimensions.put(VESPA_VERSION, Vtag.currentVersion.toFullString());
- return globalDimensions;
- }
-
- /**
- * If the metrics packet is less than one minute newer or older than the given startTime,
- * set its timestamp to the given startTime. This is done to ensure that metrics retrieved
- * from different sources for this invocation get the same timestamp, and a timestamp as close
- * as possible to the invocation from the external metrics retrieving client. The assumption
- * is that the client requests metrics periodically every minute.
- * <p>
- * However, if the timestamp of the packet is too far off in time, we don't adjust it because
- * we would otherwise be masking a real problem with retrieving the metrics.
- */
- static MetricsPacket.Builder adjustTimestamp(MetricsPacket.Builder builder, Instant startTime) {
- Duration age = Duration.between(startTime, builder.getTimestamp());
- if (age.abs().minusMinutes(1).isNegative())
- builder.timestamp(startTime.getEpochSecond());
- return builder;
- }
-
- /**
- * Returns the health metrics for the given services. The empty list is returned if no services are given.
- *
- * @param services The services to retrieve health metrics for.
- * @return Health metrics for all matching services.
- */
- public List<MetricsPacket> getHealthMetrics(List<VespaService> services) {
- if (services.isEmpty()) return Collections.emptyList();
- vespaServices.updateServices(services);
-
- // TODO: Add global dimensions to health metrics?
- return vespaMetrics.getHealthMetrics(services);
- }
-
- public void setExtraMetrics(List<MetricsPacket.Builder> packets) {
- extraDimensions = extractConfigserverDimensions(packets);
- externalMetrics.setExtraMetrics(packets);
- }
-
- /**
- * Returns a space separated list of all distinct service names.
- */
- public String getAllVespaServices() {
- return vespaServices.getVespaServices().stream()
- .map(VespaService::getServiceName)
- .distinct()
- .collect(Collectors.joining(" "));
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/VespaMetrics.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/VespaMetrics.java
deleted file mode 100644
index becfd9a54ce..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/VespaMetrics.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.core;
-
-
-import ai.vespa.metricsproxy.metric.AggregationKey;
-import ai.vespa.metricsproxy.metric.HealthMetric;
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.MetricsFormatter;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import ai.vespa.metricsproxy.service.VespaService;
-import ai.vespa.metricsproxy.service.VespaServices;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static com.yahoo.log.LogLevel.DEBUG;
-
-/**
- * @author Unknown
- * @author gjoranv
- */
-public class VespaMetrics {
- private static final Logger log = Logger.getLogger(VespaMetrics.class.getPackage().getName());
-
- // MUST be the same as the constant defined in config-model
- public static final ConsumerId VESPA_CONSUMER_ID = toConsumerId("Vespa");
-
- public static final DimensionId METRIC_TYPE_DIMENSION_ID = toDimensionId("metrictype");
- public static final DimensionId INSTANCE_DIMENSION_ID = toDimensionId("instance");
-
- private static final Set<ConsumerId> DEFAULT_CONSUMERS = Collections.singleton(VESPA_CONSUMER_ID);
-
- private final MetricsConsumers metricsConsumers;
-
- private static final MetricsFormatter formatter = new MetricsFormatter(false, false);
-
- public VespaMetrics(MetricsConsumers metricsConsumers, VespaServices vespaServices) {
- this.metricsConsumers = metricsConsumers;
- }
-
- public List<MetricsPacket> getHealthMetrics(List<VespaService> services) {
- List<MetricsPacket> result = new ArrayList<>();
- for (VespaService s : services) {
- HealthMetric h = s.getHealth();
- MetricsPacket.Builder builder = new MetricsPacket.Builder(toServiceId(s.getMonitoringName()))
- .statusCode(h.isOk() ? 0 : 1)
- .statusMessage(h.getMessage())
- .putDimension(METRIC_TYPE_DIMENSION_ID, "health")
- .putDimension(INSTANCE_DIMENSION_ID, s.getInstanceName());
-
- result.add(builder.build());
- }
-
- return result;
- }
-
- /**
- * @param services The services to get metrics for
- * @return A list of metrics packet builders (to allow modification by the caller).
- */
- public List<MetricsPacket.Builder> getMetrics(List<VespaService> services) {
- List<MetricsPacket.Builder> metricsPackets = new ArrayList<>();
-
- log.log(DEBUG, () -> "Updating services prior to fetching metrics, number of services= " + services.size());
-
- Map<ConsumersConfig.Consumer.Metric, List<ConsumerId>> consumersByMetric = metricsConsumers.getConsumersByMetric();
-
- for (VespaService service : services) {
- // One metrics packet for system metrics
- Optional<MetricsPacket.Builder> systemCheck = getSystemMetrics(service);
- systemCheck.ifPresent(metricsPackets::add);
-
- // One metrics packet per set of metrics that share the same dimensions+consumers
- // TODO: Move aggregation into MetricsPacket itself?
- Metrics serviceMetrics = getServiceMetrics(service, consumersByMetric);
- Map<AggregationKey, List<Metric>> aggregatedMetrics =
- aggregateMetrics(service.getDimensions(), serviceMetrics);
-
- aggregatedMetrics.forEach((aggregationKey, metrics) -> {
- MetricsPacket.Builder builder = new MetricsPacket.Builder(toServiceId(service.getMonitoringName()))
- .putMetrics(metrics)
- .putDimension(METRIC_TYPE_DIMENSION_ID, "standard")
- .putDimension(INSTANCE_DIMENSION_ID, service.getInstanceName())
- .putDimensions(aggregationKey.getDimensions());
- setMetaInfo(builder, serviceMetrics.getTimeStamp());
- builder.addConsumers(aggregationKey.getConsumers());
- metricsPackets.add(builder);
- });
- }
-
- return metricsPackets;
- }
-
- /**
- * Returns the metrics to output for the given service, with updated timestamp
- * In order to include a metric, it must exist in the given map of metric to consumers.
- * Each returned metric will contain a collection of consumers that it should be routed to.
- */
- private Metrics getServiceMetrics(VespaService service, Map<ConsumersConfig.Consumer.Metric, List<ConsumerId>> consumersByMetric) {
- Metrics serviceMetrics = new Metrics();
- Metrics allServiceMetrics = service.getMetrics();
- serviceMetrics.setTimeStamp(getMostRecentTimestamp(allServiceMetrics));
- for (Metric candidate : allServiceMetrics.getMetrics()) {
- getConfiguredMetrics(candidate.getName(), consumersByMetric.keySet()).forEach(
- configuredMetric -> serviceMetrics.add(
- metricWithConfigProperties(candidate, configuredMetric, consumersByMetric)));
- }
- return serviceMetrics;
- }
-
- private Map<DimensionId, String> extractDimensions(Map<DimensionId, String> dimensions, List<ConsumersConfig.Consumer.Metric.Dimension> configuredDimensions) {
- if ( ! configuredDimensions.isEmpty()) {
- Map<DimensionId, String> dims = new HashMap<>(dimensions);
- configuredDimensions.forEach(d -> dims.put(toDimensionId(d.key()), d.value()));
- dimensions = Collections.unmodifiableMap(dims);
- }
- return dimensions;
- }
-
- private Set<ConsumerId> extractConsumers(List<ConsumerId> configuredConsumers) {
- Set<ConsumerId> consumers = Collections.emptySet();
- if (configuredConsumers != null) {
- if ( configuredConsumers.size() == 1) {
- consumers = Collections.singleton(configuredConsumers.get(0));
- } else if (configuredConsumers.size() > 1){
- consumers = new HashSet<>();
- consumers.addAll(configuredConsumers);
- consumers = Collections.unmodifiableSet(consumers);
- }
- }
- return consumers;
- }
-
- private Metric metricWithConfigProperties(Metric candidate,
- ConsumersConfig.Consumer.Metric configuredMetric,
- Map<ConsumersConfig.Consumer.Metric, List<ConsumerId>> consumersByMetric) {
- Metric metric = candidate.clone();
- metric.setDimensions(extractDimensions(candidate.getDimensions(), configuredMetric.dimension()));
- metric.setConsumers(extractConsumers(consumersByMetric.get(configuredMetric)));
-
- if (!isNullOrEmpty(configuredMetric.outputname()))
- metric.setName(configuredMetric.outputname());
- return metric;
- }
-
- /**
- * Returns all configured metrics (for any consumer) that have the given id as 'name'.
- */
- private static Set<ConsumersConfig.Consumer.Metric> getConfiguredMetrics(String id,
- Set<ConsumersConfig.Consumer.Metric> configuredMetrics) {
- return configuredMetrics.stream()
- .filter(m -> m.name().equals(id))
- .collect(Collectors.toSet());
- }
-
- private Optional<MetricsPacket.Builder> getSystemMetrics(VespaService service) {
- Metrics systemMetrics = service.getSystemMetrics();
- if (systemMetrics.size() == 0) return Optional.empty();
-
- MetricsPacket.Builder builder = new MetricsPacket.Builder(toServiceId(service.getMonitoringName()));
- setMetaInfo(builder, systemMetrics.getTimeStamp());
-
- builder.putDimension(METRIC_TYPE_DIMENSION_ID, "system")
- .putDimension(INSTANCE_DIMENSION_ID, service.getInstanceName())
- .putDimensions(service.getDimensions())
- .putMetrics(systemMetrics.getMetrics());
-
- builder.addConsumers(metricsConsumers.getAllConsumers());
- return Optional.of(builder);
- }
-
- private long getMostRecentTimestamp(Metrics metrics) {
- long mostRecentTimestamp = 0L;
- for (Metric metric : metrics.getMetrics()) {
- if (metric.getTimeStamp() > mostRecentTimestamp) {
- mostRecentTimestamp = metric.getTimeStamp();
- }
- }
- return mostRecentTimestamp;
- }
-
- private Map<AggregationKey, List<Metric>> aggregateMetrics(Map<DimensionId, String> serviceDimensions,
- Metrics metrics) {
- Map<AggregationKey, List<Metric>> aggregatedMetrics = new HashMap<>();
-
- for (Metric metric : metrics.getMetrics() ) {
- Map<DimensionId, String> mergedDimensions = new LinkedHashMap<>();
- mergedDimensions.putAll(metric.getDimensions());
- mergedDimensions.putAll(serviceDimensions);
- AggregationKey aggregationKey = new AggregationKey(mergedDimensions, metric.getConsumers());
-
- if (aggregatedMetrics.containsKey(aggregationKey)) {
- aggregatedMetrics.get(aggregationKey).add(metric);
- } else {
- List<Metric> ml = new ArrayList<>();
- ml.add(metric);
- aggregatedMetrics.put(aggregationKey, ml);
- }
- }
- return aggregatedMetrics;
- }
-
- private List<ConsumersConfig.Consumer.Metric> getMetricDefinitions(ConsumerId consumer) {
- if (metricsConsumers == null) return Collections.emptyList();
-
- List<ConsumersConfig.Consumer.Metric> definitions = metricsConsumers.getMetricDefinitions(consumer);
- return definitions == null ? Collections.emptyList() : definitions;
- }
-
- private static void setMetaInfo(MetricsPacket.Builder builder, long timestamp) {
- builder.timestamp(timestamp)
- .statusCode(0)
- .statusMessage("Data collected successfully");
- }
-
- /**
- * Returns a string representation of metrics for the given services;
- * a space separated list of key=value.
- */
- public String getMetricsAsString(List<VespaService> services) {
- StringBuilder b = new StringBuilder();
- for (VespaService s : services) {
- for (Metric metric : s.getMetrics().getMetrics()) {
- String key = metric.getName();
- String alias = key;
-
- boolean isForwarded = false;
- for (ConsumersConfig.Consumer.Metric metricConsumer : getMetricDefinitions(VESPA_CONSUMER_ID)) {
- if (metricConsumer.name().equals(key)) {
- alias = metricConsumer.outputname();
- isForwarded = true;
- }
- }
- if (isForwarded) {
- b.append(formatter.format(s, alias, metric.getValue()))
- .append(" ");
- }
- }
- }
- return b.toString();
- }
-
- /**
- * Get all metric names for the given services
- *
- * @return String representation
- */
- public String getMetricNames(List<VespaService> services, ConsumerId consumer) {
- StringBuilder bufferOn = new StringBuilder();
- StringBuilder bufferOff = new StringBuilder();
- for (VespaService s : services) {
-
- for (Metric m : s.getMetrics().getMetrics()) {
- String description = m.getDescription();
- String alias = "";
- boolean isForwarded = false;
-
- for (ConsumersConfig.Consumer.Metric metric : getMetricDefinitions(consumer)) {
- if (metric.name().equals(m.getName())) {
- alias = metric.outputname();
- isForwarded = true;
- if (description.isEmpty()) {
- description = metric.description();
- }
- }
- }
-
- String message = "OFF";
- StringBuilder buffer = bufferOff;
- if (isForwarded) {
- buffer = bufferOn;
- message = "ON";
- }
- buffer.append(m.getName()).append('=').append(message);
- if (!description.isEmpty()) {
- buffer.append(";description=").append(description);
- }
- if (!alias.isEmpty()) {
- buffer.append(";output-name=").append(alias);
- }
- buffer.append(',');
- }
- }
-
- return bufferOn.toString() + bufferOff.toString();
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/package-info.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/package-info.java
deleted file mode 100644
index 617cf0a1525..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/core/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-@ExportPackage
-package ai.vespa.metricsproxy.core;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/AggregationKey.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/AggregationKey.java
deleted file mode 100644
index 9eb1b242535..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/AggregationKey.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * @author gjoranv
- */
-public final class AggregationKey {
-
- private Map<DimensionId, String> dimensions;
- private Set<ConsumerId> consumers;
-
- public AggregationKey(Map<DimensionId, String> dimensions, Set<ConsumerId> consumers) {
- this.dimensions = dimensions;
- this.consumers = consumers;
- }
-
- public Map<DimensionId, String> getDimensions() { return Collections.unmodifiableMap(dimensions); }
-
- public Set<ConsumerId> getConsumers() { return Collections.unmodifiableSet(consumers); }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- AggregationKey that = (AggregationKey) o;
- return Objects.equals(dimensions, that.dimensions) &&
- Objects.equals(consumers, that.consumers);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(dimensions, consumers);
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java
deleted file mode 100644
index 62465909798..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.core.MetricsConsumers;
-import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import ai.vespa.metricsproxy.metric.model.MetricId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import ai.vespa.metricsproxy.metric.model.ServiceId;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static com.yahoo.log.LogLevel.DEBUG;
-import static java.util.stream.Collectors.toCollection;
-
-/**
- * This class is responsible for handling metrics received from external processes.
- *
- * @author gjoranv
- */
-public class ExternalMetrics {
- private static final Logger log = Logger.getLogger(ExternalMetrics.class.getName());
-
- public static final DimensionId ROLE_DIMENSION = toDimensionId("role");
- public static final DimensionId STATE_DIMENSION = toDimensionId("state");
- public static final DimensionId ORCHESTRATOR_STATE_DIMENSION = toDimensionId("orchestratorState");
-
- static final ServiceId VESPA_NODE_SERVICE_ID = toServiceId("vespa.node");
-
- private volatile List<MetricsPacket.Builder> metrics = new ArrayList<>();
- private final MetricsConsumers consumers;
-
- public ExternalMetrics(MetricsConsumers consumers) {
- this.consumers = consumers;
- }
-
- public List<MetricsPacket.Builder> getMetrics() {
- return metrics;
- }
-
- public void setExtraMetrics(List<MetricsPacket.Builder> externalPackets) {
- log.log(DEBUG, () -> "Setting new external metrics with " + externalPackets.size() + " metrics packets.");
- externalPackets.forEach(packet -> {
- packet.addConsumers(consumers.getAllConsumers())
- .service(VESPA_NODE_SERVICE_ID)
- .retainMetrics(metricsToRetain())
- .applyOutputNames(outputNamesById());
- });
- metrics = List.copyOf(externalPackets);
- }
-
- private Set<MetricId> metricsToRetain() {
- return consumers.getConsumersByMetric().keySet().stream()
- .map(configuredMetric -> toMetricId(configuredMetric.name()))
- .collect(toCollection(LinkedHashSet::new));
- }
-
- /**
- * Returns a mapping from metric id to a list of the metric's output names.
- * Metrics that only have their id as output name are included in the output.
- */
- private Map<MetricId, List<String>> outputNamesById() {
- Map<MetricId, List<String>> outputNamesById = new LinkedHashMap<>();
- for (Consumer.Metric metric : consumers.getConsumersByMetric().keySet()) {
- MetricId id = toMetricId(metric.name());
- outputNamesById.computeIfAbsent(id, unused -> new ArrayList<>())
- .add(metric.outputname());
- }
- return outputNamesById;
- }
-
- /**
- * Extracts the node repository dimensions (role, state etc.) from the given packets.
- * If the same dimension exists in multiple packets, this implementation gives no guarantees
- * about which value is returned.
- */
- public static Map<DimensionId, String> extractConfigserverDimensions(Collection<MetricsPacket.Builder> packets) {
- Map<DimensionId, String> dimensions = new HashMap<>();
- for (MetricsPacket.Builder packet : packets) {
- dimensions.putAll(packet.build().dimensions());
- }
- dimensions.keySet().retainAll(Set.of(ROLE_DIMENSION, STATE_DIMENSION, ORCHESTRATOR_STATE_DIMENSION));
- return dimensions;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/HealthMetric.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/HealthMetric.java
deleted file mode 100644
index 41a8c3d414e..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/HealthMetric.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-/**
- * @author Jo Kristian Bergum
- */
-public class HealthMetric {
- private final String message;
- private final String status;
- private final boolean isAlive;
-
- private HealthMetric(String status, String message, boolean isAlive) {
- this.message = message;
- this.status = status;
- this.isAlive = isAlive;
- }
-
- public static HealthMetric get(String status, String message) {
- if (status == null) {
- status = "";
- }
- if (message == null) {
- message = "";
- }
- status = status.toLowerCase();
-
- if (status.equals("up") || status.equals("ok")) {
- return new HealthMetric(status, message, true);
- } else {
- return new HealthMetric(status, message, false);
- }
- }
-
- public static HealthMetric getFailed(String message) {
- return new HealthMetric("down", message, false);
- }
-
- public static HealthMetric getOk(String message) {
- return new HealthMetric("up", message, true);
- }
-
- public String getMessage() {
- return this.message;
- }
-
- public String getStatus() {
- return this.status;
- }
-
- public boolean isOk() {
- return this.isAlive;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metric.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metric.java
deleted file mode 100644
index 59fbe301a49..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metric.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Jo Kristian Bergum
- */
-public class Metric {
- private final long time;
- private final Number value;
- private final String description;
- private String name;
- private Map<DimensionId, String> dimensions;
- private Set<ConsumerId> consumers;
-
- /**
- * Creates a new metric instance
- *
- * @param name The metric name. E.g 'documents'
- * @param value The numeric value
- * @param time The timestamp of this metric in seconds
- */
- public Metric(String name, Number value, long time, Map<DimensionId, String> dimensions, String description) {
- this.time = time;
- this.value = value;
- this.name = name;
- this.dimensions = dimensions;
- this.description = description;
- }
-
- public Metric(String name, Number value, long timestamp) {
- this(name, value, timestamp, Collections.emptyMap(), "");
- }
-
- public Metric(String name, Number value) {
- this(name, value, System.currentTimeMillis() / 1000);
- }
-
- public void setDimensions(Map<DimensionId, String> dimensions) {
- this.dimensions = dimensions;
- }
-
- /**
- * @return A map of the dimensions registered for this metric
- */
- public Map<DimensionId, String> getDimensions() { return dimensions; }
-
- public void setConsumers(Set<ConsumerId> consumers) { this.consumers = consumers; }
-
- /**
- * @return The consumers this metric should be routed to.
- */
- public Set<ConsumerId> getConsumers() { return consumers; }
-
- /**
- * @return The number that this metric name represent
- */
- public Number getValue() {
- return value;
- }
-
- /**
- * Set the name of this metric
- *
- * @param name The name to use for this metric
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * @return The name of the metric
- */
- public String getName() {
- return name;
- }
-
- /**
- * @return The UTC timestamp for when this metric was collected
- */
- public long getTimeStamp() {
- return this.time;
- }
-
- @Override
- public String toString() {
- return "Metric{" +
- "time=" + time +
- ", name=" + name +
- ", value='" + value + '\'' +
- ", dimensions=" + dimensions +
- '}';
- }
-
- @Override
- public Metric clone() {
- return new Metric(name, value, time, new LinkedHashMap<>(dimensions), getDescription());
- }
-
- /**
- * @return the description of this metric
- */
- public String getDescription() {
- return this.description;
- }
-
- /** Return an adjusted (rounded up) time if necessary */
- public static long adjustTime(long timestamp, long now) {
- if ((now == (timestamp+1)) && ((now % 60) == 0)) {
- return now;
- }
- return timestamp;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metrics.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metrics.java
deleted file mode 100644
index ca611368730..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/Metrics.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Once a getter is called, the instance is frozen and no more metrics can be added.
- *
- * @author Unknown
- */
-// TODO: remove timestamp, only used as temporary storage.
-// TODO: instances of this class can probably be replaced by a simple freezable map.
-public class Metrics {
- private final List<Metric> metrics = new ArrayList<>();
- private long timestamp;
- private boolean isFrozen = false;
-
- public Metrics() {
- this(System.currentTimeMillis() / 1000L);
- }
-
- public Metrics(long timestamp) {
- this.timestamp = timestamp;
- }
-
- private void ensureNotFrozen() {
- if (isFrozen) throw new IllegalStateException("Frozen Metrics cannot be modified!");
-
- }
-
- public long getTimeStamp() {
- return this.timestamp;
- }
-
- /**
- * Update the timestamp
- *
- * @param timestamp IN UTC seconds resolution
- */
- public void setTimeStamp(long timestamp) {
- ensureNotFrozen();
- this.timestamp = timestamp;
- }
-
- public void add(Metric m) {
- ensureNotFrozen();
- this.timestamp = m.getTimeStamp();
- this.metrics.add(m);
- }
-
- /**
- * Get the size of the metrics covered. Note that this might also contain expired metrics
- *
- * @return size of metrics
- */
- public int size() {
- return this.metrics.size();
- }
-
- /**
- * TODO: Remove, might be multiple metrics with same name but different dimensions
- *
- * @param key metric name
- * @return the metric, or null
- */
- public Metric getMetric(String key) {
- isFrozen = true;
- for (Metric m: metrics) {
- if (m.getName().equals(key)) {
- return m;
- }
- }
- return null;
- }
-
- public List<Metric> getMetrics() {
- isFrozen = true;
- return Collections.unmodifiableList(metrics);
- }
-
-
- /**
- * Get a single metric based on the metric name
- * TODO: Remove, might be multiple metrics with same name, but different
- *
- * @param key metric name
- * @return The value or null if metric was not found or expired
- */
- public Number get(String key) {
- isFrozen = true;
- Metric m = getMetric(key);
- if (m != null) {
- return m.getValue();
- }
- return null;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- for (Metric m : metrics) {
- sb.append(m.getName()).append(":").append(m.getValue()).append("\n");
- }
- return sb.toString();
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/MetricsFormatter.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/MetricsFormatter.java
deleted file mode 100644
index 8858e21486a..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/MetricsFormatter.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.service.VespaService;
-
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.Locale;
-
-/**
- * Format metrics as required by users of the "getMetricsById" rpc method.
- *
- * @author Unknown
- */
-public class MetricsFormatter {
- private final boolean includeServiceName;
- private final boolean isSystemMetric;
- private final DecimalFormat df = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.ENGLISH));
-
- public MetricsFormatter(boolean includeServiceName, boolean isSystemMetric) {
- this.includeServiceName = includeServiceName;
- this.isSystemMetric = isSystemMetric;
- }
-
- public String format(VespaService service, String name, Number value) {
- StringBuilder sb = new StringBuilder();
-
- if (includeServiceName) {
- sb.append(service.getServiceName()).append(".");
- }
-
- if (isSystemMetric)
- sb.append(toSystemServiceId(service.getConfigId()));
- else
- sb.append(toServiceId(service.getConfigId()));
-
- sb.append(".")
- .append(formatMetricName(name))
- .append("=");
-
- if (value instanceof Double) {
- sb.append(df.format(value.doubleValue()));
- } else {
- sb.append(value.toString());
- }
-
- return sb.toString();
- }
-
- private static String formatMetricName(String name) {
- name = name.replaceAll("\"", "");
- name = name.replaceAll("\\.", "_");
- return name;
- }
-
- // E.g. container/qrserver.1 -> 'container.qrserver.1'
- private static String toServiceId(String configId) {
- return "'" + configId.replace("/", ".") + "'";
- }
-
- // E.g. container/qrserver.1 -> container.'qrserver.1'
- private static String toSystemServiceId(String configId) {
- String name = configId.replace("/", ".");
- name = name.replaceFirst("\\.", ".'") + "'";
- return name;
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/ApplicationDimensions.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/ApplicationDimensions.java
deleted file mode 100644
index ae40f672a32..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/ApplicationDimensions.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.dimensions;
-
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.core.MetricsConsumers.toUnmodifiableLinkedMap;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-
-/**
- * Application-specific but node-agnostic dimensions.
- *
- * @author gjoranv
- */
-public class ApplicationDimensions {
-
- private final Map<DimensionId, String> dimensions;
-
- public ApplicationDimensions(ApplicationDimensionsConfig config) {
- dimensions = config.dimensions().entrySet().stream().collect(
- toUnmodifiableLinkedMap(e -> toDimensionId(e.getKey()), Map.Entry::getValue));
- }
-
- public Map<DimensionId, String> getDimensions() { return dimensions; }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/NodeDimensions.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/NodeDimensions.java
deleted file mode 100644
index d2c1799e148..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/NodeDimensions.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.dimensions;
-
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.core.MetricsConsumers.toUnmodifiableLinkedMap;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-
-/**
- * Node-specific metric dimensions.
- *
- * @author gjoranv
- */
-public class NodeDimensions {
-
- private final Map<DimensionId, String> dimensions;
-
- public NodeDimensions(NodeDimensionsConfig config) {
- dimensions = config.dimensions().entrySet().stream().collect(
- toUnmodifiableLinkedMap(e -> toDimensionId(e.getKey()), Map.Entry::getValue));
- }
-
- public Map<DimensionId, String> getDimensions() { return dimensions; }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/package-info.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/package-info.java
deleted file mode 100644
index f4e5f74313a..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/dimensions/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-@ExportPackage
-package ai.vespa.metricsproxy.metric.dimensions;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ConsumerId.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ConsumerId.java
deleted file mode 100644
index 0d7acd5f354..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ConsumerId.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import java.util.Objects;
-
-/**
- * @author gjoranv
- */
-public class ConsumerId {
- public final String id;
- private ConsumerId(String id) { this.id = id; }
-
- public static ConsumerId toConsumerId(String id) { return new ConsumerId(id); }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ConsumerId that = (ConsumerId) o;
- return Objects.equals(id, that.id);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id);
- }
-
- @Override
- public String toString() {
- return "ConsumerId{" +
- "id='" + id + '\'' +
- '}';
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/DimensionId.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/DimensionId.java
deleted file mode 100644
index 03f4c2c01ff..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/DimensionId.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import java.util.Objects;
-
-/**
- * @author gjoranv
- */
-public class DimensionId {
-
- public final String id;
- private DimensionId(String id) { this.id = id; }
-
- public static DimensionId toDimensionId(String id) { return new DimensionId(id); }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DimensionId that = (DimensionId) o;
- return Objects.equals(id, that.id);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id);
- }
-
- @Override
- public String toString() {
- return "DimensionId{" +
- "id='" + id + '\'' +
- '}';
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricId.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricId.java
deleted file mode 100644
index c93735c7fca..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricId.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import java.util.Objects;
-
-/**
- * @author gjoranv
- */
-public class MetricId {
-
- public final String id;
- private MetricId(String id) { this.id = id; }
-
- public static MetricId toMetricId(String id) { return new MetricId(id); }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- MetricId metricId = (MetricId) o;
- return Objects.equals(id, metricId.id);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id);
- }
-
- @Override
- public String toString() {
- return "MetricId{" +
- "id='" + id + '\'' +
- '}';
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricsPacket.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricsPacket.java
deleted file mode 100644
index fa45c6251f6..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/MetricsPacket.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import ai.vespa.metricsproxy.metric.Metric;
-
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-
-import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
-import static java.util.Collections.unmodifiableList;
-import static java.util.Collections.unmodifiableMap;
-import static java.util.stream.Collectors.joining;
-
-/**
- * Represents a packet of metrics (with meta information) that belong together because they:
- * <ul>
- * <li>share both the same dimensions and consumers, AND</li>
- * <li>represent the same source, e.g. a vespa service or the system hardware.</li>
- * </ul>
- *
- * @author gjoranv
- */
-public class MetricsPacket {
- public final int statusCode;
- public final String statusMessage;
- public final long timestamp;
- public final ServiceId service;
- private final Map<MetricId, Number> metrics;
- private final Map<DimensionId, String> dimensions;
- private final List<ConsumerId> consumers;
-
- private MetricsPacket(int statusCode, String statusMessage, long timestamp, ServiceId service,
- Map<MetricId, Number> metrics, Map<DimensionId, String> dimensions, Set<ConsumerId> consumers ) {
- this.statusCode = statusCode;
- this.statusMessage = statusMessage;
- this.timestamp = timestamp;
- this.service = service;
- this.metrics = metrics;
- this.dimensions = dimensions;
- this.consumers = new ArrayList<>(consumers);
- }
-
- public Map<MetricId, Number> metrics() {
- return unmodifiableMap(metrics);
- }
-
- public Map<DimensionId, String> dimensions() {
- return unmodifiableMap(dimensions);
- }
-
- public List<ConsumerId> consumers() {
- return unmodifiableList(consumers);
- }
-
- @Override
- public String toString() {
- return "MetricsPacket{" +
- "statusCode=" + statusCode +
- ", statusMessage='" + statusMessage + '\'' +
- ", timestamp=" + timestamp +
- ", service=" + service.id +
- ", metrics=" + idMapToString(metrics, id -> id.id) +
- ", dimensions=" + idMapToString(dimensions, id -> id.id) +
- ", consumers=" + consumers.stream().map(id -> id.id).collect(joining(",", "[", "]")) +
- '}';
- }
-
- private static <K,V> String idMapToString(Map<K,V> map, Function<K, String> idMapper) {
- return map.entrySet().stream()
- .map(entry -> idMapper.apply(entry.getKey()) + "=" + entry.getValue())
- .collect(joining(",", "{", "}"));
- }
-
- public static class Builder {
- // Set sensible defaults here, and use null guard in all setters.
- // Except for 'service' for which we require an explicit non-null value.
- private ServiceId service;
- private int statusCode = 0;
- private String statusMessage = "<null>";
- private long timestamp = 0L;
- private Map<MetricId, Number> metrics = new LinkedHashMap<>();
- private final Map<DimensionId, String> dimensions = new LinkedHashMap<>();
- private final Set<ConsumerId> consumers = new LinkedHashSet<>();
-
- public Builder(ServiceId service) {
- Objects.requireNonNull(service, "Service cannot be null.");
- this.service = service;
- }
-
- public Builder service(ServiceId service) {
- if (service == null) throw new IllegalArgumentException("Service cannot be null.");
- this.service = service;
- return this;
- }
-
- public Builder statusCode(Integer statusCode) {
- if (statusCode != null) this.statusCode = statusCode;
- return this;
- }
-
- public Builder statusMessage(String statusMessage) {
- if (statusMessage != null) this.statusMessage = statusMessage;
- return this;
- }
-
- public Builder timestamp(Long timestamp) {
- if (timestamp != null) this.timestamp = timestamp;
- return this;
- }
-
- public Builder putMetrics(Collection<Metric> extraMetrics) {
- if (extraMetrics != null)
- extraMetrics.forEach(metric -> metrics.put(toMetricId(metric.getName()),
- metric.getValue().doubleValue()));
- return this;
- }
-
- public Builder putMetric(MetricId id, Number value) {
- metrics.put(id, value);
- return this;
- }
-
- public Builder retainMetrics(Set<MetricId> idsToRetain) {
- metrics.keySet().retainAll(idsToRetain);
- return this;
- }
-
- public Builder applyOutputNames(Map<MetricId, List<String>> outputNamesById) {
- Map<MetricId, Number> newMetrics = new LinkedHashMap<>();
- outputNamesById.forEach((id, outputNames) -> {
- if (metrics.containsKey(id))
- outputNames.forEach(outputName -> newMetrics.put(toMetricId(outputName), metrics.get(id)));
- });
- metrics = newMetrics;
- return this;
- }
-
- public Builder putDimension(DimensionId id, String value) {
- dimensions.put(id, value);
- return this;
- }
-
- public Builder putDimensions(Map<DimensionId, String> extraDimensions) {
- if (extraDimensions != null) dimensions.putAll(extraDimensions);
- return this;
- }
-
- public Builder putDimensionsIfAbsent(Map<DimensionId, String> extraDimensions) {
- if (extraDimensions != null) extraDimensions.forEach(dimensions::putIfAbsent);
- return this;
- }
-
- public Builder addConsumers(Set<ConsumerId> extraConsumers) {
- if (extraConsumers != null) consumers.addAll(extraConsumers);
- return this;
- }
-
- public MetricsPacket build() {
- return new MetricsPacket(statusCode, statusMessage, timestamp, service, metrics, dimensions, consumers);
- }
-
- public boolean hasMetrics() {
- return ! metrics.isEmpty();
- }
-
- public Instant getTimestamp() { return Instant.ofEpochSecond(timestamp); }
-
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ServiceId.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ServiceId.java
deleted file mode 100644
index b61ead75b72..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/ServiceId.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import java.util.Objects;
-
-/**
- * @author gjoranv
- */
-public class ServiceId {
-
- public final String id;
- private ServiceId(String id) { this.id = id; }
-
- public static ServiceId toServiceId(String id) { return new ServiceId(id); }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ServiceId serviceId = (ServiceId) o;
- return Objects.equals(id, serviceId.id);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id);
- }
-
- @Override
- public String toString() {
- return "ServiceId{" +
- "id='" + id + '\'' +
- '}';
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonUtil.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonUtil.java
deleted file mode 100644
index f48e5759528..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonUtil.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model.json;
-
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableMap;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static com.yahoo.stream.CustomCollectors.toLinkedMap;
-import static java.util.Collections.emptyList;
-import static java.util.logging.Level.WARNING;
-
-/**
- * @author gjoranv
- */
-public class JsonUtil {
- private static final Logger log = Logger.getLogger(JsonUtil.class.getName());
-
- static final String YAMAS_ROUTING = "yamas";
-
- public static MetricsPacket.Builder toMetricsPacketBuilder(YamasJsonModel jsonModel) {
- if (jsonModel.application == null)
- throw new IllegalArgumentException("Service id cannot be null");
-
- return new MetricsPacket.Builder(toServiceId(jsonModel.application))
- .statusCode(jsonModel.status_code)
- .statusMessage(jsonModel.status_msg)
- .timestamp(jsonModel.timestamp)
- .putMetrics(jsonModel.getMetricsList())
- .putDimensions(jsonModel.getDimensionsById())
- .addConsumers(jsonModel.getYamasConsumers());
- }
-
- public static YamasArrayJsonModel toYamasArray(Collection<MetricsPacket> metricsPackets) {
- YamasArrayJsonModel yamasArray = toYamasArray(metricsPackets, false);
-
- // Add a single status object at the end
- yamasArray.metrics.stream().findFirst().map(YamasJsonModel::getYamasConsumers)
- .ifPresent(consumers -> yamasArray.add(getStatusYamasModel("Data collected successfully", 0, consumers)));
- return yamasArray;
- }
-
- public static YamasArrayJsonModel toYamasArray(Collection<MetricsPacket> metricsPackets, boolean addStatus) {
- YamasArrayJsonModel yamasArray = new YamasArrayJsonModel();
- metricsPackets.forEach(packet -> yamasArray.add(toYamasModel(packet, addStatus)));
- return yamasArray;
- }
-
- /**
- * Converts the given json formatted string to a list of metrics packet builders.
- * Note that this method returns an empty list if an IOException occurs,
- * and logs a warning as a side effect.
- */
- public static List<MetricsPacket.Builder> toMetricsPackets(String jsonString) {
- List<MetricsPacket.Builder> packets = new ArrayList<>();
- try {
- JsonParser jp = new JsonFactory().createParser(jsonString);
- jp.setCodec(new ObjectMapper());
- while (jp.nextToken() != null) {
- YamasJsonModel jsonModel = jp.readValueAs(YamasJsonModel.class);
- packets.add(toMetricsPacketBuilder(jsonModel));
- }
- return packets;
- } catch (IOException e) {
- log.log(WARNING, "Could not create metrics packet from string:\n" + jsonString, e);
- return emptyList();
- }
- }
-
- private static YamasJsonModel getStatusYamasModel(String statusMessage, int statusCode, Collection<ConsumerId> consumers) {
- YamasJsonModel model = new YamasJsonModel();
- model.status_code = statusCode;
- model.status_msg = statusMessage;
- model.application = "yms_check_vespa";
- model.routing = ImmutableMap.of(YAMAS_ROUTING, toYamasJsonNamespaces(consumers));
- return model;
- }
-
- private static YamasJsonModel toYamasModel(MetricsPacket packet, boolean addStatus) {
- YamasJsonModel model = new YamasJsonModel();
-
- if (addStatus) {
- model.status_code = packet.statusCode;
- model.status_msg = packet.statusMessage;
- }
-
- model.application = packet.service.id;
- model.timestamp = (packet.timestamp == 0L) ? null : packet.timestamp;
-
- if (packet.metrics().isEmpty()) model.metrics = null;
- else {
- model.metrics = packet.metrics().entrySet().stream().collect(
- toLinkedMap(id2metric -> id2metric.getKey().id,
- id2metric -> id2metric.getValue().doubleValue()));
- }
-
- if (packet.dimensions().isEmpty()) model.dimensions = null;
- else {
- model.dimensions = packet.dimensions().entrySet().stream().collect(
- toLinkedMap(id2dim -> id2dim.getKey().id,
- Map.Entry::getValue));
- }
-
- if (packet.consumers().isEmpty()) model.routing = null;
- else model.routing = ImmutableMap.of(YAMAS_ROUTING, toYamasJsonNamespaces(packet.consumers()));
-
- return model;
- }
-
- private static YamasJsonModel.YamasJsonNamespace toYamasJsonNamespaces(Collection<ConsumerId> consumers) {
- YamasJsonModel.YamasJsonNamespace namespaces = new YamasJsonModel.YamasJsonNamespace();
- namespaces.namespaces = consumers.stream().map(consumer -> consumer.id).collect(Collectors.toList());
- return namespaces;
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasArrayJsonModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasArrayJsonModel.java
deleted file mode 100644
index fdac0521256..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasArrayJsonModel.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model.json;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.Version;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Datamodel for the metricsproxy representation of multiple yamas checks.
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonInclude(JsonInclude.Include.NON_NULL)
-public class YamasArrayJsonModel {
- @JsonProperty("metrics")
- public final List<YamasJsonModel> metrics = new ArrayList<>();
-
- public void add(List<YamasJsonModel> results) {
- metrics.addAll(results);
- }
-
- public void add(YamasJsonModel result) {
- metrics.add(result);
- }
-
- public void add(YamasArrayJsonModel array) {
- metrics.addAll(array.metrics);
- }
-
- /**
- * Convenience method to serialize.
- * <p>
- * Custom floating point serializer to avoid scientifc notation
- *
- * @return Serialized json
- */
- public String serialize() {
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule("DoubleSerializer",
- new Version(1, 0, 0, "", null, null));
- module.addSerializer(Double.class, new DoubleSerializer());
- mapper.registerModule(module);
-
- if (metrics.size() > 0) {
- try {
- return mapper.writeValueAsString(this);
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- }
-
- return "{}"; // Backwards compatability
- }
-
- public class DoubleSerializer extends JsonSerializer<Double> {
- @Override
- public void serialize(Double value, JsonGenerator jgen,
- SerializerProvider provider) throws IOException, JsonProcessingException {
- DecimalFormat df = new DecimalFormat("#.####", new DecimalFormatSymbols(Locale.ENGLISH));
- df.setMaximumFractionDigits(13);
- jgen.writeNumber(df.format(value));
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModel.java
deleted file mode 100644
index 5fdbe9577be..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModel.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model.json;
-
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static com.yahoo.stream.CustomCollectors.toLinkedMap;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.emptySet;
-
-/**
- * Datamodel for Yamas execute output
- * <p>
- * Used to read from original yamas checks and annotate with routing information.
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonInclude(JsonInclude.Include.NON_NULL)
-@JsonPropertyOrder({ "status_code", "timestamp", "application", "metrics", "dimensions", "routing", "status_msg"})
-public class YamasJsonModel {
- @JsonProperty("status_code")
- public Integer status_code;
- @JsonProperty("status_msg")
- public String status_msg;
- @JsonProperty("timestamp")
- public Long timestamp;
- @JsonProperty("application")
- public String application;
- @JsonProperty("metrics")
- public Map<String, Double> metrics;
- @JsonProperty("dimensions")
- public Map<String, String> dimensions;
- @JsonProperty("routing")
- public Map<String, YamasJsonNamespace> routing;
-
- public static class YamasJsonNamespace {
- @JsonProperty("namespaces")
- public List<String> namespaces;
- }
-
- // NOTE: do not rename to 'setMetrics', as jackson will try to use it.
- public void resetMetrics(List<Metric> newMetrics) {
- metrics = new LinkedHashMap<>();
- newMetrics.forEach(metric -> metrics.put(metric.getName(), metric.getValue().doubleValue()));
- }
-
- /**
- * Convenience method to add targets to the routing object
- *
- * @param names Namespaces E.g "Vespa"
- */
- public void addRouting(Set<ConsumerId> names) {
- // Setup routing structure if not already existing
- if (routing == null) {
- routing = new HashMap<>();
- }
-
- if (! routing.containsKey("yamas")) {
- routing.put("yamas", new YamasJsonModel.YamasJsonNamespace());
- }
- YamasJsonModel.YamasJsonNamespace namespace = routing.get("yamas");
-
- if (namespace.namespaces == null) {
- namespace.namespaces = new ArrayList<>();
- }
-
- namespace.namespaces.addAll(names.stream().map(consumer -> consumer.id).collect(Collectors.toList()));
- }
-
- /**
- * Convenience method to add dimensions
- */
- public void addDimensions(Map<DimensionId, String> additionalDimensions, boolean replace) {
- additionalDimensions.forEach((k,v) -> {
- addDimension(k.id, v, replace);
- });
- }
-
- /**
- * Convenience method to add dimensions
- */
- public void addDimension(String key, String value, boolean replace) {
- if (dimensions == null) {
- dimensions = new HashMap<>();
- }
- if (!dimensions.containsKey(key) || replace) {
- dimensions.put(key, value);
- }
- }
-
- List<Metric> getMetricsList() {
- if (metrics == null) return emptyList();
-
- return metrics.keySet().stream()
- .map(name -> new Metric(name, metrics.get(name)))
- .collect(Collectors.toList());
- }
-
- Map<DimensionId, String> getDimensionsById() {
- if (dimensions == null) return emptyMap();
-
- return dimensions.keySet().stream().collect(toLinkedMap(DimensionId::toDimensionId,
- name -> dimensions.get(name)));
- }
-
- Set<ConsumerId> getYamasConsumers() {
- if (routing == null || routing.get("yamas") == null) return emptySet();
-
- return routing.get("yamas").namespaces.stream()
- .map(ConsumerId::toConsumerId)
- .collect(Collectors.toCollection(LinkedHashSet::new));
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/package-info.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/package-info.java
deleted file mode 100644
index c72f2484f8c..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-@ExportPackage
-package ai.vespa.metricsproxy.metric;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcConnector.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcConnector.java
deleted file mode 100644
index e7feab9926d..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcConnector.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.rpc;
-
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.jrt.Acceptor;
-import com.yahoo.jrt.ListenFailedException;
-import com.yahoo.jrt.Method;
-import com.yahoo.jrt.Spec;
-import com.yahoo.jrt.Supervisor;
-import com.yahoo.jrt.Transport;
-
-import java.util.logging.Logger;
-
-import static com.yahoo.log.LogLevel.DEBUG;
-import static java.util.logging.Level.INFO;
-
-/**
- * Contains the connector for the rpc server, to prevent it from going down after component reconfiguration.
- * This will only be recreated if the rpc port changes, which should never happen under normal circumstances.
- *
- * @author gjoranv
- */
-public class RpcConnector extends AbstractComponent {
- private static final Logger log = Logger.getLogger(RpcConnector.class.getName());
-
- private final Supervisor supervisor = new Supervisor(new Transport());
- private final Acceptor acceptor;
-
- public RpcConnector(RpcConnectorConfig config) {
- Spec spec = new Spec(config.port());
- try {
- acceptor = supervisor.listen(spec);
- log.log(DEBUG, "Listening on " + spec.host() + ":" + spec.port());
- } catch (ListenFailedException e) {
- stop();
- log.log(INFO, "Failed listening at " + spec.host() + ":" + spec.port());
- throw new RuntimeException("Could not listen at " + spec, e);
- }
- }
-
- /**
- * Adds a method. If a method with the same name already exists, it will be replaced.
- * @param method The method to add.
- */
- public void addMethod(Method method) {
- supervisor.addMethod(method);
- }
-
- public void stop() {
- acceptor.shutdown().join();
- supervisor.transport().shutdown().join();
- }
-
- @Override
- public void deconstruct() {
- stop();
- super.deconstruct();
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcServer.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcServer.java
deleted file mode 100644
index e0e0e7a3f87..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/rpc/RpcServer.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.rpc;
-
-import ai.vespa.metricsproxy.core.MetricsManager;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import ai.vespa.metricsproxy.service.VespaService;
-import ai.vespa.metricsproxy.service.VespaServices;
-import com.yahoo.jrt.ErrorCode;
-import com.yahoo.jrt.Method;
-import com.yahoo.jrt.Request;
-import com.yahoo.jrt.Spec;
-import com.yahoo.jrt.StringValue;
-import com.yahoo.jrt.Supervisor;
-import com.yahoo.jrt.Transport;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.json.JsonUtil.toMetricsPackets;
-import static ai.vespa.metricsproxy.metric.model.json.JsonUtil.toYamasArray;
-import static com.yahoo.collections.CollectionUtil.mkString;
-import static com.yahoo.log.LogLevel.DEBUG;
-import static java.util.logging.Level.INFO;
-import static java.util.logging.Level.WARNING;
-
-/**
- * Rpc server for the metrics proxy.
- *
- * When a new object is created after reconfiguration, it will claim ownership of the methods
- * in the given {@link RpcConnector}. This is ok because at the time this component is created,
- * all components it depends on are already created.
- *
- * @author gjoranv
- */
-public class RpcServer {
-
- private static final Logger log = Logger.getLogger(RpcServer.class.getName());
-
- private static int LOG_SPENT_TIME_LIMIT = 10 * 1000; // ms. same as default client RPC timeout used in rpc_invoke
-
- private final VespaServices vespaServices;
- private final MetricsManager metricsManager;
-
- public RpcServer(RpcConnector connector, VespaServices vespaServices, MetricsManager metricsManager) {
- this.vespaServices = vespaServices;
- this.metricsManager = metricsManager;
- addMethods(connector);
- log.log(DEBUG, "RPC server created");
- }
-
- private void addMethods(RpcConnector connector) {
- // Add/replace this method first to increase likelihood of getting extra metrics and global dimensions
- connector.addMethod(
- new Method("setExtraMetrics", "s", "", this::setExtraMetrics)
- .methodDesc("Set extra metrics that will be added to output from getMetricsForYamas.")
- .paramDesc(0, "metricsJson", "The metrics in json format"));
-
- connector.addMethod(
- new Method("getMetricsById", "s", "s", this::getMetricsById)
- .methodDesc("Get Vespa metrics for the service with the given Id")
- .paramDesc(0, "id", "The id of the service")
- .returnDesc(0, "ret", "Vespa metrics"));
-
- connector.addMethod(
- new Method("getServices", "", "s", this::getServices)
- .methodDesc("Get Vespa services monitored by this metrics proxy")
- .returnDesc(0, "ret", "Vespa metrics"));
-
- connector.addMethod(
- new Method("getMetricsForYamas", "s", "s", this::getMetricsForYamas)
- .methodDesc("Get JSON formatted Vespa metrics for a given service name or 'all'")
- .paramDesc(0, "service", "The vespa service name, or 'all'")
- .returnDesc(0, "ret", "Vespa metrics"));
-
- connector.addMethod(
- new Method("getHealthMetricsForYamas", "s", "s", this::getHealthMetricsForYamas)
- .methodDesc("Get JSON formatted Health check for a given service name or 'all'")
- .paramDesc(0, "service", "The vespa service name")
- .returnDesc(0, "ret", "Vespa metrics"));
-
- connector.addMethod(
- new Method("getAllMetricNamesForService", "ss", "s", this::getAllMetricNamesForService)
- .methodDesc("Get metric names known for service ")
- .paramDesc(0, "service", "The vespa service name'")
- .paramDesc(1, "consumer", "The consumer'")
- .returnDesc(0, "ret", "Metric names, one metric name per line"));
- }
-
- void getAllMetricNamesForService(Request req) {
- String service = req.parameters().get(0).asString();
- ConsumerId consumer = toConsumerId(req.parameters().get(1).asString());
- withExceptionHandling(req, () -> {
- String metricNames = metricsManager.getMetricNamesForServiceAndConsumer(service, consumer);
- req.returnValues().add(new StringValue(metricNames));
- });
- }
-
- void getMetricsById(Request req) {
- String id = req.parameters().get(0).asString();
- withExceptionHandling(req, () -> {
- String metricsString = metricsManager.getMetricsByConfigId(id);
- req.returnValues().add(new StringValue(metricsString));
- });
- }
-
-
- void getServices(Request req) {
- withExceptionHandling(req, () -> {
- String servicesString = metricsManager.getAllVespaServices();
- req.returnValues().add(new StringValue(servicesString));
- });
- }
-
- void getMetricsForYamas(Request req) {
- Instant startTime = Instant.now();
- req.detach();
- String service = req.parameters().get(0).asString();
- log.log(DEBUG, () -> "getMetricsForYamas called at " + startTime + " with argument: " + service);
- List<VespaService> services = vespaServices.getMonitoringServices(service);
- log.log(DEBUG, () -> "Getting metrics for services: " + mkString(services, "[", ", ", "]"));
- if (services.isEmpty()) setNoServiceError(req, service);
- else withExceptionHandling(req, () -> {
- List<MetricsPacket> packets = metricsManager.getMetrics(services, startTime);
- log.log(DEBUG,() -> "Returning metrics packets:\n" + mkString(packets, "\n"));
- req.returnValues().add(new StringValue(toYamasArray(packets).serialize()));
- });
- req.returnRequest();
- }
-
- void getHealthMetricsForYamas(Request req) {
- req.detach();
- String service = req.parameters().get(0).asString();
- List<VespaService> services = vespaServices.getMonitoringServices(service);
- if (services.isEmpty()) setNoServiceError(req, service);
- else withExceptionHandling(req, () -> {
- List<MetricsPacket> packets = metricsManager.getHealthMetrics(services);
- req.returnValues().add(new StringValue(toYamasArray(packets, true).serialize()));
- });
- req.returnRequest();
- }
-
- void setExtraMetrics(Request req) {
- String metricsJson = req.parameters().get(0).asString();
- log.log(DEBUG, "setExtraMetrics called with argument: " + metricsJson);
- withExceptionHandling(req, () -> metricsManager.setExtraMetrics(toMetricsPackets(metricsJson)));
- }
-
- private static void withExceptionHandling(Request req, ThrowingRunnable runnable) {
- try {
- TimeTracker timeTracker = new TimeTracker(req);
- runnable.run();
- timeTracker.logSpentTime();
- } catch (Exception e) {
- log.log(WARNING, "Got exception when running RPC command " + req.methodName(), e);
- setMethodFailedError(req, e);
- } catch (Error e) {
- log.log(WARNING, "Got error when running RPC command " + req.methodName(), e);
- setMethodFailedError(req, e);
- } catch (Throwable t) {
- log.log(WARNING, "Got throwable (non-error, non-exception) when running RPC command " + req.methodName(), t);
- setMethodFailedError(req, t);
- }
- }
-
- private static void setMethodFailedError(Request req, Throwable t) {
- String msg = "Request failed due to internal error: " + t.getClass().getName() + ": " + t.getMessage();
- req.setError(ErrorCode.METHOD_FAILED, msg);
- req.returnValues().add(new StringValue(""));
- }
-
- private static void setNoServiceError(Request req, String serviceName) {
- String msg = "No service with name '" + serviceName + "'";
- req.setError(ErrorCode.BAD_REQUEST, msg);
- req.returnValues().add(new StringValue(""));
- }
-
-
- private static class TimeTracker {
- private final long startTime = System.currentTimeMillis();
- private final Request request;
-
- private TimeTracker(Request request) {
- this.request = request;
- }
-
- long spentTime() {
- return System.currentTimeMillis() - startTime;
- }
-
- private void logSpentTime() {
- Level logLevel = DEBUG;
- if (spentTime() > LOG_SPENT_TIME_LIMIT) {
- logLevel = INFO;
- }
- if (log.isLoggable(logLevel)) {
- log.log(logLevel, "RPC request '" + request.methodName() + "' with parameters '" +
- request.parameters() + "' took " + spentTime() + " ms");
- }
- }
- }
-
- private interface ThrowingRunnable {
- void run() throws Exception;
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ConfigSentinelClient.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ConfigSentinelClient.java
deleted file mode 100644
index 875b6190763..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ConfigSentinelClient.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import com.google.inject.Inject;
-import com.yahoo.log.LogLevel;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Logger;
-
-/**
- * Connects to the config sentinel and gets information like pid for the services on the node
- */
-public class ConfigSentinelClient {
- private final static Logger log = Logger.getLogger(ConfigSentinelClient.class.getName());
-
- private final CmdClient client;
-
- @Inject
- public ConfigSentinelClient() {
- this.client = new CmdClient();
- }
-
- /**
- * Update all services reading from config sentinel
- *
- * @param services The list of services
- */
- synchronized void updateServiceStatuses(List<VespaService> services) {
- try {
- setStatus(services);
- } catch (Exception e) {
- log.log(LogLevel.ERROR, "Unable to update service pids from sentinel", e);
- }
- }
-
-
- /**
- * Update status
- *
- * @param s The service to update the status for
- */
- public synchronized void ping(VespaService s) {
- List<VespaService> services = new ArrayList<>();
- services.add(s);
- log.log(LogLevel.DEBUG, "Ping for service " + s);
- try {
- setStatus(services);
- } catch (Exception e) {
- log.log(LogLevel.ERROR, "Unable to update service pids from sentinel", e);
- }
- }
-
- /**
- * Update the status (pid check etc)
- *
- * @param services list of services
- * @throws Exception if something went wrong
- */
- protected synchronized void setStatus(List<VespaService> services) throws Exception {
- InputStream in;
- PrintStream out;
- client.connect();
-
- in = client.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- String line;
- List<VespaService> updatedServices = new ArrayList<>();
- while ((line = reader.readLine()) != null) {
- if (line.equals("")) {
- break;
- }
-
- VespaService s = parseServiceString(line, services);
- if (s != null) {
- updatedServices.add(s);
- }
- }
-
- //Check if there are services that were not found in output
- //from the sentinel
- for (VespaService s : services) {
- if ((!s.getServiceName().equals("configserver")) && !updatedServices.contains(s)) {
- log.log(LogLevel.DEBUG,"Service " + s + " is no longer found with sentinel - setting alive = false");
- s.setAlive(false);
- }
- }
-
- //Close streams
- reader.close();
- client.disconnect();
- }
-
- protected static VespaService parseServiceString(String line, List<VespaService> services) {
- String[] parts = line.split(" ");
- if (parts.length < 3)
- return null;
-
- String name = parts[0];
- int pid = -1;
- String state = null;
- VespaService service = null;
-
- for (VespaService s : services) {
- if (s.getInstanceName().compareToIgnoreCase(name) == 0) {
- service = s;
- break;
- }
- }
-
- //Could not find this service
- //nothing wrong with that as the check is invoked per line from sentinel
- if (service == null) {
- return service;
- }
-
- for (int i = 1; i < parts.length; i++) {
- String keyValue[] = parts[i].split("=");
-
- String key = keyValue[0];
- String value = keyValue[1];
-
- if (key.equals("state")) {
- state = value;
- } else if (key.equals("pid")) {
- pid = Integer.parseInt(value);
- }
- }
-
- if (state != null) {
- service.setState(state);
- if (pid >= 0 && "RUNNING".equals(state)) {
- service.setAlive(true);
- service.setPid(pid);
- } else {
- service.setAlive(false);
-
- }
- } else {
- service.setAlive(false);
- }
- return service;
- }
-
- static class CmdClient {
- Process proc;
- // NOTE: hostname/port not used yet
- void connect() {
- String[] args = new String[]{"vespa-sentinel-cmd", "list"};
- try {
- proc = Runtime.getRuntime().exec(args);
- } catch (Exception e) {
- log.log(LogLevel.WARNING, "could not run vespa-sentinel-cmd: "+e);
- proc = null;
- }
- }
- void disconnect() {
- if (proc.isAlive()) {
- proc.destroy();
- }
- proc = null;
- }
- InputStream getInputStream() {
- return (proc != null)
- ? proc.getInputStream()
- : new java.io.ByteArrayInputStream(new byte[0]);
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/CpuJiffies.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/CpuJiffies.java
deleted file mode 100644
index 312e7d5c0c1..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/CpuJiffies.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-class CpuJiffies {
- private int cpuId;
- private long jiffies;
-
- CpuJiffies(String line) {
- parseLine(line);
- }
-
- private void parseLine(String line) {
- String elems[];
- String cpuId;
- long jiffies;
-
- elems = line.split("\\s+");
- cpuId = elems[0].substring(3);
- if (cpuId.length() == 0) {
- this.cpuId = -1;
- } else {
- this.cpuId = Integer.parseInt(cpuId);
- }
-
- jiffies = 0;
- for (int i = 1; i < elems.length; i++) {
- jiffies += Long.parseLong(elems[i].replaceAll("[\\n\\r]+", ""));
- }
-
- this.jiffies = jiffies;
- }
-
- public int getCpuId() {
- return cpuId;
- }
-
- public long getTotalJiffies() {
- return jiffies;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyHealthMetricFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyHealthMetricFetcher.java
deleted file mode 100644
index f87171a42dc..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyHealthMetricFetcher.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.HealthMetric;
-
-/**
- * Dummy class used for getting health status for a vespa service that has no HTTP service
- * for getting health status
- *
- * @author hmusum
- */
-public class DummyHealthMetricFetcher extends RemoteHealthMetricFetcher {
-
- /**
- * @param service The service to fetch metrics from
- */
- DummyHealthMetricFetcher(VespaService service) {
- super(service, 0);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- public HealthMetric getHealth(int fetchCount) {
- if (service.isAlive()) {
- return HealthMetric.getOk("Service is running - pid check only");
- } else {
- return HealthMetric.getFailed("Service is not running - pid check only");
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyMetricsFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyMetricsFetcher.java
deleted file mode 100644
index f21d125e279..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/DummyMetricsFetcher.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.Metrics;
-
-/**
- * Dummy class used for getting health status for a vespa service that has no HTTP service
- * for getting metrics
- *
- * @author hmusum
- */
-public class DummyMetricsFetcher extends RemoteMetricsFetcher {
-
- /**
- * @param service The service to fetch metrics from
- */
- DummyMetricsFetcher(VespaService service) {
- super(service, 0);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- public Metrics getMetrics(int fetchCount) {
- return new Metrics();
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/HttpMetricFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/HttpMetricFetcher.java
deleted file mode 100644
index 9094ef22c20..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/HttpMetricFetcher.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.util.http.VespaHttpClientBuilder;
-import com.yahoo.log.LogLevel;
-import com.yahoo.yolean.Exceptions;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.CloseableHttpClient;
-
-import java.io.IOException;
-import java.net.URI;
-import java.util.logging.Logger;
-
-/**
- * HTTP client to get metrics or health data from a service
- *
- * @author hmusum
- * @author bjorncs
- */
-public abstract class HttpMetricFetcher {
- private final static Logger log = Logger.getLogger(HttpMetricFetcher.class.getPackage().getName());
- public final static String STATE_PATH = "/state/v1/";
- final static String METRICS_PATH = STATE_PATH + "metrics";
- final static String HEALTH_PATH = STATE_PATH + "health";
- // The call to apache will do 3 retries. As long as we check the services in series, we can't have this too high.
- public static int CONNECTION_TIMEOUT = 5000;
- private final static int SOCKET_TIMEOUT = 60000;
- private final URI url;
- protected final VespaService service;
- private static final CloseableHttpClient httpClient = createHttpClient();
-
-
- /**
- * @param service The service to fetch metrics from
- * @param port The port to use
- */
- HttpMetricFetcher(VespaService service, int port, String path) {
- this.service = service;
-
- String u = "http://localhost:" + port + path;
- this.url = URI.create(u);
- log.log(LogLevel.DEBUG, "Fetching metrics from " + u + " with timeout " + CONNECTION_TIMEOUT);
- }
-
- String getJson() throws IOException {
- log.log(LogLevel.DEBUG, "Connecting to url " + url + " for service '" + service + "'");
- return httpClient.execute(new HttpGet(url), new BasicResponseHandler());
- }
-
- public String toString() {
- return this.getClass().getSimpleName() + " using " + url;
- }
-
- String errMsgNoResponse(IOException e) {
- return "Unable to get response from service '" + service + "': " +
- Exceptions.toMessageString(e);
- }
-
- void handleException(Exception e, String data, int timesFetched) {
- logMessage("Unable to parse json '" + data + "' for service '" + service + "': " +
- Exceptions.toMessageString(e), timesFetched);
- }
-
- private void logMessage(String message, int timesFetched) {
- if (service.isAlive() && timesFetched > 5) {
- log.log(LogLevel.INFO, message);
- } else {
- log.log(LogLevel.DEBUG, message);
- }
- }
-
- void logMessageNoResponse(String message, int timesFetched) {
- if (timesFetched > 5) {
- log.log(LogLevel.WARNING, message);
- } else {
- log.log(LogLevel.INFO, message);
- }
- }
-
- private static CloseableHttpClient createHttpClient() {
- return VespaHttpClientBuilder.create()
- .setUserAgent("metrics-proxy-http-client")
- .setDefaultRequestConfig(RequestConfig.custom()
- .setConnectTimeout(CONNECTION_TIMEOUT)
- .setSocketTimeout(SOCKET_TIMEOUT)
- .build())
- .build();
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java
deleted file mode 100644
index 503f582a827..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.HealthMetric;
-import com.yahoo.log.LogLevel;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.util.logging.Logger;
-
-/**
- * Fetch health status for a given vespa service
- *
- * @author Jo Kristian Bergum
- */
-public class RemoteHealthMetricFetcher extends HttpMetricFetcher {
-
- private final static Logger log = Logger.getLogger(RemoteHealthMetricFetcher.class.getPackage().getName());
-
- /**
- * @param service The service to fetch metrics from
- * @param port The port to use
- */
- public RemoteHealthMetricFetcher(VespaService service, int port) {
- super(service, port, HEALTH_PATH);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- public HealthMetric getHealth(int fetchCount) {
- String data = "{}";
- try {
- data = getJson();
- } catch (IOException e) {
- logMessageNoResponse(errMsgNoResponse(e), fetchCount);
- }
- return createHealthMetrics(data, fetchCount);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- HealthMetric createHealthMetrics(String data, int fetchCount) {
- HealthMetric healthMetric = HealthMetric.getFailed("Failed fetching status page for service");
- try {
- healthMetric = parse(data);
- } catch (Exception e) {
- handleException(e, data, fetchCount);
- }
- return healthMetric;
- }
-
- private HealthMetric parse(String data) {
- if (data == null || data.isEmpty()) {
- return HealthMetric.getFailed("Empty response from status page");
- }
- try {
- JSONObject o = new JSONObject(data);
- JSONObject status = o.getJSONObject("status");
- String code = status.getString("code");
- String message = "";
- if (status.has("message")) {
- message = status.getString("message");
- }
- return HealthMetric.get(code, message);
-
- } catch (JSONException e) {
- log.log(LogLevel.DEBUG, "Failed to parse json response from metrics page:" + e + ":" + data);
- return HealthMetric.getFailed("Not able to parse json from status page");
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
deleted file mode 100644
index a606ec7d8cd..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-
-/**
- * Fetch metrics for a given vespa service
- *
- * @author Jo Kristian Bergum
- */
-public class RemoteMetricsFetcher extends HttpMetricFetcher {
- /**
- * @param service The service to fetch metrics from
- * @param port The port to use
- */
- RemoteMetricsFetcher(VespaService service, int port) {
- super(service, port, METRICS_PATH);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- public Metrics getMetrics(int fetchCount) {
- String data = "{}";
- try {
- data = getJson();
- } catch (IOException e) {
- logMessageNoResponse(errMsgNoResponse(e), fetchCount);
- }
-
- return createMetrics(data, fetchCount);
- }
-
- /**
- * Connect to remote service over http and fetch metrics
- */
- public Metrics createMetrics(String data, int fetchCount) {
- Metrics remoteMetrics = new Metrics();
- try {
- remoteMetrics = parse(data);
- } catch (Exception e) {
- handleException(e, data, fetchCount);
- }
-
- return remoteMetrics;
- }
-
- Metrics parse(String data) throws JSONException {
- JSONObject o = new JSONObject(data);
- if (!(o.has("metrics"))) {
- return new Metrics(); //empty
- }
-
- JSONObject metrics = o.getJSONObject("metrics");
- JSONArray values;
- long timestamp;
-
- try {
- JSONObject snapshot = metrics.getJSONObject("snapshot");
- timestamp = (long) snapshot.getDouble("to");
- values = metrics.getJSONArray("values");
- } catch (JSONException e) {
- // snapshot might not have been produced. Do not throw exception into log
- return new Metrics();
- }
- long now = System.currentTimeMillis() / 1000;
- timestamp = Metric.adjustTime(timestamp, now);
- Metrics m = new Metrics(timestamp);
-
- Map<DimensionId, String> noDims = Collections.emptyMap();
- Map<String, Map<DimensionId, String>> uniqueDimensions = new HashMap<>();
- for (int i = 0; i < values.length(); i++) {
- JSONObject metric = values.getJSONObject(i);
- String name = metric.getString("name");
- String description = "";
-
- if (metric.has("description")) {
- description = metric.getString("description");
- }
-
- Map<DimensionId, String> dim = noDims;
- if (metric.has("dimensions")) {
- JSONObject dimensions = metric.getJSONObject("dimensions");
- StringBuilder sb = new StringBuilder();
- for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) {
- String k = (String) it.next();
- String v = dimensions.getString(k);
- sb.append(toDimensionId(k)).append(v);
- }
- if ( ! uniqueDimensions.containsKey(sb.toString())) {
- dim = new HashMap<>();
- for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) {
- String k = (String) it.next();
- String v = dimensions.getString(k);
- dim.put(toDimensionId(k), v);
- }
- uniqueDimensions.put(sb.toString(), Collections.unmodifiableMap(dim));
- }
- dim = uniqueDimensions.get(sb.toString());
- }
-
- JSONObject aggregates = metric.getJSONObject("values");
- for (Iterator<?> it = aggregates.keys(); it.hasNext(); ) {
- String aggregator = (String) it.next();
- Number value = (Number) aggregates.get(aggregator);
- StringBuilder metricName = (new StringBuilder()).append(name).append(".").append(aggregator);
- m.add(new Metric(metricName.toString(), value, timestamp, dim, description));
- }
- }
-
- return m;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ServiceListener.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ServiceListener.java
deleted file mode 100644
index 810eb5fb908..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/ServiceListener.java
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
-* Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import java.util.List;
-
-/**
- * @author Unknown
- */
-public interface ServiceListener {
- void setServices(List<VespaService> services);
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPoller.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPoller.java
deleted file mode 100644
index 9f6614668a5..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPoller.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import com.yahoo.log.LogLevel;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
-
-/**
- * Class to get data from the system and update the services at given intervals.
- * TODO: rewrite to use ScheduledExecutorService or just call poll() directly.
- *
- * @author Eirik Nygaard
- */
-public class SystemPoller implements ServiceListener {
- final private static Logger log = Logger.getLogger(SystemPoller.class.getPackage().getName());
-
- private final int pollingIntervalSecs;
- private volatile List<VespaService> services;
-
- private final int memoryTypeVirtual = 0;
- private final int memoryTypeResident = 1;
- private final Map<VespaService, Long> lastCpuJiffiesMetrics = new ConcurrentHashMap<>();
- private final Timer systemPollTimer;
-
- private long lastTotalCpuJiffies = -1;
-
- public SystemPoller(List<VespaService> services, int pollingIntervalSecs) {
- this.services = services;
- this.pollingIntervalSecs = pollingIntervalSecs;
- systemPollTimer = new Timer("systemPollTimer", true);
- }
-
- @Override
- public void setServices(List<VespaService> services) {
- log.log(LogLevel.DEBUG, "Setting services in SystemPoller to: " + services);
- this.services = services;
- }
-
- void stop() {
- systemPollTimer.cancel();
- }
-
- /**
- * Return memory usage for a given process, both resident and virtual is
- * returned.
- *
- * @param service The instance to get memory usage for
- * @return array[0] = memoryResident, array[1] = memoryVirtual (kB units)
- */
- long[] getMemoryUsage(VespaService service) {
- long size[] = new long[2];
- BufferedReader br;
- int pid = service.getPid();
-
- size[0] = 0;
- size[1] = 0;
- try {
- br = new BufferedReader(new FileReader("/proc/" + pid + "/smaps"));
- } catch (FileNotFoundException ex) {
- markDead(service);
- return size;
- }
- String line;
- try {
- while ((line = br.readLine()) != null) {
- String[] elems = line.split("\\s+");
- /* Memory size is given in kB - convert to bytes by multiply with 1024*/
- if (line.startsWith("Rss:")) {
- size[memoryTypeResident] += Long.parseLong(elems[1]) * 1024;
- } else if (line.startsWith("Size:")) {
- size[memoryTypeVirtual] += Long.parseLong(elems[1]) * 1024;
- }
- }
-
- br.close();
- } catch (IOException ex) {
- log.log(LogLevel.DEBUG, "Unable to read line from smaps file", ex);
- return size;
- }
-
- return size;
- }
-
- /**
- * Mark a service as dead.
- *
- * @param service The service to mark as dead.
- */
- private static void markDead(VespaService service) {
- service.setAlive(false);
- }
-
- /**
- * Poll services for system metrics
- */
- void poll() {
- long startTime = System.currentTimeMillis();
- boolean someAlive = false;
-
- /* Don't do any work if there are no known services */
- if (services.isEmpty()) {
- schedule();
- return;
- }
-
- log.log(LogLevel.DEBUG, "Monitoring system metrics for " + services.size() + " services");
-
- long sysJiffies = getNormalizedSystemJiffies();
- for (VespaService s : services) {
-
-
- if(s.isAlive()) {
- someAlive = true;
- }
-
- Metrics metrics = new Metrics();
- log.log(LogLevel.DEBUG, "Current size of system metrics for service " + s + " is " + metrics.size());
-
- long[] size = getMemoryUsage(s);
- log.log(LogLevel.DEBUG, "Updating memory metric for service " + s);
-
- metrics.add(new Metric("memory_virt", size[memoryTypeVirtual], startTime / 1000));
- metrics.add(new Metric("memory_rss", size[memoryTypeResident], startTime / 1000));
-
- long procJiffies = getPidJiffies(s);
- if (lastTotalCpuJiffies >= 0 && lastCpuJiffiesMetrics.containsKey(s)) {
- long last = lastCpuJiffiesMetrics.get(s);
- long diff = procJiffies - last;
-
- if (diff >= 0) {
- metrics.add(new Metric("cpu", 100 * ((double) diff) / (sysJiffies - lastTotalCpuJiffies), startTime / 1000));
- }
- }
- lastCpuJiffiesMetrics.put(s, procJiffies);
- s.setSystemMetrics(metrics);
- }
-
- lastTotalCpuJiffies = sysJiffies;
-
- // If none of the services were alive, reschedule in a short time
- if (!someAlive) {
- reschedule(System.currentTimeMillis() - startTime);
- } else {
- schedule();
- }
- }
-
- long getPidJiffies(VespaService service) {
- BufferedReader in;
- String line;
- String[] elems;
- int pid = service.getPid();
-
- try {
- in = new BufferedReader(new FileReader("/proc/" + pid + "/stat"));
- } catch (FileNotFoundException ex) {
- log.log(LogLevel.DEBUG, "Unable to find pid in proc directory " + pid);
- service.setAlive(false);
- return 0;
- }
-
- try {
- line = in.readLine();
- in.close();
- } catch (IOException ex) {
- log.log(LogLevel.DEBUG, "Unable to read line from process stat file", ex);
- return 0;
- }
-
- elems = line.split(" ");
-
- /* Add user mode and kernel mode jiffies for the given process */
- return Long.parseLong(elems[13]) + Long.parseLong(elems[14]);
- }
-
- long getNormalizedSystemJiffies() {
- BufferedReader in;
- String line;
- ArrayList<CpuJiffies> jiffies = new ArrayList<>();
- CpuJiffies total = null;
-
- try {
- in = new BufferedReader(new FileReader("/proc/stat"));
- } catch (FileNotFoundException ex) {
- log.log(LogLevel.ERROR, "Unable to open stat file", ex);
- return 0;
- }
- try {
- while ((line = in.readLine()) != null) {
- if (line.startsWith("cpu ")) {
- total = new CpuJiffies(line);
- } else if (line.startsWith("cpu")) {
- jiffies.add(new CpuJiffies(line));
- }
- }
-
- in.close();
- } catch (IOException ex) {
- log.log(LogLevel.ERROR, "Unable to read line from stat file", ex);
- return 0;
- }
-
- /* Normalize so that a process that uses an entire CPU core will get 100% util */
- if (total != null) {
- return total.getTotalJiffies() / jiffies.size();
- } else {
- return 0;
- }
- }
-
- private void schedule(long time) {
- try {
- systemPollTimer.schedule(new PollTask(this), time);
- } catch(IllegalStateException e){
- log.info("Tried to schedule task, but timer was already shut down.");
- }
- }
-
- public void schedule() {
- schedule(pollingIntervalSecs * 1000);
- }
-
- private void reschedule(long skew) {
- long sleep = (pollingIntervalSecs * 1000) - skew;
-
- // Don't sleep less than 1 min
- sleep = Math.max(60 * 1000, sleep);
- schedule(sleep);
- }
-
-
- private static class PollTask extends TimerTask {
- private final SystemPoller poller;
-
- PollTask(SystemPoller poller) {
- this.poller = poller;
- }
-
- @Override
- public void run() {
- poller.poll();
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPollerProvider.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPollerProvider.java
deleted file mode 100644
index dea7b2b4809..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/SystemPollerProvider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.core.MonitoringConfig;
-import com.yahoo.container.di.componentgraph.Provider;
-
-/**
- * @author gjoranv
- */
-public class SystemPollerProvider implements Provider<SystemPoller> {
-
- private final SystemPoller poller;
-
- /**
- * @param services The list of VespaService instances to monitor for System metrics
- * @param monitoringConfig The interval in seconds between each polling.
- */
- public SystemPollerProvider (VespaServices services, MonitoringConfig monitoringConfig) {
- poller = new SystemPoller(services.getVespaServices(), 60 * monitoringConfig.intervalMinutes());
- poller.poll();
- }
-
- public void deconstruct() {
- poller.stop();
- }
-
- public SystemPoller get() {
- return poller;
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java
deleted file mode 100644
index d3f674176b3..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.HealthMetric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-
-/**
- * Represents a Vespa service
- *
- * @author jobergum
- */
-public class VespaService implements Comparable<VespaService> {
-
- private static final Map<DimensionId, String> EMPTY_DIMENSIONS = Collections.emptyMap();
- private static final String DEFAULT_MONITORING_PREFIX = "vespa";
- public static final String SEPARATOR = ".";
-
- private final String instanceName;
- private final String configId;
- private final String serviceName;
- private final String monitoringPrefix;
- private final Map<DimensionId, String> dimensions;
-
-
- private volatile int pid = -1;
- private volatile String state = "UNKNOWN";
-
- // Used to keep the last polled system metrics for service
- private Metrics systemMetrics;
-
- private final int statePort;
-
- private final RemoteHealthMetricFetcher remoteHealthMetricFetcher;
- private final RemoteMetricsFetcher remoteMetricsFetcher;
-
- private boolean isAlive;
-
- // Used to keep track of log level when health or metrics requests fail
- private AtomicInteger metricsFetchCount = new AtomicInteger(0);
- private AtomicInteger healthFetchCount = new AtomicInteger(0);
-
-
- public static VespaService create(String name, String id, int statePort) {
- return create(name,id, statePort, DEFAULT_MONITORING_PREFIX, EMPTY_DIMENSIONS);
- }
-
- public static VespaService create(String name, String id, int statePort, String monitoringName, Map<DimensionId, String> dimensions) {
- String serviceName = name.replaceAll("\\d*$", "");
- return new VespaService(serviceName, name, id, statePort, monitoringName, dimensions);
- }
-
- VespaService(String serviceName, String configId) {
- this(serviceName, serviceName, configId);
- }
-
- VespaService(String serviceName, String instanceName, String configId) {
- this(serviceName, instanceName, configId, -1, DEFAULT_MONITORING_PREFIX, EMPTY_DIMENSIONS);
- }
-
- private VespaService(String serviceName, String instanceName, String configId,
- int statePort, String monitoringPrefix,
- Map<DimensionId, String> dimensions) {
- this.serviceName = serviceName;
- this.instanceName = instanceName;
- this.monitoringPrefix = monitoringPrefix;
- this.configId = configId;
- this.statePort = statePort;
- this.dimensions = dimensions;
- this.systemMetrics = new Metrics();
- this.isAlive = false;
- this.remoteMetricsFetcher = (this.statePort> 0) ? new RemoteMetricsFetcher(this, this.statePort) : new DummyMetricsFetcher(this);
- this.remoteHealthMetricFetcher = (this.statePort > 0) ? new RemoteHealthMetricFetcher(this, this.statePort) : new DummyHealthMetricFetcher(this);
- }
-
- /**
- * The name used for this service in the monitoring system:
- * monitoring-system-name.serviceName
- */
- public String getMonitoringName() {
- return monitoringPrefix + SEPARATOR + serviceName;
- }
-
- @Override
- public int compareTo(VespaService other) {
- return this.getInstanceName().compareTo(other.getInstanceName());
- }
-
- /**
- * Get the service name/type. E.g 'searchnode', but not 'searchnode2'
- *
- * @return the service name
- */
- public String getServiceName() {
- return this.serviceName;
- }
-
- /**
- * Get the instance name. E.g searchnode2
- *
- * @return the instance service name
- */
- public String getInstanceName() {
- return this.instanceName;
- }
-
- public Map<DimensionId, String> getDimensions() {
- return dimensions;
- }
-
- /**
- * @return The health of this service
- */
- public HealthMetric getHealth() {
- HealthMetric healthMetric = remoteHealthMetricFetcher.getHealth(healthFetchCount.get());
- healthFetchCount.getAndIncrement();
- return healthMetric;
- }
-
- /**
- * Gets the system metrics for this service
- *
- * @return System metrics
- */
- public synchronized Metrics getSystemMetrics() {
- return this.systemMetrics;
- }
-
- /**
- * Get the Metrics registered for this service. Metrics are fetched over HTTP
- * if a metric http port has been defined, otherwise from log file
- *
- * @return the non-system metrics
- */
- public Metrics getMetrics() {
- Metrics remoteMetrics = remoteMetricsFetcher.getMetrics(metricsFetchCount.get());
- metricsFetchCount.getAndIncrement();
- return remoteMetrics;
- }
-
- /**
- * Gets the config id of this service
- *
- * @return the config id
- */
- public String getConfigId() {
- return configId;
- }
-
- /**
- * The current pid of this service
- *
- * @return The pid
- */
- public int getPid() {
- return this.pid;
- }
-
- /**
- * update the pid of this service
- *
- * @param pid The pid that this service runs as
- */
- public void setPid(int pid) {
- this.pid = pid;
- }
-
- /**
- * Get the string representation of the state of this service
- *
- * @return string representing the state of this service - obtained from config-sentinel
- */
- public String getState() {
- return state;
- }
-
- /**
- * Update the state of this service
- *
- * @param state the new state
- */
- public void setState(String state) {
- this.state = state;
- }
-
- /**
- * Check if this pid/service is running
- *
- * @return true if the service is alive (e.g the pid is running)
- */
- public boolean isAlive() {
- return (isAlive && (pid >= 0));
- }
-
- @Override
- public String toString() {
- return instanceName + ":" + pid + ":" + state + ":" + configId;
- }
-
- public void setAlive(boolean alive) {
- this.isAlive = alive;
- }
-
- public synchronized void setSystemMetrics(Metrics systemMetrics) {
- this.systemMetrics = systemMetrics;
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaServices.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaServices.java
deleted file mode 100644
index 2668c158ed6..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaServices.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.core.MonitoringConfig;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import ai.vespa.metricsproxy.service.VespaServicesConfig.Service;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import static ai.vespa.metricsproxy.core.MetricsConsumers.toUnmodifiableLinkedMap;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static com.yahoo.log.LogLevel.DEBUG;
-
-/**
- * Creates representations for the Vespa services running on the node,
- * and provides methods for updating and getting them.
- *
- * @author gjoranv
- */
-public class VespaServices {
- private static final Logger log = Logger.getLogger(VespaServices.class.getName());
-
- public static final String ALL_SERVICES = "all";
-
- private final ConfigSentinelClient sentinel;
- private final List<VespaService> services;
-
- @Inject
- public VespaServices(VespaServicesConfig config, MonitoringConfig monitoringConfig, ConfigSentinelClient sentinel) {
- this.services = createServices(config, monitoringConfig.systemName());
- this.sentinel = sentinel;
- }
-
- @VisibleForTesting
- public VespaServices(List<VespaService> services) {
- this.services = services;
- sentinel = null;
- }
-
- private List<VespaService> createServices(VespaServicesConfig servicesConfig, String monitoringSystemName) {
- List<VespaService> services = new ArrayList<>();
- for (Service s : servicesConfig.service()) {
- log.log(DEBUG, "Re-configuring service " + s.name());
- VespaService vespaService = VespaService.create(s.name(), s.configId(), s.healthport(), monitoringSystemName,
- createServiceDimensions(s));
- services.add(vespaService);
- }
- log.log(DEBUG, "Created new services: " + services.size());
- updateServices(services);
- return services;
- }
-
- /**
- * Sets 'alive=false' for services that are no longer running.
- * Note that the status is updated in-place for the given services.
- */
- public void updateServices(List<VespaService> services) {
- if (sentinel != null) {
- log.log(DEBUG, "Updating services ");
- sentinel.updateServiceStatuses(services);
- }
- }
-
- /**
- * Get all known vespa services
- *
- * @return A list of VespaService objects
- */
- public List<VespaService> getVespaServices() {
- return Collections.unmodifiableList(services);
- }
-
- /**
- * @param id The configid
- * @return A list with size 1 as there should only be one service with the given configid
- */
- public List<VespaService> getInstancesById(String id) {
- List<VespaService> myServices = new ArrayList<>();
- for (VespaService s : services) {
- if (s.getConfigId().equals(id)) {
- myServices.add(s);
- }
- }
-
- return myServices;
- }
-
- /**
- * Get services matching pattern for the name used in the monitoring system.
- *
- * @param service name in monitoring system + service name, without index, e.g: vespa.container
- * @return A list of VespaServices
- */
- public List<VespaService> getMonitoringServices(String service) {
- if (service.equalsIgnoreCase(ALL_SERVICES))
- return services;
-
- List<VespaService> myServices = new ArrayList<>();
- for (VespaService s : services) {
- log.log(DEBUG, () -> "getMonitoringServices. service=" + service + ", checking against " + s + ", which has monitoring name " + s.getMonitoringName());
- if (s.getMonitoringName().equalsIgnoreCase(service)) {
- myServices.add(s);
- }
- }
-
- return myServices;
- }
-
- private static Map<DimensionId, String> createServiceDimensions(Service service) {
- return service.dimension().stream().collect(
- toUnmodifiableLinkedMap(dim -> toDimensionId(dim.key()), Service.Dimension::value));
- }
-
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/package-info.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/package-info.java
deleted file mode 100644
index b478cdf8b5b..00000000000
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-@ExportPackage
-package ai.vespa.metricsproxy.service;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/metrics-proxy/src/main/resources/configdefinitions/application-dimensions.def b/metrics-proxy/src/main/resources/configdefinitions/application-dimensions.def
deleted file mode 100644
index 41b384193e5..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/application-dimensions.def
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.metric.dimensions
-
-# Dimensions based on application properties
-dimensions{} string
diff --git a/metrics-proxy/src/main/resources/configdefinitions/consumers.def b/metrics-proxy/src/main/resources/configdefinitions/consumers.def
deleted file mode 100644
index a1828b1320c..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/consumers.def
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.core
-
-# Consumers with metric definitions
-consumer[].name string default=""
-consumer[].metric[].name string
-consumer[].metric[].description string default=""
-consumer[].metric[].outputname string
-consumer[].metric[].dimension[].key string
-consumer[].metric[].dimension[].value string
diff --git a/metrics-proxy/src/main/resources/configdefinitions/monitoring.def b/metrics-proxy/src/main/resources/configdefinitions/monitoring.def
deleted file mode 100644
index 6fe24c86394..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/monitoring.def
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.core
-
-# The rate at which metrics are passed to the monitoring system. Currently (Apr 2019) only used by SystemPoller.
-intervalMinutes int default=5
-
-# The name used for this application in the monitoring system
-systemName string default=vespa
diff --git a/metrics-proxy/src/main/resources/configdefinitions/node-dimensions.def b/metrics-proxy/src/main/resources/configdefinitions/node-dimensions.def
deleted file mode 100644
index 917cd165a9f..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/node-dimensions.def
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.metric.dimensions
-
-# Dimensions based on node properties
-dimensions{} string
diff --git a/metrics-proxy/src/main/resources/configdefinitions/rpc-connector.def b/metrics-proxy/src/main/resources/configdefinitions/rpc-connector.def
deleted file mode 100644
index 7a8450780de..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/rpc-connector.def
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.rpc
-
-port int
diff --git a/metrics-proxy/src/main/resources/configdefinitions/vespa-services.def b/metrics-proxy/src/main/resources/configdefinitions/vespa-services.def
deleted file mode 100644
index 6fdacfffd42..00000000000
--- a/metrics-proxy/src/main/resources/configdefinitions/vespa-services.def
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package=ai.vespa.metricsproxy.service
-
-# Services with service id and the http port for the metrics page
-service[].configId string
-service[].name string default=""
-service[].port int default=-1
-service[].healthport int default=-1
-service[].dimension[].key string
-service[].dimension[].value string
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java
deleted file mode 100644
index 5997f85a46f..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.stream.Collectors;
-
-/**
- * @author gjoranv
- */
-public class TestUtil {
-
- public static String getContents(String filename) {
- InputStream in = TestUtil.class.getClassLoader().getResourceAsStream(filename);
- if (in == null) {
- throw new RuntimeException("File not found: " + filename);
- }
- return new BufferedReader(new InputStreamReader(in)).lines().collect(Collectors.joining("\n"));
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java
deleted file mode 100644
index 81634323269..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.core;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
-import ai.vespa.metricsproxy.metric.ExternalMetrics;
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import ai.vespa.metricsproxy.service.DummyService;
-import ai.vespa.metricsproxy.service.VespaService;
-import ai.vespa.metricsproxy.service.VespaServices;
-import com.google.common.collect.ImmutableList;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.core.MetricsManager.VESPA_VERSION;
-import static ai.vespa.metricsproxy.core.VespaMetrics.METRIC_TYPE_DIMENSION_ID;
-import static ai.vespa.metricsproxy.core.VespaMetrics.VESPA_CONSUMER_ID;
-import static ai.vespa.metricsproxy.metric.ExternalMetrics.ROLE_DIMENSION;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author gjoranv
- */
-public class MetricsManagerTest {
-
- private MetricsManager metricsManager;
-
- private static final String SERVICE_0_ID = "dummy/id/0";
- private static final String SERVICE_1_ID = "dummy/id/1";
-
- private static final List<VespaService> testServices = ImmutableList.of(
- new DummyService(0, SERVICE_0_ID),
- new DummyService(1, SERVICE_1_ID));
-
- private static final String WHITELISTED_METRIC_ID = "whitelisted";
-
- @Before
- public void setupMetricsManager() {
- metricsManager = getMetricsManager();
- }
-
- @Test
- public void each_service_gets_separate_metrics_packets() {
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertThat(packets.size(), is(2));
-
- assertThat(packets.get(0).dimensions().get(toDimensionId("instance")), is("dummy0"));
- assertThat(packets.get(0).metrics().get(toMetricId("c.test")), is(1.0));
- assertThat(packets.get(0).metrics().get(toMetricId("val")), is(1.05));
-
- assertThat(packets.get(1).dimensions().get(toDimensionId("instance")), is("dummy1"));
- assertThat(packets.get(1).metrics().get(toMetricId("c.test")), is(6.0));
- assertThat(packets.get(1).metrics().get(toMetricId("val")), is(2.35));
- }
-
- @Test
- public void verify_expected_output_from_getMetricsById() {
- String dummy0Metrics = metricsManager.getMetricsByConfigId("dummy/id/0");
- assertThat(dummy0Metrics, containsString("'dummy.id.0'.val=1.050"));
- assertThat(dummy0Metrics, containsString("'dummy.id.0'.c_test=1"));
-
- String dummy1Metrics = metricsManager.getMetricsByConfigId("dummy/id/1");
- assertThat(dummy1Metrics, containsString("'dummy.id.1'.val=2.350"));
- assertThat(dummy1Metrics, containsString("'dummy.id.1'.c_test=6"));
- }
-
- @Test
- public void getServices_returns_service_types() {
- assertThat(metricsManager.getAllVespaServices(), is("dummy"));
- }
-
- @Test
- public void global_dimensions_are_added_but_do_not_override_metric_dimensions() {
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertEquals(2, packets.size());
- assertGlobalDimensions(packets.get(0).dimensions());
- assertGlobalDimensions(packets.get(1).dimensions());
- }
-
- private void assertGlobalDimensions(Map<DimensionId, String> dimensions) {
- assertTrue(dimensions.containsKey(VESPA_VERSION));
- assertEquals("value", dimensions.get(toDimensionId("global")));
- assertEquals("metric-dim", dimensions.get(toDimensionId("dim0")));
- }
-
-
- @Test
- public void system_metrics_are_added() {
- VespaService service0 = testServices.get(0);
- Metrics oldSystemMetrics = service0.getSystemMetrics();
-
- service0.getSystemMetrics().add(new Metric("cpu", 1));
-
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertEquals(3, packets.size());
-
- MetricsPacket systemPacket = packets.get(0); // system metrics are added before other metrics
- assertThat(systemPacket.metrics().get(toMetricId("cpu")), is(1.0));
- assertThat(systemPacket.dimensions().get(toDimensionId("metrictype")), is("system"));
-
- service0.setSystemMetrics(oldSystemMetrics);
- }
-
- @Test
- public void extra_metrics_packets_containing_whitelisted_metrics_are_added() {
- metricsManager.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(new Metric(WHITELISTED_METRIC_ID, 0)))));
-
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertThat(packets.size(), is(3));
- }
-
- @Test
- public void extra_metrics_packets_without_whitelisted_metrics_are_not_added() {
- metricsManager.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(new Metric("not-whitelisted", 0)))));
-
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertThat(packets.size(), is(2));
- }
-
- @Test
- public void extra_dimensions_are_added_to_metrics_packets_that_do_not_have_those_dimensions() {
- metricsManager.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(new Metric(WHITELISTED_METRIC_ID, 0)))
- .putDimension(ROLE_DIMENSION, "role from extraMetrics")));
-
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- for (MetricsPacket packet : packets) {
- assertThat(packet.dimensions().get(ROLE_DIMENSION), is("role from extraMetrics"));
- }
- }
-
- @Test
- public void extra_dimensions_do_not_overwrite_existing_dimension_values() {
- metricsManager.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(new Metric(WHITELISTED_METRIC_ID, 0)))
- .putDimension(METRIC_TYPE_DIMENSION_ID, "from extraMetrics")));
-
- List<MetricsPacket> packets = metricsManager.getMetrics(testServices, Instant.EPOCH);
- assertThat(packets.get(0).dimensions().get(METRIC_TYPE_DIMENSION_ID), is("standard"));
- assertThat(packets.get(1).dimensions().get(METRIC_TYPE_DIMENSION_ID), is("standard"));
- assertThat(packets.get(2).dimensions().get(METRIC_TYPE_DIMENSION_ID), is("from extraMetrics"));
- }
-
- @Test
- public void timestamp_is_adjusted_when_metric_is_less_than_one_minute_younger_than_start_time() {
- Instant START_TIME = Instant.ofEpochSecond(0);
- Instant METRIC_TIME = Instant.ofEpochSecond(59);
- assertEquals(START_TIME, getAdjustedTimestamp(START_TIME, METRIC_TIME));
- }
-
- @Test
- public void timestamp_is_adjusted_when_metric_is_less_than_one_minute_older_than_start_time() {
- Instant START_TIME = Instant.ofEpochSecond(59);
- Instant METRIC_TIME = Instant.ofEpochSecond(0);
- assertEquals(START_TIME, getAdjustedTimestamp(START_TIME, METRIC_TIME));
- }
-
- @Test
- public void timestamp_is_not_adjusted_when_metric_is_at_least_one_minute_younger_than_start_time() {
- Instant START_TIME = Instant.ofEpochSecond(0);
- Instant METRIC_TIME = Instant.ofEpochSecond(60);
- assertEquals(METRIC_TIME, getAdjustedTimestamp(START_TIME, METRIC_TIME));
- }
-
- @Test
- public void timestamp_is_not_adjusted_when_metric_is_at_least_one_minute_older_than_start_time() {
- Instant START_TIME = Instant.ofEpochSecond(60);
- Instant METRIC_TIME = Instant.ofEpochSecond(0);
- assertEquals(METRIC_TIME, getAdjustedTimestamp(START_TIME, METRIC_TIME));
- }
-
- private Instant getAdjustedTimestamp(Instant startTime, Instant metricTime) {
- MetricsPacket.Builder builder = new MetricsPacket.Builder(toServiceId("foo"))
- .timestamp(metricTime.getEpochSecond());
- return MetricsManager.adjustTimestamp(builder, startTime).getTimestamp();
- }
-
- private MetricsManager getMetricsManager() {
- VespaServices vespaServices = new VespaServices(testServices);
- MetricsConsumers consumers = getMetricsConsumers();
- VespaMetrics metrics = new VespaMetrics(consumers, vespaServices);
-
- return new MetricsManager(vespaServices, metrics, new ExternalMetrics(consumers),
- getApplicationDimensions(),getNodeDimensions());
- }
-
- private static MetricsConsumers getMetricsConsumers() {
- Consumer.Metric.Dimension.Builder metricDimension = new Consumer.Metric.Dimension.Builder()
- .key("dim0").value("metric-dim");
-
- return new MetricsConsumers(new ConsumersConfig.Builder()
- .consumer(new Consumer.Builder()
- .name(VESPA_CONSUMER_ID.id)
- .metric(new Consumer.Metric.Builder()
- .name(WHITELISTED_METRIC_ID)
- .outputname(WHITELISTED_METRIC_ID))
- .metric(new Consumer.Metric.Builder()
- .name(DummyService.METRIC_1)
- .outputname(DummyService.METRIC_1)
- .dimension(metricDimension))
- .metric(new Consumer.Metric.Builder()
- .name(DummyService.METRIC_2)
- .outputname(DummyService.METRIC_2)
- .dimension(metricDimension)))
- .build());
- }
-
- private ApplicationDimensions getApplicationDimensions() {
- return new ApplicationDimensions(new ApplicationDimensionsConfig.Builder()
- .dimensions("global", "value").build());
- }
-
- private NodeDimensions getNodeDimensions() {
- return new NodeDimensions(new NodeDimensionsConfig.Builder()
- .dimensions("dim0", "should not override metric dim").build());
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/ExternalMetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/ExternalMetricsTest.java
deleted file mode 100644
index 11c271d46e4..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/ExternalMetricsTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig;
-import ai.vespa.metricsproxy.core.MetricsConsumers;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-
-import java.util.List;
-
-import static ai.vespa.metricsproxy.metric.ExternalMetrics.VESPA_NODE_SERVICE_ID;
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static org.junit.Assert.assertEquals;
-
-/**
- * @author gjoranv
- */
-public class ExternalMetricsTest {
- private static final ConsumerId CUSTOM_CONSUMER_1 = toConsumerId("consumer-1");
- private static final ConsumerId CUSTOM_CONSUMER_2 = toConsumerId("consumer-2");
-
- @Test
- public void extra_metrics_are_added() {
- MetricsConsumers noConsumers = new MetricsConsumers(new ConsumersConfig.Builder().build());
- ExternalMetrics externalMetrics = new ExternalMetrics(noConsumers);
-
- externalMetrics.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))));
-
- List<MetricsPacket.Builder> packets = externalMetrics.getMetrics();
- assertEquals(1, packets.size());
- }
-
- @Test
- public void service_id_is_set_to_vespa_node_id() {
- MetricsConsumers noConsumers = new MetricsConsumers(new ConsumersConfig.Builder().build());
- ExternalMetrics externalMetrics = new ExternalMetrics(noConsumers);
- externalMetrics.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("replace_with_vespa_node_id"))));
-
- List<MetricsPacket.Builder> packets = externalMetrics.getMetrics();
- assertEquals(1, packets.size());
- assertEquals(VESPA_NODE_SERVICE_ID, packets.get(0).build().service);
- }
-
- @Test
- public void custom_consumers_are_added() {
- ConsumersConfig consumersConfig = new ConsumersConfig.Builder()
- .consumer(new ConsumersConfig.Consumer.Builder().name(CUSTOM_CONSUMER_1.id))
- .consumer(new ConsumersConfig.Consumer.Builder().name(CUSTOM_CONSUMER_2.id))
- .build();
- MetricsConsumers consumers = new MetricsConsumers(consumersConfig);
- ExternalMetrics externalMetrics = new ExternalMetrics(consumers);
-
- externalMetrics.setExtraMetrics(ImmutableList.of(
- new MetricsPacket.Builder(toServiceId("foo"))));
-
- List<MetricsPacket.Builder> packets = externalMetrics.getMetrics();
- assertEquals(1, packets.size());
-
- List<ConsumerId> consumerIds = packets.get(0).build().consumers();
- assertEquals(2, consumerIds.size());
- assertEquals(CUSTOM_CONSUMER_1, consumerIds.get(0));
- assertEquals(CUSTOM_CONSUMER_2, consumerIds.get(1));
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/MetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/MetricsTest.java
deleted file mode 100644
index b9e6377c27b..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/MetricsTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric;
-
-import ai.vespa.metricsproxy.service.DummyService;
-import ai.vespa.metricsproxy.service.VespaService;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknowm
- */
-public class MetricsTest {
-
- @Test
- public void testIterator() {
- Metrics m = new Metrics();
- long now = System.currentTimeMillis() / 1000;
- m.add(new Metric("a", 1, now));
- m.add(new Metric("b", 2.5, now));
-
- //should expire after 0 seconds
- m.add(new Metric("c", 2, now));
-
- Map<String, Number> map = new HashMap<>();
-
- for (Metric metric: m.getMetrics()) {
- String k = metric.getName();
-
- assertThat(map.containsKey(k), is(false));
- map.put(k, metric.getValue());
- }
-
- assertThat(map.get("a").intValue(), is(1));
- assertThat(map.get("b").doubleValue(), is(2.5));
- }
-
- @Test
- public void testBasicMetric() {
- Metrics m = new Metrics();
- m.add(new Metric("count", 1, System.currentTimeMillis() / 1000));
- assertThat(m.get("count").intValue(), is(1));
- }
-
- @Test
- public void testHealthMetric() {
- HealthMetric m = HealthMetric.get(null, null);
- assertThat(m.isOk(), is(false));
- m = HealthMetric.get("up", "test message");
- assertThat(m.isOk(), is(true));
- assertThat(m.getMessage(), is("test message"));
- m = HealthMetric.get("ok", "test message");
- assertThat(m.isOk(), is(true));
- assertThat(m.getMessage(), is("test message"));
-
- m = HealthMetric.get("bad", "test message");
- assertThat(m.isOk(), is(false));
- assertThat(m.getStatus(), is("bad"));
- }
-
- @Test
- public void testMetricFormatter() {
- MetricsFormatter formatter = new MetricsFormatter(false, false);
- VespaService service = new DummyService(0, "config.id");
- String data = formatter.format(service, "key", 1);
- assertThat(data, is("'config.id'.key=1"));
-
- formatter = new MetricsFormatter(true, false);
- data = formatter.format(service, "key", 1);
- assertThat(data, is("dummy.'config.id'.key=1"));
-
-
- formatter = new MetricsFormatter(true, true);
- data = formatter.format(service, "key", 1);
- assertThat(data, is("dummy.config.'id'.key=1"));
-
- formatter = new MetricsFormatter(false, true);
- data = formatter.format(service, "key", 1);
- assertThat(data, is("config.'id'.key=1"));
- }
-
- @Test
- public void testTimeAdjustment() {
- assertThat(Metric.adjustTime(0L, 0L), is(0L));
- assertThat(Metric.adjustTime(59L, 59L), is(59L));
- assertThat(Metric.adjustTime(60L, 60L), is(60L));
- assertThat(Metric.adjustTime(59L, 60L), is(60L));
- assertThat(Metric.adjustTime(60L, 59L), is(60L));
- assertThat(Metric.adjustTime(59L, 61L), is(59L));
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/MetricsPacketTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/MetricsPacketTest.java
deleted file mode 100644
index d522a56a9ac..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/MetricsPacketTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model;
-
-import ai.vespa.metricsproxy.metric.Metric;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.junit.Test;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * @author gjoranv
- */
-public class MetricsPacketTest {
-
- @Test
- public void service_cannot_be_null() {
- try {
- MetricsPacket packet = new MetricsPacket.Builder(null)
- .statusCode(0)
- .statusMessage("")
- .timestamp(0L)
- .build();
- fail("Expected exception due to null service.");
- } catch (Exception e) {
- assertEquals("Service cannot be null.", e.getMessage());
- }
- }
-
- @Test
- public void consumers_are_always_distinct() {
- ConsumerId DUPLICATE_CONSUMER = toConsumerId("duplicateConsumer");
-
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .statusCode(0)
- .statusMessage("")
- .addConsumers(Collections.singleton(DUPLICATE_CONSUMER))
- .addConsumers(Collections.singleton(DUPLICATE_CONSUMER))
- .build();
- assertEquals(1, packet.consumers().size());
- }
-
- @Test
- public void builder_can_retain_subset_of_metrics() {
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(
- new Metric("remove", 1),
- new Metric("keep", 2)))
- .retainMetrics(ImmutableSet.of(toMetricId("keep"), toMetricId("non-existent")))
- .build();
-
- assertFalse("should not contain 'remove'", packet.metrics().containsKey(toMetricId("remove")));
- assertTrue("should contain 'keep'", packet.metrics().containsKey(toMetricId("keep")));
- assertFalse("should not contain 'non-existent'", packet.metrics().containsKey(toMetricId("non-existent")));
- }
-
- @Test
- public void builder_applies_output_names() {
- String ONE = "one";
- String TWO = "two";
- String THREE = "three";
- String NON_EXISTENT = "non-existent";
- MetricId ONE_ID = toMetricId(ONE);
- MetricId TWO_ID = toMetricId(TWO);
- MetricId THREE_ID = toMetricId(THREE);
- MetricId NON_EXISTENT_ID = toMetricId(NON_EXISTENT);
-
- Map<MetricId, List<String>> outputNamesById = ImmutableMap.of(
- toMetricId(ONE), ImmutableList.of(ONE),
- toMetricId(TWO), ImmutableList.of(TWO, "dos"),
- toMetricId(THREE), ImmutableList.of("3"),
- toMetricId(NON_EXISTENT), ImmutableList.of(NON_EXISTENT));
-
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .putMetrics(ImmutableList.of(
- new Metric(ONE, 1),
- new Metric(TWO, 2),
- new Metric(THREE, 3)))
- .applyOutputNames(outputNamesById)
- .build();
-
- // Only original name
- assertTrue(packet.metrics().containsKey(ONE_ID));
-
- // Both names
- assertTrue(packet.metrics().containsKey(TWO_ID));
- assertTrue(packet.metrics().containsKey(toMetricId("dos")));
-
- // Only new name
- assertFalse(packet.metrics().containsKey(THREE_ID));
- assertTrue(packet.metrics().containsKey(toMetricId("3")));
-
- // Non-existent metric not added
- assertFalse(packet.metrics().containsKey(NON_EXISTENT_ID));
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonUtilTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonUtilTest.java
deleted file mode 100644
index 28912293fdb..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonUtilTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model.json;
-
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import org.junit.Test;
-
-import java.util.List;
-
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static ai.vespa.metricsproxy.metric.model.json.JsonUtil.toMetricsPackets;
-import static java.util.Collections.singleton;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author gjoranv
- */
-public class JsonUtilTest {
- @Test
- public void json_model_gets_null_status_by_default() {
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .build();
- YamasJsonModel jsonModel = JsonUtil.toYamasArray(singleton(packet)).metrics.get(0);
- assertNull(jsonModel.status_code);
- assertNull(jsonModel.status_msg);
- }
-
- @Test
- public void status_is_included_in_json_model_when_explicitly_asked_for() {
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .build();
- YamasJsonModel jsonModel = JsonUtil.toYamasArray(singleton(packet), true).metrics.get(0);
- assertNotNull(jsonModel.status_code);
- assertNotNull(jsonModel.status_msg);
- }
-
- @Test
- public void timestamp_0_in_packet_is_translated_to_null_in_json_model() {
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .timestamp(0L)
- .build();
- YamasJsonModel jsonModel = JsonUtil.toYamasArray(singleton(packet)).metrics.get(0);
- assertNull(jsonModel.timestamp);
- }
-
- @Test
- public void empty_consumers_is_translated_to_null_routing_in_json_model() {
- MetricsPacket packet = new MetricsPacket.Builder(toServiceId("foo"))
- .build();
- YamasJsonModel jsonModel = JsonUtil.toYamasArray(singleton(packet)).metrics.get(0);
- assertNull(jsonModel.routing);
- }
-
- @Test
- public void empty_json_string_yields_empty_packet_list() {
- List<MetricsPacket.Builder> builders = toMetricsPackets("");
- assertTrue(builders.isEmpty());
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java
deleted file mode 100644
index e91ff32e3b4..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.metric.model.json;
-
-import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Test;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Collections;
-
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-/**
- * Tests for YamasJsonModel and YamasArrayJsonModel
- *
- * @author smorgrav
- * @author gjoranv
- */
-public class YamasJsonModelTest {
-
- private static final String EXPECTED_JSON = "{\"metrics\":[{\"status_code\":0,\"timestamp\":1400047900,\"application\":\"vespa.searchnode\",\"metrics\":{\"cpu\":55.5555555555555,\"memory_virt\":22222222222,\"memory_rss\":5555555555},\"dimensions\":{\"applicationName\":\"app\",\"tenantName\":\"tenant\",\"metrictype\":\"system\",\"instance\":\"searchnode\",\"applicationInstance\":\"default\",\"clustername\":\"cluster\"},\"routing\":{\"yamas\":{\"namespaces\":[\"Vespa\"]}},\"status_msg\":\"Data collected successfully\"}]}";
-
- @Test
- public void array_definition_creates_correct_json() throws IOException {
- YamasJsonModel jsonModel = getYamasJsonModel("yamas-array.json");
-
- YamasArrayJsonModel yamasData = new YamasArrayJsonModel();
- yamasData.add(jsonModel);
-
- assertEquals(EXPECTED_JSON, yamasData.serialize());
- }
-
- @Test
- public void deserialize_serialize_roundtrip() throws IOException {
- YamasJsonModel jsonModel = getYamasJsonModel("yamas-array.json");
-
- // Do some sanity checking
- assertEquals("vespa.searchnode", jsonModel.application);
- assertEquals("Vespa", jsonModel.routing.get("yamas").namespaces.get(0));
- assertEquals(5.555555555E9, jsonModel.metrics.get("memory_rss"), 0.1d); //Not using custom double renderer
-
- // Serialize and verify
- YamasArrayJsonModel yamasArray = new YamasArrayJsonModel();
- yamasArray.add(jsonModel);
- String string = yamasArray.serialize();
- assertEquals(EXPECTED_JSON, string);
- }
-
- @Test
- public void deserialize_serialize_roundtrip_with_metrics_packet() throws IOException {
- YamasJsonModel jsonModel = getYamasJsonModel("yamas-array.json");
- MetricsPacket metricsPacket = JsonUtil.toMetricsPacketBuilder(jsonModel).build();
-
- // Do some sanity checking
- assertEquals(toServiceId("vespa.searchnode"), metricsPacket.service);
- assertEquals(toConsumerId("Vespa"), metricsPacket.consumers().get(0));
- assertEquals(5.555555555E9, metricsPacket.metrics().get(toMetricId("memory_rss")).doubleValue(), 0.1d); //Not using custom double rendrer
-
- // Serialize and verify
- YamasArrayJsonModel yamasArray = JsonUtil.toYamasArray(Collections.singleton(metricsPacket), true);
- String string = yamasArray.serialize();
- assertEquals(EXPECTED_JSON, string);
- }
-
- @Test
- public void missing_routing_object_makes_it_null() throws IOException {
- // Read file that was taken from production (real -life example that is)
- String filename = getClass().getClassLoader().getResource("yamas-array-no-routing.json").getFile();
- BufferedReader reader = Files.newBufferedReader(Paths.get(filename));
- ObjectMapper mapper = new ObjectMapper();
- YamasJsonModel jsonModel = mapper.readValue(reader, YamasJsonModel.class);
-
- // Do some sanity checking
- assertNull(jsonModel.routing);
- }
-
- private YamasJsonModel getYamasJsonModel(String testFile) throws IOException {
- String filename = getClass().getClassLoader().getResource(testFile).getFile();
- BufferedReader reader = Files.newBufferedReader(Paths.get(filename));
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(reader, YamasJsonModel.class);
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/IntegrationTester.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/IntegrationTester.java
deleted file mode 100644
index 6bbf4ae1ef5..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/IntegrationTester.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.rpc;
-
-import ai.vespa.metricsproxy.core.ConsumersConfig;
-import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
-import ai.vespa.metricsproxy.core.MetricsConsumers;
-import ai.vespa.metricsproxy.core.MetricsManager;
-import ai.vespa.metricsproxy.core.MonitoringConfig;
-import ai.vespa.metricsproxy.core.VespaMetrics;
-import ai.vespa.metricsproxy.metric.ExternalMetrics;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
-import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.metric.model.ServiceId;
-import ai.vespa.metricsproxy.service.HttpMetricFetcher;
-import ai.vespa.metricsproxy.service.MockHttpServer;
-import ai.vespa.metricsproxy.service.VespaServices;
-import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import ai.vespa.metricsproxy.service.VespaServicesConfig.Service;
-
-import java.io.IOException;
-
-import static ai.vespa.metricsproxy.core.VespaMetrics.VESPA_CONSUMER_ID;
-import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId;
-import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
-import static ai.vespa.metricsproxy.service.HttpMetricFetcher.STATE_PATH;
-
-
-/**
- * Setup and shutdown of config and servers for integration-style unit tests.
- *
- * @author hmusum
- * @author gjoranv
- */
-public class IntegrationTester implements AutoCloseable {
-
- static final String MONITORING_SYSTEM = "test-system";
- static final ConsumerId CUSTOM_CONSUMER_ID = toConsumerId("custom-consumer");
- static final String SERVICE_1_CONFIG_ID = "container/qrserver.0";
- static final String SERVICE_2_CONFIG_ID = "storage/cluster.storage/storage/0";
-
- private final int httpPort;
- private final int rpcPort;
- private final RpcConnector connector;
- private final MockHttpServer mockHttpServer;
- private final VespaServices vespaServices;
-
- static {
- HttpMetricFetcher.CONNECTION_TIMEOUT = 60000; // 60 secs in unit tests
- }
-
- IntegrationTester(int httpPort, int rpcPort) {
- if (httpPort == 0 || rpcPort == 0) {
- throw new IllegalArgumentException("http port and rpc port must be defined");
- }
- this.httpPort = httpPort;
- this.rpcPort = rpcPort;
- try {
- mockHttpServer = new MockHttpServer(httpPort, null, STATE_PATH);
- } catch (IOException e) {
- throw new RuntimeException("Unable to start web server on port:" + httpPort);
- }
-
- vespaServices = new VespaServices(servicesConfig(), monitoringConfig(), null);
- MetricsConsumers consumers = new MetricsConsumers(consumersConfig());
- VespaMetrics vespaMetrics = new VespaMetrics(consumers, vespaServices);
- ExternalMetrics externalMetrics = new ExternalMetrics(consumers);
- ApplicationDimensions appDimensions = new ApplicationDimensions(applicationDimensionsConfig());
- NodeDimensions nodeDimensions = new NodeDimensions(nodeDimensionsConfig());
-
- connector = new RpcConnector(rpcConnectorConfig());
- RpcServer server = new RpcServer(connector, vespaServices, new MetricsManager(vespaServices, vespaMetrics, externalMetrics, appDimensions, nodeDimensions));
- }
-
- MockHttpServer httpServer() {
- return mockHttpServer;
- }
-
- VespaServices vespaServices() { return vespaServices; }
-
- @Override
- public void close() {
- mockHttpServer.close();
- this.connector.stop();
- }
-
- private RpcConnectorConfig rpcConnectorConfig() {
- return new RpcConnectorConfig.Builder()
- .port(rpcPort)
- .build();
- }
-
- private ConsumersConfig consumersConfig() {
- return new ConsumersConfig.Builder()
- .consumer(createConsumer(VESPA_CONSUMER_ID, "foo.count", "foo_count"))
- .consumer(createConsumer(CUSTOM_CONSUMER_ID, "foo.count", "foo.count"))
- .build();
- }
-
- private static Consumer.Builder createConsumer(ConsumerId consumerId, String metricName, String outputName) {
- return new Consumer.Builder()
- .name(consumerId.id)
- .metric(new Consumer.Metric.Builder()
- .dimension(new Consumer.Metric.Dimension.Builder().key("somekey").value("somevalue"))
- .name(metricName)
- .outputname(outputName));
- }
-
- private VespaServicesConfig servicesConfig() {
- return new VespaServicesConfig.Builder()
- .service(createService(toServiceId("qrserver"), SERVICE_1_CONFIG_ID, httpPort))
- .service(createService(toServiceId("storagenode"), SERVICE_2_CONFIG_ID, httpPort))
- .build();
- }
-
- private static Service.Builder createService(ServiceId serviceId, String configId, int port) {
- return new Service.Builder()
- .name(serviceId.id)
- .configId(configId)
- .port(port)
- .healthport(port)
- .dimension(new Service.Dimension.Builder().key("serviceDim").value("serviceDimValue"));
- }
-
- private MonitoringConfig monitoringConfig() {
- return new MonitoringConfig.Builder()
- .systemName(MONITORING_SYSTEM)
- .build();
- }
-
- private ApplicationDimensionsConfig applicationDimensionsConfig() {
- return new ApplicationDimensionsConfig.Builder().build();
- }
-
- private NodeDimensionsConfig nodeDimensionsConfig() {
- return new NodeDimensionsConfig.Builder().build();
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcHealthMetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcHealthMetricsTest.java
deleted file mode 100644
index 20fb69e410e..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcHealthMetricsTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.rpc;
-
-import ai.vespa.metricsproxy.TestUtil;
-import ai.vespa.metricsproxy.metric.HealthMetric;
-import ai.vespa.metricsproxy.service.MockHttpServer;
-import ai.vespa.metricsproxy.service.VespaService;
-import com.yahoo.jrt.Request;
-import com.yahoo.jrt.Spec;
-import com.yahoo.jrt.StringValue;
-import com.yahoo.jrt.Supervisor;
-import com.yahoo.jrt.Target;
-import com.yahoo.jrt.Transport;
-import org.junit.Test;
-
-import java.util.List;
-
-import static ai.vespa.metricsproxy.rpc.IntegrationTester.SERVICE_1_CONFIG_ID;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author jobergum
- * @author gjoranv
- */
-public class RpcHealthMetricsTest {
-
- private static final String HEALTH_OK_RESPONSE =
- TestUtil.getContents("health-check.response.json");
- private static final String HEALTH_FAILED_RESPONSE =
- TestUtil.getContents("health-check-failed.response.json");
- private static final String WANTED_RPC_RESPONSE =
- TestUtil.getContents("rpc-json-output-check.json").trim();
-
- // see factory/doc/port-ranges.txt
- private static final int httpPort = 18635;
- private static final int rpcPort = 18636;
-
- @Test
- public void expected_response_is_returned() {
- try (IntegrationTester tester = new IntegrationTester(httpPort, rpcPort)) {
-
- MockHttpServer mockHttpServer = tester.httpServer();
- mockHttpServer.setResponse(HEALTH_OK_RESPONSE);
- List<VespaService> services = tester.vespaServices().getInstancesById(SERVICE_1_CONFIG_ID);
-
- assertThat(services.size(), is(1));
- VespaService qrserver = services.get(0);
- HealthMetric h = qrserver.getHealth();
- assertNotNull("Health metric should never be null", h);
- assertThat("Status failed, reason = " + h.getMessage(), h.isOk(), is(true));
- assertThat(h.getMessage(), is("WORKING"));
-
- mockHttpServer.setResponse(HEALTH_FAILED_RESPONSE);
- h = qrserver.getHealth();
- assertNotNull("Health metric should never be null", h);
- assertThat("Status should be failed" + h.getMessage(), h.isOk(), is(false));
- assertThat(h.getMessage(), is("SOMETHING FAILED"));
-
- String jsonRPCMessage = getHealthMetrics(qrserver.getMonitoringName());
- assertThat(jsonRPCMessage, is(WANTED_RPC_RESPONSE));
- }
- }
-
- @Test
- public void non_existent_service_name_returns_an_error_message() {
- try (IntegrationTester tester = new IntegrationTester(httpPort, rpcPort)) {
- String jsonRPCMessage = getHealthMetrics("non-existing service");
- assertThat(jsonRPCMessage, is("105: No service with name 'non-existing service'"));
- }
- }
-
- private String getHealthMetrics(String service) {
- Supervisor supervisor = new Supervisor(new Transport());
- Target target = supervisor.connect(new Spec("localhost", rpcPort));
- Request req = new Request("getHealthMetricsForYamas");
- req.parameters().add(new StringValue(service));
- String returnValue;
-
- target.invokeSync(req, 20.0);
- if (req.checkReturnTypes("s")) {
- returnValue = req.returnValues().get(0).asString();
- } else {
- System.out.println("RpcServer invocation failed " + req.errorCode() + ": " + req.errorMessage());
- returnValue = req.errorCode() + ": " + req.errorMessage();
- }
- target.close();
- supervisor.transport().shutdown().join();
-
- return returnValue;
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java
deleted file mode 100644
index f264fd13ddc..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.rpc;
-
-import ai.vespa.metricsproxy.TestUtil;
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.model.ConsumerId;
-import ai.vespa.metricsproxy.service.VespaService;
-import com.yahoo.jrt.Request;
-import com.yahoo.jrt.Spec;
-import com.yahoo.jrt.StringValue;
-import com.yahoo.jrt.Supervisor;
-import com.yahoo.jrt.Target;
-import com.yahoo.jrt.Transport;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Test;
-
-import java.util.List;
-
-import static ai.vespa.metricsproxy.core.VespaMetrics.VESPA_CONSUMER_ID;
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static ai.vespa.metricsproxy.rpc.IntegrationTester.CUSTOM_CONSUMER_ID;
-import static ai.vespa.metricsproxy.rpc.IntegrationTester.MONITORING_SYSTEM;
-import static ai.vespa.metricsproxy.rpc.IntegrationTester.SERVICE_1_CONFIG_ID;
-import static ai.vespa.metricsproxy.rpc.IntegrationTester.SERVICE_2_CONFIG_ID;
-import static ai.vespa.metricsproxy.service.VespaServices.ALL_SERVICES;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author jobergum
- * @author gjoranv
- */
-public class RpcMetricsTest {
-
- private static final String METRICS_RESPONSE_CCL =
- TestUtil.getContents("metrics-storage-simple.json").trim();
-
- // see factory/doc/port-ranges.txt
- private static final int httpPort = 18633;
- private static final int rpcPort = 18634;
-
- @Test
- public void testGetMetrics() throws Exception {
- try (IntegrationTester tester = new IntegrationTester(httpPort, rpcPort)) {
- tester.httpServer().setResponse(METRICS_RESPONSE_CCL);
- List<VespaService> services = tester.vespaServices().getInstancesById(SERVICE_1_CONFIG_ID);
-
- assertThat("#Services should be 1 for config id " + SERVICE_1_CONFIG_ID, services.size(), is(1));
-
- VespaService qrserver = services.get(0);
- assertThat(qrserver.getMonitoringName(), is(MONITORING_SYSTEM + VespaService.SEPARATOR + "qrserver"));
-
- Metrics metrics = qrserver.getMetrics();
- assertThat("Fetched number of metrics is not correct", metrics.size(), is(2));
- Metric m = metrics.getMetric("foo.count");
- assertNotNull("Did not find expected metric with name 'foo.count'", m);
- Metric m2 = metrics.getMetric("bar.count");
- assertNotNull("Did not find expected metric with name 'bar.count'", m2);
-
- // Setup RPC client
- Supervisor supervisor = new Supervisor(new Transport());
- Target target = supervisor.connect(new Spec("localhost", rpcPort));
-
- verifyMetricsFromRpcRequest(qrserver, target);
-
- services = tester.vespaServices().getInstancesById(SERVICE_2_CONFIG_ID);
- assertThat("#Services should be 1 for config id " + SERVICE_2_CONFIG_ID, services.size(), is(1));
-
- VespaService storageService = services.get(0);
- verfiyMetricsFromServiceObject(storageService);
-
- String metricsById = getMetricsById(storageService.getConfigId(), target);
- assertThat(metricsById, is("'storage.cluster.storage.storage.0'.foo_count=1 "));
-
- String jsonResponse = getMetricsForYamas("non-existing", target).trim();
- assertThat(jsonResponse, is("105: No service with name 'non-existing'"));
-
- verifyMetricsFromRpcRequestForAllServices(target);
-
- // Shutdown RPC
- target.close();
- supervisor.transport().shutdown().join();
- }
- }
-
- private static void verifyMetricsFromRpcRequest(VespaService service, Target target) throws JSONException {
- String jsonResponse = getMetricsForYamas(service.getMonitoringName(), target).trim();
- JSONArray metrics = new JSONObject(jsonResponse).getJSONArray("metrics");
- assertThat("Expected 3 metric messages", metrics.length(), is(3));
- for (int i = 0; i < metrics.length() - 1; i++) { // The last "metric message" contains only status code/message
- JSONObject jsonObject = metrics.getJSONObject(i);
- assertFalse(jsonObject.has("status_code"));
- assertFalse(jsonObject.has("status_msg"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("foo"), is("bar"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("bar"), is("foo"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("serviceDim"), is("serviceDimValue"));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").length(), is(1));
- if (jsonObject.getJSONObject("metrics").has("foo_count")) {
- assertThat(jsonObject.getJSONObject("metrics").getInt("foo_count"), is(1));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(VESPA_CONSUMER_ID.id));
- } else {
- assertThat(jsonObject.getJSONObject("metrics").getInt("foo.count"), is(1));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(CUSTOM_CONSUMER_ID.id));
- }
- }
-
- verifyStatusMessage(metrics.getJSONObject(metrics.length() - 1));
- }
-
- private void verfiyMetricsFromServiceObject(VespaService service) {
- Metrics storageMetrics = service.getMetrics();
- assertThat(storageMetrics.size(), is(2));
- Metric foo = storageMetrics.getMetric("foo.count");
- assertNotNull("Did not find expected metric with name 'foo.count'", foo);
- assertThat("Expected 2 dimensions for metric foo", foo.getDimensions().size(), is(2));
- assertThat("Metric foo did not contain correct dimension mapping for key = foo.count", foo.getDimensions().containsKey(toDimensionId("foo")), is(true));
- assertThat("Metric foo did not contain correct dimension", foo.getDimensions().get(toDimensionId("foo")), is("bar"));
- assertThat("Metric foo did not contain correct dimension", foo.getDimensions().containsKey(toDimensionId("bar")), is(true));
- assertThat("Metric foo did not contain correct dimension for key = bar", foo.getDimensions().get(toDimensionId("bar")), is("foo"));
- }
-
- private void verifyMetricsFromRpcRequestForAllServices(Target target) throws JSONException {
- // Verify that metrics for all services can be retrieved in one request.
- String allServicesResponse = getMetricsForYamas(ALL_SERVICES, target).trim();
- JSONArray allServicesMetrics = new JSONObject(allServicesResponse).getJSONArray("metrics");
- assertThat(allServicesMetrics.length(), is(5));
- }
-
- @Test
- public void testGetAllMetricNames() {
- try (IntegrationTester tester = new IntegrationTester(httpPort, rpcPort)) {
-
- tester.httpServer().setResponse(METRICS_RESPONSE_CCL);
- List<VespaService> services = tester.vespaServices().getInstancesById(SERVICE_1_CONFIG_ID);
-
- assertThat(services.size(), is(1));
- Metrics metrics = services.get(0).getMetrics();
- assertThat("Fetched number of metrics is not correct", metrics.size(), is(2));
- Metric m = metrics.getMetric("foo.count");
- assertNotNull("Did not find expected metric with name 'foo.count'", m);
-
-
- Metric m2 = metrics.getMetric("bar.count");
- assertNotNull("Did not find expected metric with name 'bar'", m2);
-
- // Setup RPC
- Supervisor supervisor = new Supervisor(new Transport());
- Target target = supervisor.connect(new Spec("localhost", rpcPort));
-
- String response = getAllMetricNamesForService(services.get(0).getMonitoringName(), VESPA_CONSUMER_ID, target);
- assertThat(response, is("foo.count=ON;output-name=foo_count,bar.count=OFF,"));
-
- // Shutdown RPC
- target.close();
- supervisor.transport().shutdown().join();
- }
- }
-
- private static String getMetricsForYamas(String service, Target target) {
- Request req = new Request("getMetricsForYamas");
- req.parameters().add(new StringValue(service));
- return invoke(req, target);
- }
-
- private String getMetricsById(String service, Target target) {
- Request req = new Request("getMetricsById");
- req.parameters().add(new StringValue(service));
- return invoke(req, target);
- }
-
- private String getAllMetricNamesForService(String service, ConsumerId consumer, Target target) {
- Request req = new Request("getAllMetricNamesForService");
- req.parameters().add(new StringValue(service));
- req.parameters().add(new StringValue(consumer.id));
- return invoke(req, target);
- }
-
- private static String invoke(Request req, Target target) {
- String returnValue;
- target.invokeSync(req, 20.0);
- if (req.checkReturnTypes("s")) {
- returnValue = req.returnValues().get(0).asString();
- } else {
- System.out.println(req.methodName() + " from rpcserver - Invocation failed "
- + req.errorCode() + ": " + req.errorMessage());
- returnValue = req.errorCode() + ": " + req.errorMessage();
- }
- return returnValue;
- }
-
- private static void verifyStatusMessage(JSONObject jsonObject) throws JSONException {
- assertThat(jsonObject.getInt("status_code"), is(0));
- assertThat(jsonObject.getString("status_msg"), notNullValue());
- assertThat(jsonObject.getString("application"), notNullValue());
- assertThat(jsonObject.getString("routing"), notNullValue());
- assertThat(jsonObject.length(), is(4));
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelClientTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelClientTest.java
deleted file mode 100644
index bd61b8443aa..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelClientTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknown
- */
-public class ConfigSentinelClientTest {
-
- @Test
- public void testConfigSentinelClient() {
- ConfigSentinelDummy configsentinel = new ConfigSentinelDummy();
- List<VespaService> services = new ArrayList<>();
- VespaService docproc = new VespaService("docprocservice", "docproc/cluster.x.indexing/0");
- VespaService searchnode4 = new VespaService("searchnode4", "search/cluster.x/g0/c1/r1");
- VespaService qrserver = new VespaService("qrserver", "container/qrserver.0");
-
- services.add(searchnode4);
- services.add(qrserver);
- services.add(docproc);
-
- MockConfigSentinelClient client = new MockConfigSentinelClient(configsentinel);
- client.updateServiceStatuses(services);
-
- assertThat(qrserver.getPid(), is(6520));
- assertThat(qrserver.getState(), is("RUNNING"));
- assertThat(qrserver.isAlive(), is(true));
- assertThat(searchnode4.getPid(), is(6534));
- assertThat(searchnode4.getState(), is("RUNNING"));
- assertThat(searchnode4.isAlive(), is(true));
-
- assertThat(docproc.getPid(), is(-1));
- assertThat(docproc.getState(), is("FINISHED"));
- assertThat(docproc.isAlive(), is(false));
-
-
- configsentinel.reConfigure();
-
- client.ping(docproc);
- assertThat(docproc.getPid(), is(100));
- assertThat(docproc.getState(), is("RUNNING"));
- assertThat(docproc.isAlive(), is(true));
-
- //qrserver has yet not been checked
- assertThat(qrserver.isAlive(), is(true));
-
- client.updateServiceStatuses(services);
-
- assertThat(docproc.getPid(), is(100));
- assertThat(docproc.getState(), is("RUNNING"));
- assertThat(docproc.isAlive(), is(true));
- //qrserver is no longer running on this node - so should be false
- assertThat(qrserver.isAlive(), is(false));
- }
-
- @Test
- public void testElastic() throws Exception {
- String response = "container state=RUNNING mode=AUTO pid=14338 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"get/container.0\"\n" +
- "container-clustercontroller state=RUNNING mode=AUTO pid=25020 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/cluster-controllers/0\"\n" +
- "distributor state=RUNNING mode=AUTO pid=25024 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/distributor/0\"\n" +
- "docprocservice state=RUNNING mode=AUTO pid=11973 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"docproc/cluster.search.indexing/0\"\n" +
- "logd state=RUNNING mode=AUTO pid=25016 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"hosts/vespa19.dev.gq1.yahoo.com/logd\"\n" +
- "logserver state=RUNNING mode=AUTO pid=25018 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/logserver\"\n" +
- "metricsproxy state=RUNNING mode=AUTO pid=13107 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"hosts/vespa19.dev.gq1.yahoo.com/metricsproxy\"\n" +
- "searchnode state=RUNNING mode=AUTO pid=25023 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/search/cluster.search/0\"\n" +
- "slobrok state=RUNNING mode=AUTO pid=25019 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/slobrok.0\"\n" +
- "topleveldispatch state=RUNNING mode=AUTO pid=25026 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/search/cluster.search/tlds/tld.0\"\n" +
- "\n";
-
- ConfigSentinelDummy configsentinel = new ConfigSentinelDummy(response);
- List<VespaService> services = new ArrayList<>();
-
- VespaService container = VespaService.create("container", "get/container.0", -1);
-
- VespaService containerClusterController =
- VespaService.create("container-clustercontroller", "get/container.0", -1);
-
- VespaService notPresent = VespaService.create("dummy","fake", -1);
-
- services.add(container);
- services.add(containerClusterController);
- services.add(notPresent);
-
- MockConfigSentinelClient client = new MockConfigSentinelClient(configsentinel);
- client.updateServiceStatuses(services);
- assertThat(container.isAlive(),is(true));
- assertThat(container.getPid(),is(14338));
- assertThat(container.getState(),is("RUNNING"));
-
- assertThat(containerClusterController.isAlive(),is(true));
- assertThat(containerClusterController.getPid(),is(25020));
- assertThat(containerClusterController.getState(),is("RUNNING"));
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelDummy.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelDummy.java
deleted file mode 100644
index 108f5c18e1d..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ConfigSentinelDummy.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-package ai.vespa.metricsproxy.service;
-
-/**
- * @author Eirik Nygaard
- */
-public class ConfigSentinelDummy {
- private String serviceList =
- "docprocservice state=FINISHED mode=MANUAL pid=6555 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"docproc/cluster.x.indexing/0\"\n"
- + "distributor state=RUNNING mode=AUTO pid=6548 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/distributor/0\"\n"
- + "fleetcontroller state=RUNNING mode=AUTO pid=6543 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/fleetcontroller/0\"\n"
- + "storagenode state=RUNNING mode=AUTO pid=6539 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/storage/0\"\n"
- + "searchnode4 state=RUNNING mode=AUTO pid=6534 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c1/r1\"\n"
- + "qrserver2 state=RUNNING mode=AUTO pid=6521 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"container/qrserver.1\"\n"
- + "logserver state=RUNNING mode=AUTO pid=6518 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/logserver\"\n"
- + "logd state=RUNNING mode=AUTO pid=6517 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"hosts/dell-bl5s7.trondheim.corp.yahoo.com/logd\"\n"
- + "searchnode2 state=RUNNING mode=AUTO pid=6527 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c0/r1\"\n"
- + "topleveldispatch2 state=RUNNING mode=AUTO pid=6525 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/tlds/tld.1\"\n"
- + "topleveldispatch state=RUNNING mode=AUTO pid=6524 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/tlds/tld.0\"\n"
- + "clustercontroller2 state=RUNNING mode=AUTO pid=6523 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/rtx/1\"\n"
- + "clustercontroller state=RUNNING mode=AUTO pid=6522 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/rtx/0\"\n"
- + "slobrok state=RUNNING mode=AUTO pid=6519 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/slobrok.0\"\n"
- + "searchnode3 state=RUNNING mode=AUTO pid=6529 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c1/r0\"\n"
- + "searchnode state=RUNNING mode=AUTO pid=6526 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c0/r0\"\n"
- + "qrserver state=RUNNING mode=AUTO pid=6520 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"container/qrserver.0\"\n"
- + "\n";
-
-
- public ConfigSentinelDummy() {
- }
-
- public ConfigSentinelDummy(String response) {
- serviceList = response;
- }
-
- public void reConfigure() {
- this.serviceList = "docprocservice state=RUNNING mode=AUTO pid=100 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"docproc/cluster.x.indexing/0\"\n"
- + "distributor state=RUNNING mode=AUTO pid=6548 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/distributor/0\"\n"
- + "fleetcontroller state=RUNNING mode=AUTO pid=6543 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/fleetcontroller/0\"\n"
- + "storagenode state=RUNNING mode=AUTO pid=6539 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"storage/cluster.storage/storage/0\"\n"
- + "searchnode4 state=RUNNING mode=AUTO pid=6534 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c1/r1\"\n"
- + "qrserver2 state=RUNNING mode=AUTO pid=6521 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"container/qrserver.1\"\n"
- + "logserver state=RUNNING mode=AUTO pid=6518 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/logserver\"\n"
- + "logd state=RUNNING mode=AUTO pid=6517 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"hosts/dell-bl5s7.trondheim.corp.yahoo.com/logd\"\n"
- + "searchnode2 state=RUNNING mode=AUTO pid=6527 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c0/r1\"\n"
- + "topleveldispatch2 state=RUNNING mode=AUTO pid=6525 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/tlds/tld.1\"\n"
- + "topleveldispatch state=RUNNING mode=AUTO pid=6524 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/tlds/tld.0\"\n"
- + "clustercontroller2 state=RUNNING mode=AUTO pid=6523 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/rtx/1\"\n"
- + "clustercontroller state=RUNNING mode=AUTO pid=6522 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/rtx/0\"\n"
- + "slobrok state=RUNNING mode=AUTO pid=6519 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"admin/slobrok.0\"\n"
- + "searchnode3 state=RUNNING mode=AUTO pid=6529 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c1/r0\"\n"
- + "searchnode state=RUNNING mode=AUTO pid=6526 exitstatus=0 autostart=TRUE autorestart=TRUE id=\"search/cluster.x/g0/c0/r0\"\n"
- + "\n";
- }
-
- public String getServiceList() {
- return serviceList;
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java
deleted file mode 100644
index 4174b18f3a7..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.TestUtil;
-import ai.vespa.metricsproxy.metric.Metric;
-import org.json.JSONException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknown
- */
-public class ContainerServiceTest {
-
- private MockHttpServer service;
- private int csPort;
-
- @BeforeClass
- public static void init() {
- HttpMetricFetcher.CONNECTION_TIMEOUT = 60000; // 60 secs in unit tests
- }
-
- @Before
- public void setupHTTPServer() {
- csPort = 18637; // see factory/doc/port-ranges.txt
- try {
- String response = TestUtil.getContents("metrics-container-state-multi-chain.json");
- service = new MockHttpServer(csPort, response, HttpMetricFetcher.METRICS_PATH);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void testMultipleQueryDimensions() throws JSONException {
- int count = 0;
- VespaService service = VespaService.create("service1", "id", csPort);
- for (Metric m : service.getMetrics().getMetrics()) {
- if (m.getName().equals("queries.rate")) {
- count++;
- System.out.println("Name: " + m.getName() + " value: " + m.getValue());
- if (m.getDimensions().get(toDimensionId("chain")).equals("asvBlendingResult")) {
- assertThat((Double)m.getValue(), is(26.4));
- } else if (m.getDimensions().get(toDimensionId("chain")).equals("blendingResult")) {
- assertThat((Double)m.getValue(), is(0.36666666666666664));
- } else {
- assertThat("Unknown unknown chain", false, is(true));
- }
- }
- }
- assertThat(count, is(2));
- }
-
- @After
- public void shutdown() {
- this.service.close();
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/DummyService.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/DummyService.java
deleted file mode 100644
index 380a992aead..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/DummyService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.metric.Metric;
-import ai.vespa.metricsproxy.metric.Metrics;
-
-/**
- * @author Unknown
- */
-public class DummyService extends VespaService {
- static final String NAME = "dummy";
- public static final String METRIC_1 = "c.test";
- public static final String METRIC_2 = "val";
-
- private final int num;
-
- public DummyService(int num, String configid) {
- super(NAME, NAME + num, configid);
- this.num = num;
- }
-
- @Override
- public Metrics getMetrics() {
- Metrics m = new Metrics();
-
- long timestamp = System.currentTimeMillis() / 1000;
- m.add(new Metric(METRIC_1, 5 * num + 1, timestamp));
- m.add(new Metric(METRIC_2, 1.3 * num + 1.05, timestamp));
-
- return m;
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MetricsFetcherTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MetricsFetcherTest.java
deleted file mode 100644
index 27e1bb97943..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MetricsFetcherTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.TestUtil;
-import ai.vespa.metricsproxy.metric.Metrics;
-import org.junit.Test;
-
-import java.io.File;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknowm
- */
-public class MetricsFetcherTest {
- private static int port = 9; //port number is not used in this test
-
- @Test
- public void testStateFormatMetricsParse() {
- String jsonData = TestUtil.getContents("metrics-state.json");
- RemoteMetricsFetcher fetcher = new RemoteMetricsFetcher(new DummyService(0, "dummy/id/0"), port);
- Metrics metrics = fetcher.createMetrics(jsonData, 0);
- assertThat("Wrong number of metrics", metrics.size(), is(10));
- assertThat("Wrong value for metric", metrics.get("query_hits.count").intValue(), is(28));
- assertThat("Wrong value for metric ", metrics.get("queries.rate").doubleValue(), is(0.4667));
- assertThat("Wrong timestamp", metrics.getTimeStamp(), is(1334134700L));
- }
-
- @Test
- public void testEmptyJson() {
- String jsonData = "{}";
- RemoteMetricsFetcher fetcher = new RemoteMetricsFetcher(new DummyService(0, "dummy/id/0"), port);
- Metrics metrics = fetcher.createMetrics(jsonData, 0);
- assertThat("Wrong number of metrics", metrics.size(), is(0));
- }
-
- @Test
- public void testErrors() {
- String jsonData;
- Metrics metrics;
-
- RemoteMetricsFetcher fetcher = new RemoteMetricsFetcher(new DummyService(0, "dummy/id/0"), port);
-
- jsonData = "";
- metrics = fetcher.createMetrics(jsonData, 0);
- assertThat("Wrong number of metrics", metrics.size(), is(0));
-
- jsonData = "{\n" +
- "\"status\" : {\n" +
- " \"code\" : \"up\",\n" +
- " \"message\" : \"Everything ok here\"\n" +
- "}\n" +
- "}";
- metrics = fetcher.createMetrics(jsonData, 0);
- assertThat("Wrong number of metrics", metrics.size(), is(0));
-
- jsonData = "{\n" +
- "\"status\" : {\n" +
- " \"code\" : \"up\",\n" +
- " \"message\" : \"Everything ok here\"\n" +
- "},\n" +
- "\"metrics\" : {\n" +
- " \"snapshot\" : {\n" +
- " \"from\" : 1334134640.089,\n" +
- " \"to\" : 1334134700.088\n" +
- " },\n" +
- " \"values\" : [\n" +
- " {\n" +
- " \"name\" : \"queries\",\n" +
- " \"description\" : \"Number of queries executed during snapshot interval\",\n" +
- " \"values\" : {\n" +
- " \"count\" : null,\n" +
- " \"rate\" : 0.4667\n" +
- " },\n" +
- " \"dimensions\" : {\n" +
- " \"searcherid\" : \"x\"\n" +
- " }\n" +
- " }\n" + "" +
- " ]\n" +
- "}\n" +
- "}";
-
- metrics = fetcher.createMetrics(jsonData, 0);
- assertThat("Wrong number of metrics", metrics.size(), is(0));
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockConfigSentinelClient.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockConfigSentinelClient.java
deleted file mode 100644
index 917c529e63e..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockConfigSentinelClient.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-package ai.vespa.metricsproxy.service;
-
-import com.yahoo.log.LogLevel;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Logger;
-
-/**
- * Mock config sentinel
- *
- * @author hmusum
- */
-public class MockConfigSentinelClient extends ConfigSentinelClient {
- private final ConfigSentinelDummy configSentinel;
- private final static Logger log = Logger.getLogger(MockConfigSentinelClient.class.getPackage().getName());
-
- public MockConfigSentinelClient(ConfigSentinelDummy configSentinel) {
- super();
- this.configSentinel = configSentinel;
- }
-
- @Override
- protected synchronized void setStatus(List<VespaService> services) throws Exception {
- List<VespaService> updatedServices = new ArrayList<>();
- String[] lines = configSentinel.getServiceList().split("\n");
- for (String line : lines) {
- if (line.equals("")) {
- break;
- }
-
- VespaService s = parseServiceString(line, services);
- if (s != null) {
- updatedServices.add(s);
- }
- }
-
- //Check if there are services that were not found in
- //from the sentinel
- for (VespaService s : services) {
- if (!updatedServices.contains(s)) {
- log.log(LogLevel.DEBUG, "Service " + s + " is no longer found with sentinel - setting alive = false");
- s.setAlive(false);
- }
- }
- }
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockHttpServer.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockHttpServer.java
deleted file mode 100644
index fdf2fae3081..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/MockHttpServer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-
-/**
- * @author jobergum
- */
-public class MockHttpServer {
-
- private String response;
- private HttpServer server;
-
- /**
- * Mock http server that will return response as body
- *
- * @param port the port to listen to
- * @param response the response to return along with 200 OK
- * @param path the file path that the server will accept requests for. E.g /state/v1/metrics
- */
- public MockHttpServer(int port, String response, String path) throws IOException {
- this.response = response;
- this.server = HttpServer.create(new InetSocketAddress(port), 10);
- this.server.createContext(path, new MyHandler());
- this.server.setExecutor(null); // creates a default executor
- this.server.start();
- System.out.println("Started web server on port " + port);
- }
-
- public synchronized void setResponse(String r) {
- this.response = r;
- }
-
- public void close() {
- this.server.stop(0);
- }
-
- private class MyHandler implements HttpHandler {
- public void handle(HttpExchange t) throws IOException {
- t.sendResponseHeaders(200, response.length());
- OutputStream os = t.getResponseBody();
- os.write(response.getBytes());
- os.close();
- }
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/SystemPollerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/SystemPollerTest.java
deleted file mode 100644
index a42d52b7ea6..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/SystemPollerTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknown
- */
-public class SystemPollerTest {
-
- @Test
- public void testSystemPoller() {
- DummyService s = new DummyService(0, "id");
- List<VespaService> services = new ArrayList<>();
- services.add(s);
-
- SystemPoller poller = new SystemPoller(services, 60*5);
- assertThat(s.isAlive(), is(false));
- services.remove(0);
- poller.setServices(services);
- long n = poller.getPidJiffies(s);
- assertThat(n, is(0L));
- long[] memusage = poller.getMemoryUsage(s);
- assertThat(memusage[0], is(0L));
- assertThat(memusage[1], is(0L));
- }
-
- @Test
- public void testCPUJiffies() {
- String line = "cpu1 102180864 789 56766899 12800020140 1654757 0 0";
- CpuJiffies n = new CpuJiffies(line);
- assertThat(n.getCpuId(), is(1));
- assertThat(n.getTotalJiffies(), is(12960623449L));
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServiceTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServiceTest.java
deleted file mode 100644
index 13be98db23a..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServiceTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import ai.vespa.metricsproxy.TestUtil;
-import ai.vespa.metricsproxy.metric.Metrics;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Unknown
- */
-public class VespaServiceTest {
- private MockHttpServer service;
- private int csPort;
- private static final String response;
-
- static {
- response = TestUtil.getContents("metrics-state.json");
- HttpMetricFetcher.CONNECTION_TIMEOUT = 60000; // 60 secs in unit tests
- }
-
- @Before
- public void setupHTTPServer() {
- csPort = 18632; // see factory/doc/port-ranges.txt
- try {
- service = new MockHttpServer(csPort, response, HttpMetricFetcher.METRICS_PATH);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void testService() {
- VespaService service = new VespaService("qrserver", "container/qrserver.0");
- assertThat(service.getServiceName(), is("qrserver"));
- assertThat(service.getInstanceName(), is("qrserver"));
- assertThat(service.getPid(), is(-1));
- assertThat(service.getConfigId(), is("container/qrserver.0"));
-
-
- service = VespaService.create("qrserver2", "container/qrserver.0", -1);
- assertThat(service.getServiceName(), is("qrserver"));
- assertThat(service.getInstanceName(), is("qrserver2"));
- assertThat(service.getPid(), is(-1));
- assertThat(service.getConfigId(), is("container/qrserver.0"));
- }
-
- @Test
- // TODO: Make it possible to test this without running a HTTP server to create the response
- public void testMetricsFetching() {
- VespaService service = VespaService.create("service1", "id", csPort);
- Metrics metrics = service.getMetrics();
- assertThat(metrics.getMetric("queries.count").getValue().intValue(), is(28));
-
- // Shutdown server and check that no metrics are returned (should use empty metrics
- // when unable to fetch new metrics)
- shutdown();
-
- metrics = service.getMetrics();
- assertThat(metrics.size(), is(0));
- }
-
- @After
- public void shutdown() {
- this.service.close();
- }
-
-}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServicesTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServicesTest.java
deleted file mode 100644
index bd0b670ca35..00000000000
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/VespaServicesTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
- */
-
-package ai.vespa.metricsproxy.service;
-
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-
-import java.util.List;
-
-import static ai.vespa.metricsproxy.service.VespaServices.ALL_SERVICES;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * TODO: add more tests
- *
- * @author gjoranv
- */
-public class VespaServicesTest {
-
- @Test
- public void services_can_be_retrieved_from_monitoring_name() {
- List<VespaService> dummyServices = ImmutableList.of(
- new DummyService(0, "dummy/id/0"),
- new DummyService(1, "dummy/id/1"));
- VespaServices services = new VespaServices(dummyServices);
-
- assertThat(services.getMonitoringServices("vespa.dummy").size(), is(2));
- }
-
- @Test
- public void all_services_can_be_retrieved_by_using_special_name() {
- List<VespaService> dummyServices = ImmutableList.of(
- new DummyService(0, "dummy/id/0"));
- VespaServices services = new VespaServices(dummyServices);
-
- assertThat(services.getMonitoringServices(ALL_SERVICES).size(), is(1));
- }
-
-}
diff --git a/metrics-proxy/src/test/resources/health-check-failed.response.json b/metrics-proxy/src/test/resources/health-check-failed.response.json
deleted file mode 100644
index e118f10ec5e..00000000000
--- a/metrics-proxy/src/test/resources/health-check-failed.response.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "status": {
- "code": "down",
- "message":"SOMETHING FAILED"
- },
- "metrics": []
- }
diff --git a/metrics-proxy/src/test/resources/health-check.response.json b/metrics-proxy/src/test/resources/health-check.response.json
deleted file mode 100644
index 8e3858ec5d8..00000000000
--- a/metrics-proxy/src/test/resources/health-check.response.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "status": {
- "code": "OK",
- "message":"WORKING"
- }
- } \ No newline at end of file
diff --git a/metrics-proxy/src/test/resources/metrics-container-state-multi-chain.json b/metrics-proxy/src/test/resources/metrics-container-state-multi-chain.json
deleted file mode 100644
index 76d0be50cca..00000000000
--- a/metrics-proxy/src/test/resources/metrics-container-state-multi-chain.json
+++ /dev/null
@@ -1,281 +0,0 @@
-{
- "metrics": {
- "snapshot": {
- "from": 1.383132197389E9,
- "to": 1.383132257389E9
- },
- "values": [
- {
- "name": "search_connections",
- "values": {
- "average": 3.459204315576534,
- "count": 1,
- "last": 3.459204315576534,
- "max": 3.459204315576534,
- "min": 3.459204315576534,
- "rate": 0.016666666666666666
- }
- },
- {
- "name": "active_queries",
- "values": {
- "average": 0,
- "count": 1,
- "last": 0,
- "max": 0,
- "min": 0,
- "rate": 0.016666666666666666
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumFailedResponseWrites",
- "values": {
- "count": 85,
- "rate": 1.4166666666666667
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumFailedResponses",
- "values": {
- "count": 8,
- "rate": 0.13333333333333333
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumConnections",
- "values": {
- "count": 1630,
- "rate": 27.166666666666668
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumSuccessfulResponses",
- "values": {
- "count": 1621,
- "rate": 27.016666666666666
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNetworkLatency",
- "values": {
- "average": 0.11715958713775308,
- "count": 20152,
- "last": 0,
- "max": 55,
- "min": 0,
- "rate": 335.8666666666667
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumSuccessfulResponseWrites",
- "values": {
- "count": 20152,
- "rate": 335.8666666666667
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverTotalSuccessfulResponseLatency",
- "values": {
- "average": 90.88401253918495,
- "count": 1595,
- "last": 80,
- "max": 233,
- "min": 0,
- "rate": 26.583333333333332
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverNumRequests",
- "values": {
- "count": 1633,
- "rate": 27.216666666666665
- }
- },
- {
- "dimensions": {
- "serverName": "qrs-server",
- "serverPort": "4080"
- },
- "name": "serverTotalFailedResponseLatency",
- "values": {
- "average": 0.75,
- "count": 8,
- "last": 1,
- "max": 1,
- "min": 0,
- "rate": 0.13333333333333333
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "query_latency",
- "values": {
- "average": 83.35949367088608,
- "count": 1580,
- "last": 61,
- "max": 224,
- "min": 12,
- "rate": 26.333333333333332
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "max_query_latency",
- "values": {
- "average": 83.35949367088608,
- "count": 1580,
- "last": 61,
- "max": 224,
- "min": 12,
- "rate": 26.333333333333332
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "peak_qps",
- "values": {
- "average": 25.87656434951563,
- "count": 6,
- "last": 23.681592039800993,
- "max": 29.7659845295212,
- "min": 23.681592039800993,
- "rate": 0.1
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "queries",
- "values": {
- "count": 1584,
- "rate": 26.4
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "mean_query_latency",
- "values": {
- "average": 83.35949367088608,
- "count": 1580,
- "last": 61,
- "max": 224,
- "min": 12,
- "rate": 26.333333333333332
- }
- },
- {
- "dimensions": {"chain": "asvBlendingResult"},
- "name": "hits_per_query",
- "values": {
- "average": 173.70126582278482,
- "count": 1580,
- "last": 175,
- "max": 175,
- "min": 5,
- "rate": 26.333333333333332
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "query_latency",
- "values": {
- "average": 39.40909090909091,
- "count": 22,
- "last": 26,
- "max": 174,
- "min": 13,
- "rate": 0.36666666666666664
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "max_query_latency",
- "values": {
- "average": 39.40909090909091,
- "count": 22,
- "last": 26,
- "max": 174,
- "min": 13,
- "rate": 0.36666666666666664
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "peak_qps",
- "values": {
- "average": 0.5890415170417276,
- "count": 3,
- "last": 0.40488561981240295,
- "max": 0.864528399757932,
- "min": 0.40488561981240295,
- "rate": 0.05
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "queries",
- "values": {
- "count": 22,
- "rate": 0.36666666666666664
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "mean_query_latency",
- "values": {
- "average": 39.40909090909091,
- "count": 22,
- "last": 26,
- "max": 174,
- "min": 13,
- "rate": 0.36666666666666664
- }
- },
- {
- "dimensions": {"chain": "blendingResult"},
- "name": "hits_per_query",
- "values": {
- "average": 47.5,
- "count": 22,
- "last": 28,
- "max": 176,
- "min": 5,
- "rate": 0.36666666666666664
- }
- }
- ]
- },
- "status": {"code": "up"},
- "time": 1383132269767
-} \ No newline at end of file
diff --git a/metrics-proxy/src/test/resources/metrics-state.json b/metrics-proxy/src/test/resources/metrics-state.json
deleted file mode 100644
index b7773e5fb8b..00000000000
--- a/metrics-proxy/src/test/resources/metrics-state.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-"status" : {
- "code" : "up",
- "message" : "Everything ok here"
-},
-"metrics" : {
- "snapshot" : {
- "from" : 1334134640.089,
- "to" : 1334134700.088
- },
- "values" : [
- {
- "name" : "queries",
- "description" : "Number of queries executed during snapshot interval",
- "values" : {
- "count" : 28,
- "rate" : 0.4667
- },
- "dimensions" : {
- "searcherid" : "x"
- }
- },
- {
- "name" : "query_hits",
- "description" : "Number of documents matched per query during snapshot interval",
- "values" : {
- "count" : 28,
- "rate" : 0.4667,
- "average" : 128.3,
- "min" : 0,
- "max" : 10000,
- "sum" : 3584,
- "median" : 124.0,
- "std_deviation": 5.43
- },
- "dimensions" : {
- "searcherid" : "x"
- }
- }
- ]
-}
-}
diff --git a/metrics-proxy/src/test/resources/metrics-storage-simple.json b/metrics-proxy/src/test/resources/metrics-storage-simple.json
deleted file mode 100644
index 00715b52046..00000000000
--- a/metrics-proxy/src/test/resources/metrics-storage-simple.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "status" : {
- "code" : "up",
- "message": "All good"
- },
-
- "metrics" : {
- "snapshot" : {
- "from": 1335523285,
- "to": 1335525685
- },
- "values": [
-
- {
- "name" : "foo",
- "values" : {
- "count" : 1
- },
- "dimensions" : {
- "foo": "bar",
- "bar" : "foo"
- }
- },
-
- {
- "name" : "bar",
- "values" : {
- "count" : 2
- },
- "dimensions" : {
- "d0": "d0val",
- "d1" : "d1val"
- }
- }
- ]
-
- }
-}
diff --git a/metrics-proxy/src/test/resources/rpc-json-output-check.json b/metrics-proxy/src/test/resources/rpc-json-output-check.json
deleted file mode 100644
index 701a06d82b2..00000000000
--- a/metrics-proxy/src/test/resources/rpc-json-output-check.json
+++ /dev/null
@@ -1 +0,0 @@
-{"metrics":[{"status_code":1,"application":"test-system.qrserver","dimensions":{"metrictype":"health","instance":"qrserver"},"status_msg":"SOMETHING FAILED"}]} \ No newline at end of file
diff --git a/metrics-proxy/src/test/resources/yamas-array-no-routing.json b/metrics-proxy/src/test/resources/yamas-array-no-routing.json
deleted file mode 100644
index 8f21e8253b9..00000000000
--- a/metrics-proxy/src/test/resources/yamas-array-no-routing.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "status_code" : 0,
- "timestamp" : 1400047900,
- "application" : "vespa.searchnode",
- "metrics" : {
- "cpu" : 55.5555555555555,
- "memory_virt" : 22222222222,
- "memory_rss" : 5555555555
- },
- "dimensions" : {
- "applicationName" : "app",
- "tenantName" : "tenant",
- "metrictype" : "system",
- "instance" : "searchnode",
- "applicationInstance" : "default",
- "clustername" : "cluster"
- },
- "status_msg" : "Data collected successfully"
-}
diff --git a/metrics-proxy/src/test/resources/yamas-array.json b/metrics-proxy/src/test/resources/yamas-array.json
deleted file mode 100644
index c9293623b25..00000000000
--- a/metrics-proxy/src/test/resources/yamas-array.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "status_code" : 0,
- "timestamp" : 1400047900,
- "application" : "vespa.searchnode",
- "metrics" : {
- "cpu" : 55.5555555555555,
- "memory_virt" : 22222222222,
- "memory_rss" : 5555555555
- },
- "dimensions" : {
- "applicationName" : "app",
- "tenantName" : "tenant",
- "metrictype" : "system",
- "instance" : "searchnode",
- "applicationInstance" : "default",
- "clustername" : "cluster"
- },
- "routing" : {
- "yamas" : {
- "namespaces" : [
- "Vespa"
- ]
- }
- },
- "status_msg" : "Data collected successfully"
-}
diff --git a/metrics/src/tests/CMakeLists.txt b/metrics/src/tests/CMakeLists.txt
index 2c69b83d4b7..11d59e63c30 100644
--- a/metrics/src/tests/CMakeLists.txt
+++ b/metrics/src/tests/CMakeLists.txt
@@ -5,6 +5,8 @@
vespa_add_executable(metrics_gtest_runner_app TEST
SOURCES
countmetrictest.cpp
+ loadmetrictest.cpp
+ summetrictest.cpp
gtest_runner.cpp
DEPENDS
metrics
@@ -23,9 +25,7 @@ vespa_add_executable(metrics_testrunner_app TEST
testrunner.cpp
valuemetrictest.cpp
metricsettest.cpp
- summetrictest.cpp
metricmanagertest.cpp
- loadmetrictest.cpp
snapshottest.cpp
stresstest.cpp
metrictest.cpp
diff --git a/metrics/src/tests/loadmetrictest.cpp b/metrics/src/tests/loadmetrictest.cpp
index 5cbec2c4fae..b89628f4476 100644
--- a/metrics/src/tests/loadmetrictest.cpp
+++ b/metrics/src/tests/loadmetrictest.cpp
@@ -3,7 +3,7 @@
#include <vespa/metrics/valuemetric.h>
#include <vespa/metrics/loadmetric.hpp>
#include <vespa/metrics/summetric.hpp>
-#include <vespa/vdstestlib/cppunit/macros.h>
+#include <vespa/vespalib/gtest/gtest.h>
namespace metrics {
@@ -20,30 +20,11 @@ struct LoadTypeSetImpl : public LoadTypeSet {
const LoadType& lt(LoadTypeSet::operator[](i));
if (lt.getName() == name) return lt;
}
- CPPUNIT_FAIL("No load type with name " + name);
- return operator[](0); // Should never get here
+ abort();
}
};
-struct LoadMetricTest : public CppUnit::TestFixture {
- void testNormalUsage();
- void testClone(Metric::CopyType);
- void testInactiveCopy() { testClone(Metric::INACTIVE); }
- void testActiveCopy() { testClone(Metric::CLONE); }
- void testAdding();
-
- CPPUNIT_TEST_SUITE(LoadMetricTest);
- CPPUNIT_TEST(testNormalUsage);
- CPPUNIT_TEST(testActiveCopy);
- CPPUNIT_TEST(testInactiveCopy);
- CPPUNIT_TEST(testAdding);
- CPPUNIT_TEST_SUITE_END();
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION(LoadMetricTest);
-
-void
-LoadMetricTest::testNormalUsage()
+TEST(LoadMetricTest, test_normal_usage)
{
LoadTypeSetImpl loadTypes;
loadTypes.add(32, "foo").add(1000, "bar");
@@ -76,7 +57,7 @@ namespace {
}
void
-LoadMetricTest::testClone(Metric::CopyType copyType)
+test_clone(Metric::CopyType copyType)
{
LoadTypeSetImpl loadTypes;
loadTypes.add(32, "foo").add(1000, "bar");
@@ -87,7 +68,7 @@ LoadMetricTest::testClone(Metric::CopyType copyType)
std::vector<Metric::UP> ownerList;
MetricSet::UP copy(dynamic_cast<MetricSet*>(top.clone(ownerList, copyType, 0, true)));
- CPPUNIT_ASSERT(copy.get());
+ ASSERT_TRUE(copy.get());
std::string expected =
"top:\n"
@@ -101,12 +82,21 @@ LoadMetricTest::testClone(Metric::CopyType copyType)
" bar:\n"
" tack average=0 last=0 count=0 total=0";
- CPPUNIT_ASSERT_EQUAL(expected, std::string(top.toString(true)));
- CPPUNIT_ASSERT_EQUAL(expected, std::string(copy->toString(true)));
+ EXPECT_EQ(expected, std::string(top.toString(true)));
+ EXPECT_EQ(expected, std::string(copy->toString(true)));
}
-void
-LoadMetricTest::testAdding()
+TEST(LoadMetricTest, test_inactive_copy)
+{
+ test_clone(Metric::INACTIVE);
+}
+
+TEST(LoadMetricTest, test_active_copy)
+{
+ test_clone(Metric::CLONE);
+}
+
+TEST(LoadMetricTest, test_adding)
{
LoadTypeSetImpl loadTypes;
loadTypes.add(32, "foo").add(1000, "bar");
@@ -118,7 +108,7 @@ LoadMetricTest::testAdding()
std::vector<Metric::UP> ownerList;
MetricSet::UP copy(dynamic_cast<MetricSet*>(
top.clone(ownerList, Metric::INACTIVE, 0, false)));
- CPPUNIT_ASSERT(copy.get());
+ ASSERT_TRUE(copy.get());
top.reset();
@@ -132,8 +122,7 @@ LoadMetricTest::testAdding()
" foo:\n"
" tack average=5 last=5 min=5 max=5 count=1 total=5";
- CPPUNIT_ASSERT_EQUAL(expected, std::string(copy->toString(true)));
-
+ EXPECT_EQ(expected, std::string(copy->toString(true)));
}
-} // documentapi
+}
diff --git a/metrics/src/tests/summetrictest.cpp b/metrics/src/tests/summetrictest.cpp
index 32c5ee2c309..e3d58659daf 100644
--- a/metrics/src/tests/summetrictest.cpp
+++ b/metrics/src/tests/summetrictest.cpp
@@ -1,29 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/metrics/metrics.h>
-#include <vespa/vdstestlib/cppunit/macros.h>
+#include <vespa/vespalib/gtest/gtest.h>
namespace metrics {
-struct SumMetricTest : public CppUnit::TestFixture {
- void testLongCountMetric();
- void testAverageMetric();
- void testMetricSet();
- void testRemove();
- void testStartValue();
-
- CPPUNIT_TEST_SUITE(SumMetricTest);
- CPPUNIT_TEST(testLongCountMetric);
- CPPUNIT_TEST(testAverageMetric);
- CPPUNIT_TEST(testMetricSet);
- CPPUNIT_TEST(testRemove);
- CPPUNIT_TEST(testStartValue);
- CPPUNIT_TEST_SUITE_END();
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION(SumMetricTest);
-
-void
-SumMetricTest::testLongCountMetric()
+TEST(SumMetricTest, test_long_count_metric)
{
MetricSet parent("parent", {}, "");
SumMetric<LongCountMetric> sum("foo", {}, "foodesc", &parent);
@@ -40,12 +21,12 @@ SumMetricTest::testLongCountMetric()
// Verify XML output. Should be in register order.
std::string expected("foo count=10");
- CPPUNIT_ASSERT_EQUAL(expected, sum.toString());
- CPPUNIT_ASSERT_EQUAL(int64_t(10), sum.getLongValue("value"));
+ EXPECT_EQ(expected, sum.toString());
+ EXPECT_EQ(int64_t(10), sum.getLongValue("value"));
}
-void
-SumMetricTest::testAverageMetric() {
+TEST(SumMetricTest, test_average_metric)
+{
MetricSet parent("parent", {}, "");
SumMetric<LongAverageMetric> sum("foo", {}, "foodesc", &parent);
@@ -61,14 +42,14 @@ SumMetricTest::testAverageMetric() {
// Verify XML output. Should be in register order.
std::string expected("foo average=5 last=7 min=3 max=7 count=2 total=10");
- CPPUNIT_ASSERT_EQUAL(expected, sum.toString());
- CPPUNIT_ASSERT_EQUAL(int64_t(5), sum.getLongValue("value"));
- CPPUNIT_ASSERT_EQUAL(int64_t(3), sum.getLongValue("min"));
- CPPUNIT_ASSERT_EQUAL(int64_t(7), sum.getLongValue("max"));
+ EXPECT_EQ(expected, sum.toString());
+ EXPECT_EQ(int64_t(5), sum.getLongValue("value"));
+ EXPECT_EQ(int64_t(3), sum.getLongValue("min"));
+ EXPECT_EQ(int64_t(7), sum.getLongValue("max"));
}
-void
-SumMetricTest::testMetricSet() {
+TEST(SumMetricTest, test_metric_set)
+{
MetricSet parent("parent", {}, "");
SumMetric<MetricSet> sum("foo", {}, "bar", &parent);
@@ -82,23 +63,22 @@ SumMetricTest::testMetricSet() {
sum.addMetricToSum(set1);
sum.addMetricToSum(set2);
- // Give them some values
+ // Give them some values
v1.addValue(3);
v2.addValue(7);
v3.inc(2);
v4.inc();
- // Verify XML output. Should be in register order.
+ // Verify XML output. Should be in register order.
std::string expected("'\n"
"foo:\n"
" c average=3 last=3 min=3 max=3 count=1 total=3\n"
" e count=2'"
);
- CPPUNIT_ASSERT_EQUAL(expected, "'\n" + sum.toString() + "'");
+ EXPECT_EQ(expected, "'\n" + sum.toString() + "'");
}
-void
-SumMetricTest::testRemove()
+TEST(SumMetricTest, test_remove)
{
MetricSet parent("parent", {}, "");
SumMetric<LongCountMetric> sum("foo", {}, "foodesc", &parent);
@@ -116,13 +96,12 @@ SumMetricTest::testRemove()
v2.inc(7);
v3.inc(10);
- CPPUNIT_ASSERT_EQUAL(int64_t(20), sum.getLongValue("value"));
+ EXPECT_EQ(int64_t(20), sum.getLongValue("value"));
sum.removeMetricFromSum(v2);
- CPPUNIT_ASSERT_EQUAL(int64_t(13), sum.getLongValue("value"));
+ EXPECT_EQ(int64_t(13), sum.getLongValue("value"));
}
-void
-SumMetricTest::testStartValue()
+TEST(SumMetricTest, test_start_value)
{
MetricSnapshot snapshot("active");
SumMetric<LongValueMetric> sum("foo", {}, "foodesc",
@@ -132,7 +111,7 @@ SumMetricTest::testStartValue()
sum.setStartValue(start);
// without children
- CPPUNIT_ASSERT_EQUAL(int64_t(50), sum.getLongValue("value"));
+ EXPECT_EQ(int64_t(50), sum.getLongValue("value"));
MetricSnapshot copy("copy");
copy.recreateSnapshot(snapshot.getMetrics(), true);
@@ -143,7 +122,7 @@ SumMetricTest::testStartValue()
value.set(10);
// with children
- CPPUNIT_ASSERT_EQUAL(int64_t(60), sum.getLongValue("value"));
+ EXPECT_EQ(int64_t(60), sum.getLongValue("value"));
}
-} // metrics
+}
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/GraphImporter.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/GraphImporter.java
index a469e666d93..419bc7ddf28 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/GraphImporter.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/GraphImporter.java
@@ -20,6 +20,7 @@ import com.yahoo.tensor.functions.ScalarFunctions;
import onnx.Onnx;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -33,8 +34,8 @@ class GraphImporter {
private static IntermediateOperation mapOperation(Onnx.NodeProto node,
List<IntermediateOperation> inputs,
IntermediateGraph graph) {
- String nodeName = node.getName();
String modelName = graph.name();
+ String nodeName = getNodeName(node);
switch (node.getOpType().toLowerCase()) {
case "abs": return new Map(modelName, nodeName, inputs, ScalarFunctions.abs());
@@ -74,7 +75,7 @@ class GraphImporter {
case "tanh": return new Map(modelName, nodeName, inputs, ScalarFunctions.tanh());
}
- IntermediateOperation op = new NoOp(modelName, node.getName(), inputs);
+ IntermediateOperation op = new NoOp(modelName, nodeName, inputs);
op.warning("Operation '" + node.getOpType() + "' is currently not implemented");
return op;
}
@@ -199,18 +200,47 @@ class GraphImporter {
}
private static Onnx.NodeProto getNodeFromGraph(String nodeName, Onnx.GraphProto graph) {
- boolean hasPortNumber = nodeName.contains(":");
+ Optional<Onnx.NodeProto> node;
+ if (nodeName.contains(":")) {
+ node = getNodeFromGraphOutputs(nodeName, graph);
+ } else {
+ node = getNodeFromGraphNames(nodeName, graph);
+ if (node.isEmpty()) {
+ node = getNodeFromGraphOutputs(nodeName, graph);
+ }
+ }
+ return node.orElseThrow(() -> new IllegalArgumentException("Node '" + nodeName + "' not found in ONNX graph"));
+ }
+
+ private static Optional<Onnx.NodeProto> getNodeFromGraphOutputs(String nodeName, Onnx.GraphProto graph) {
for (Onnx.NodeProto node : graph.getNodeList()) {
- if (hasPortNumber) {
- for (String outputName : node.getOutputList()) {
- if (outputName.equals(nodeName)) {
- return node;
- }
+ for (String outputName : node.getOutputList()) {
+ if (outputName.equals(nodeName)) {
+ return Optional.of(node);
}
- } else if (node.getName().equals(nodeName)) {
- return node;
}
}
- throw new IllegalArgumentException("Node '" + nodeName + "' not found in ONNX graph");
+ return Optional.empty();
+ }
+
+ private static Optional<Onnx.NodeProto> getNodeFromGraphNames(String nodeName, Onnx.GraphProto graph) {
+ for (Onnx.NodeProto node : graph.getNodeList()) {
+ if (node.getName().equals(nodeName)) {
+ return Optional.of(node);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private static String getNodeName(Onnx.NodeProto node) {
+ String nodeName = node.getName();
+ if (nodeName.length() > 0)
+ return nodeName;
+ if (node.getOutputCount() == 1)
+ return node.getOutput(0);
+ throw new IllegalArgumentException("Unable to find a suitable name for node '" + node.toString() + "'. " +
+ "Either no explicit name given or no single output name.");
}
+
+
}
diff --git a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/SimpleImportTestCase.java b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/SimpleImportTestCase.java
new file mode 100644
index 00000000000..d1dea730da5
--- /dev/null
+++ b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/SimpleImportTestCase.java
@@ -0,0 +1,41 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package ai.vespa.rankingexpression.importer.onnx;
+
+import ai.vespa.rankingexpression.importer.ImportedModel;
+import com.yahoo.searchlib.rankingexpression.evaluation.MapContext;
+import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.tensor.TensorType;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author lesters
+ */
+public class SimpleImportTestCase {
+
+ @Test
+ public void testSimpleOnnxModelImport() {
+ ImportedModel model = new OnnxImporter().importModel("test", "src/test/models/onnx/simple/simple.onnx");
+
+ MapContext context = new MapContext();
+ context.put("query_tensor", new TensorValue(Tensor.Builder.of(TensorType.fromSpec("tensor(d0[1],d1[4])")).
+ cell(0.1, 0, 0).
+ cell(0.2, 0, 1).
+ cell(0.3, 0, 2).
+ cell(0.4, 0, 3).build()));
+ context.put("attribute_tensor", new TensorValue(Tensor.Builder.of(TensorType.fromSpec("tensor(d0[4],d1[1])")).
+ cell(0.1, 0, 0).
+ cell(0.2, 1, 0).
+ cell(0.3, 2, 0).
+ cell(0.4, 3, 0).build()));
+ context.put("bias_tensor", new TensorValue(Tensor.Builder.of(TensorType.fromSpec("tensor(d0[1],d1[1])")).
+ cell(1.0, 0, 0).build()));
+
+ Tensor result = model.expressions().get("output").evaluate(context).asTensor();
+ assertEquals(result, Tensor.from("tensor(d0[1],d1[1]):{{d0:0,d1:0}:1.3}"));
+ }
+
+}
diff --git a/model-integration/src/test/models/onnx/simple/simple.onnx b/model-integration/src/test/models/onnx/simple/simple.onnx
new file mode 100644
index 00000000000..1c746c90efa
--- /dev/null
+++ b/model-integration/src/test/models/onnx/simple/simple.onnx
@@ -0,0 +1,23 @@
+ simple.py:ã
+0
+ query_tensor
+attribute_tensormatmul"MatMul
+"
+matmul
+ bias_tensoroutput"addsimple_scoringZ
+ query_tensor
+ 
+
+Z"
+attribute_tensor
+ 
+
+Z
+ bias_tensor
+ 
+
+b
+output
+ 
+
+B
diff --git a/model-integration/src/test/models/onnx/simple/simple.py b/model-integration/src/test/models/onnx/simple/simple.py
new file mode 100755
index 00000000000..4471ed812b8
--- /dev/null
+++ b/model-integration/src/test/models/onnx/simple/simple.py
@@ -0,0 +1,32 @@
+import onnx
+from onnx import helper, TensorProto
+
+QUERY_TENSOR = helper.make_tensor_value_info('query_tensor', TensorProto.FLOAT, [1, 4])
+ATTRIBUTE_TENSOR = helper.make_tensor_value_info('attribute_tensor', TensorProto.FLOAT, [4, 1])
+BIAS_TENSOR = helper.make_tensor_value_info('bias_tensor', TensorProto.FLOAT, [1, 1])
+OUTPUT = helper.make_tensor_value_info('output', TensorProto.FLOAT, [1, 1])
+
+nodes = [
+ helper.make_node(
+ 'MatMul',
+ ['query_tensor', 'attribute_tensor'],
+ ['matmul'],
+ ),
+ helper.make_node(
+ 'add',
+ ['matmul', 'bias_tensor'],
+ ['output'],
+ ),
+]
+graph_def = helper.make_graph(
+ nodes,
+ 'simple_scoring',
+ [
+ QUERY_TENSOR,
+ ATTRIBUTE_TENSOR,
+ BIAS_TENSOR,
+ ],
+ [OUTPUT],
+)
+model_def = helper.make_model(graph_def, producer_name='simple.py')
+onnx.save(model_def, 'simple.onnx')
diff --git a/pom.xml b/pom.xml
index 8fc9cb21db8..c17b7d4a101 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,7 +96,6 @@
<module>messagebus-disc</module>
<module>messagebus</module>
<module>metrics</module>
- <module>metrics-proxy</module>
<module>model-evaluation</module>
<module>model-integration</module>
<module>node-repository</module>
diff --git a/searchlib/src/tests/diskindex/fieldwriter/fieldwriter_test.cpp b/searchlib/src/tests/diskindex/fieldwriter/fieldwriter_test.cpp
index b091ad569fa..46ee5e15757 100644
--- a/searchlib/src/tests/diskindex/fieldwriter/fieldwriter_test.cpp
+++ b/searchlib/src/tests/diskindex/fieldwriter/fieldwriter_test.cpp
@@ -299,11 +299,10 @@ writeField(FakeWordSet &wordSet,
ostate.open();
unsigned int wordNum = 1;
- for (unsigned int wc = 0; wc < wordSet._words.size(); ++wc) {
- for (unsigned int wi = 0; wi < wordSet._words[wc].size(); ++wi) {
- FakeWord &fw = *wordSet._words[wc][wi];
+ for (const auto& words : wordSet.words()) {
+ for (const auto& word : words) {
ostate._fieldWriter->newWord(makeWordString(wordNum));
- fw.dump(*ostate._fieldWriter, false);
+ word->dump(*ostate._fieldWriter, false);
++wordNum;
}
}
@@ -349,15 +348,12 @@ readField(FakeWordSet &wordSet,
TermFieldMatchData mdfield1;
unsigned int wordNum = 1;
- for (unsigned int wc = 0; wc < wordSet._words.size(); ++wc) {
- for (unsigned int wi = 0; wi < wordSet._words[wc].size(); ++wi) {
- FakeWord &fw = *wordSet._words[wc][wi];
-
+ for (const auto& words : wordSet.words()) {
+ for (const auto& word : words) {
TermFieldMatchDataArray tfmda;
tfmda.add(&mdfield1);
- fw.validate(*istate._fieldReader, wordNum,
- tfmda, verbose);
+ word->validate(*istate._fieldReader, wordNum, tfmda, verbose);
++wordNum;
}
}
@@ -425,10 +421,8 @@ randReadField(FakeWordSet &wordSet,
for (int loop = 0; loop < 1; ++loop) {
unsigned int wordNum = 1;
- for (unsigned int wc = 0; wc < wordSet._words.size(); ++wc) {
- for (unsigned int wi = 0; wi < wordSet._words[wc].size(); ++wi) {
- FakeWord &fw = *wordSet._words[wc][wi];
-
+ for (const auto& words : wordSet.words()) {
+ for (const auto& word : words) {
PostingListOffsetAndCounts offsetAndCounts;
uint64_t checkWordNum;
dictFile->lookup(makeWordString(wordNum),
@@ -456,12 +450,12 @@ randReadField(FakeWordSet &wordSet,
sb(handle.createIterator(counts, tfmda));
// LOG(info, "loop=%d, wordNum=%u", loop, wordNum);
- fw.validate(sb.get(), tfmda, verbose);
- fw.validate(sb.get(), tfmda, 19, verbose);
- fw.validate(sb.get(), tfmda, 99, verbose);
- fw.validate(sb.get(), tfmda, 799, verbose);
- fw.validate(sb.get(), tfmda, 6399, verbose);
- fw.validate(sb.get(), tfmda, 11999, verbose);
+ word->validate(sb.get(), tfmda, verbose);
+ word->validate(sb.get(), tfmda, 19, verbose);
+ word->validate(sb.get(), tfmda, 99, verbose);
+ word->validate(sb.get(), tfmda, 799, verbose);
+ word->validate(sb.get(), tfmda, 6399, verbose);
+ word->validate(sb.get(), tfmda, 11999, verbose);
++wordNum;
}
}
diff --git a/searchlib/src/tests/features/prod_features.cpp b/searchlib/src/tests/features/prod_features.cpp
index f08cb0855af..1d9433c739f 100644
--- a/searchlib/src/tests/features/prod_features.cpp
+++ b/searchlib/src/tests/features/prod_features.cpp
@@ -1217,8 +1217,8 @@ Test::testDotProduct()
vespalib::Stash stash;
FeatureExecutor &exc = bp.createExecutor(ft.getQueryEnv(), stash);
// check that we have the optimized enum version
- dotproduct::wset::DotProductExecutor<dotproduct::wset::EnumVector, WeightedEnumContent> * myExc =
- dynamic_cast<dotproduct::wset::DotProductExecutor<dotproduct::wset::EnumVector, WeightedEnumContent> *>(&exc);
+ dotproduct::wset::DotProductExecutorByCopy<dotproduct::wset::EnumVector, WeightedEnumContent> * myExc =
+ dynamic_cast<dotproduct::wset::DotProductExecutorByCopy<dotproduct::wset::EnumVector, WeightedEnumContent> *>(&exc);
EXPECT_TRUE(myExc != nullptr);
EXPECT_EQUAL(1u, deps.output.size());
}
diff --git a/searchlib/src/tests/postinglistbm/CMakeLists.txt b/searchlib/src/tests/postinglistbm/CMakeLists.txt
index 2fb507dbd01..479f54e8531 100644
--- a/searchlib/src/tests/postinglistbm/CMakeLists.txt
+++ b/searchlib/src/tests/postinglistbm/CMakeLists.txt
@@ -1,4 +1,14 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchlib_posting_list_test_app TEST
+ SOURCES
+ posting_list_test.cpp
+ DEPENDS
+ searchlib_test
+ searchlib
+ gtest
+)
+vespa_add_test(NAME searchlib_posting_list_test_app NO_VALGRIND COMMAND searchlib_posting_list_test_app)
+
vespa_add_executable(searchlib_postinglistbm_app TEST
SOURCES
postinglistbm.cpp
@@ -7,4 +17,3 @@ vespa_add_executable(searchlib_postinglistbm_app TEST
searchlib_test
searchlib
)
-vespa_add_test(NAME searchlib_postinglistbm_app NO_VALGRIND COMMAND searchlib_postinglistbm_app -q -a)
diff --git a/searchlib/src/tests/postinglistbm/andstress.cpp b/searchlib/src/tests/postinglistbm/andstress.cpp
index 7152e2a3981..adca7892464 100644
--- a/searchlib/src/tests/postinglistbm/andstress.cpp
+++ b/searchlib/src/tests/postinglistbm/andstress.cpp
@@ -192,9 +192,9 @@ AndStressMaster::resetTasks()
_taskIdx = 0;
}
-static void
+void
makeSomePostings(FPFactory *postingFactory,
- std::vector<FakeWord *> &words,
+ const FakeWordSet::FakeWordVector &words,
std::vector<FakePosting::SP> &postings,
uint32_t stride,
bool validate,
@@ -234,9 +234,9 @@ AndStressMaster::makePostingsHelper(FPFactory *postingFactory,
tv.SetNow();
before = tv.Secs();
postingFactory->setup(_wordSet);
- for (size_t i = 0; i < _wordSet._words.size(); ++i)
+ for (size_t i = 0; i < _wordSet.words().size(); ++i)
makeSomePostings(postingFactory,
- _wordSet._words[i], _postings[i],
+ _wordSet.words()[i], _postings[i],
_stride,
validate,
verbose);
diff --git a/searchlib/src/tests/postinglistbm/posting_list_test.cpp b/searchlib/src/tests/postinglistbm/posting_list_test.cpp
new file mode 100644
index 00000000000..ad3410b8f92
--- /dev/null
+++ b/searchlib/src/tests/postinglistbm/posting_list_test.cpp
@@ -0,0 +1,173 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/test/fakedata/fake_match_loop.h>
+#include <vespa/searchlib/test/fakedata/fakeposting.h>
+#include <vespa/searchlib/test/fakedata/fakeword.h>
+#include <vespa/searchlib/test/fakedata/fakewordset.h>
+#include <vespa/searchlib/test/fakedata/fpfactory.h>
+#include <vespa/searchlib/util/rand48.h>
+#include <vespa/vespalib/gtest/gtest.h>
+#include <cinttypes>
+
+using search::fef::TermFieldMatchData;
+using search::fef::TermFieldMatchDataArray;
+using search::queryeval::SearchIterator;
+
+using namespace search::index;
+using namespace search::fakedata;
+
+using FakeWordUP = std::unique_ptr<FakeWord>;
+
+void
+validate_posting_list_for_word(const FakePosting& posting, const FakeWord& word)
+{
+ TermFieldMatchData md;
+ TermFieldMatchDataArray tfmda;
+ tfmda.add(&md);
+
+ std::unique_ptr<SearchIterator> iterator(posting.createIterator(tfmda));
+ if (posting.hasWordPositions()) {
+ word.validate(iterator.get(), tfmda, false);
+ } else {
+ word.validate(iterator.get(), false);
+ }
+}
+
+void
+test_fake(const std::string& posting_type,
+ const Schema& schema,
+ const FakeWord& word)
+{
+ std::unique_ptr<FPFactory> factory(getFPFactory(posting_type, schema));
+ std::vector<const FakeWord *> words;
+ words.push_back(&word);
+ factory->setup(words);
+ auto posting = factory->make(word);
+
+ printf("%s.bitsize=%d+%d+%d+%d+%d\n",
+ posting->getName().c_str(),
+ static_cast<int>(posting->bitSize()),
+ static_cast<int>(posting->l1SkipBitSize()),
+ static_cast<int>(posting->l2SkipBitSize()),
+ static_cast<int>(posting->l3SkipBitSize()),
+ static_cast<int>(posting->l4SkipBitSize()));
+
+ validate_posting_list_for_word(*posting, word);
+
+ uint64_t scan_time = 0;
+ uint64_t scan_unpack_time = 0;
+ int hits1 = FakeMatchLoop::single_posting_scan(*posting, word.getDocIdLimit(), scan_time);
+ int hits2 = FakeMatchLoop::single_posting_scan_with_unpack(*posting, word.getDocIdLimit(), scan_unpack_time);
+
+ printf("test_fake: '%s': hits1=%d, hits2=%d, scan_time=%" PRIu64 "(ns), scan_unpack_time=%" PRIu64 "(ns)\n",
+ posting->getName().c_str(), hits1, hits2, scan_time, scan_unpack_time);
+}
+
+void
+test_fake_pair(const std::string& posting_type, const Schema& schema,
+ const FakeWord& word1, const FakeWord& word2)
+{
+ std::unique_ptr<FPFactory> factory(getFPFactory(posting_type, schema));
+ std::vector<const FakeWord *> words;
+ words.push_back(&word1);
+ words.push_back(&word2);
+ factory->setup(words);
+ auto posting1 = factory->make(word1);
+ auto posting2 = factory->make(word2);
+
+ uint64_t scan_time = 0;
+ int hits = FakeMatchLoop::and_pair_posting_scan(*posting1, *posting2, word1.getDocIdLimit(), scan_time);
+ printf("test_fake_pair: '%s' AND '%s' => %d hits, scan_time=%" PRIu64 " (ns)\n",
+ posting1->getName().c_str(), posting2->getName().c_str(), hits, scan_time);
+}
+
+struct PostingListTest : public ::testing::Test {
+ uint32_t num_docs;
+ std::vector<std::string> posting_types;
+ FakeWordSet word_set;
+ FakeWordUP word1;
+ FakeWordUP word2;
+ FakeWordUP word3;
+ FakeWordUP word4;
+ FakeWordUP word5;
+ search::Rand48 rnd;
+
+ PostingListTest()
+ : num_docs(36000),
+ posting_types(getPostingTypes()),
+ word_set(),
+ word1(),
+ word2(),
+ word3(),
+ word4(),
+ word5(),
+ rnd()
+ {
+ rnd.srand48(32);
+ }
+
+ void setup(bool has_elements, bool has_element_weights) {
+ word_set.setupParams(has_elements, has_element_weights);
+
+ uint32_t w1_freq = 2;
+ uint32_t w2_freq = 1000;
+ uint32_t w3_freq = 10000;
+ uint32_t w4_freq = 19000;
+ uint32_t w5_freq = 5000;
+ uint32_t w4w5od = 1000;
+
+ word1 = std::make_unique<FakeWord>(num_docs, w1_freq, w1_freq / 2, "word1", rnd,
+ word_set.getFieldsParams(), word_set.getPackedIndex());
+
+ word2 = std::make_unique<FakeWord>(num_docs, w2_freq, w2_freq / 2, "word2", *word1, 4, rnd,
+ word_set.getFieldsParams(), word_set.getPackedIndex());
+
+ word3 = std::make_unique<FakeWord>(num_docs, w3_freq, w3_freq / 2, "word3", *word1, 10, rnd,
+ word_set.getFieldsParams(), word_set.getPackedIndex());
+
+ word4 = std::make_unique<FakeWord>(num_docs, w4_freq, w4_freq / 2, "word4", rnd,
+ word_set.getFieldsParams(), word_set.getPackedIndex());
+
+ word5 = std::make_unique<FakeWord>(num_docs, w5_freq, w5_freq / 2, "word5", *word4, w4w5od, rnd,
+ word_set.getFieldsParams(), word_set.getPackedIndex());
+
+ }
+
+ void run() {
+ for (const auto& type : posting_types) {
+ test_fake(type, word_set.getSchema(), *word1);
+ test_fake(type, word_set.getSchema(), *word2);
+ test_fake(type, word_set.getSchema(), *word3);
+ }
+
+ for (const auto& type : posting_types) {
+ test_fake_pair(type, word_set.getSchema(), *word1, *word3);
+ test_fake_pair(type, word_set.getSchema(), *word2, *word3);
+ }
+
+ for (const auto& type : posting_types) {
+ test_fake_pair(type, word_set.getSchema(), *word4, *word5);
+ }
+ }
+
+};
+
+TEST_F(PostingListTest, verify_posting_list_iterators_over_single_value_field)
+{
+ setup(false, false);
+ run();
+}
+
+TEST_F(PostingListTest, verify_posting_list_iterators_over_array_field)
+{
+ setup(true, false);
+ run();
+}
+
+TEST_F(PostingListTest, verify_posting_list_iterators_over_weighted_set_field)
+{
+ setup(true, true);
+ run();
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/postinglistbm/postinglistbm.cpp b/searchlib/src/tests/postinglistbm/postinglistbm.cpp
index 02fbe4878ba..0a6f99ede11 100644
--- a/searchlib/src/tests/postinglistbm/postinglistbm.cpp
+++ b/searchlib/src/tests/postinglistbm/postinglistbm.cpp
@@ -24,21 +24,16 @@ using search::queryeval::SearchIterator;
using namespace search::index;
using namespace search::fakedata;
-// needed to resolve external symbol from httpd.h on AIX
-void FastS_block_usr2() {}
-
-
namespace postinglistbm {
class PostingListBM : public FastOS_Application {
private:
- bool _verbose;
uint32_t _numDocs;
uint32_t _commonDocFreq;
uint32_t _numWordsPerClass;
std::vector<std::string> _postingTypes;
uint32_t _loops;
- unsigned int _skipCommonPairsRate;
+ uint32_t _skipCommonPairsRate;
FakeWordSet _wordSet;
uint32_t _stride;
bool _unpack;
@@ -46,12 +41,6 @@ private:
public:
search::Rand48 _rnd;
-private:
- void usage();
- void badPostingType(const std::string &postingType);
- void testFake(const std::string &postingType,
- const Schema &schema,
- const FakeWord &word);
public:
PostingListBM();
~PostingListBM();
@@ -59,23 +48,23 @@ public:
};
void
-PostingListBM::usage()
+usage()
{
- printf("postinglistbm "
+ printf("Usage: postinglistbm "
"[-C <skipCommonPairsRate>] "
- "[-a] "
+ "[-T {string, array, weightedSet}] "
"[-c <commonDoqFreq>] "
"[-d <numDocs>] "
"[-l <numLoops>] "
"[-s <stride>] "
"[-t <postingType>] "
"[-u] "
- "[-q] "
- "[-v]\n");
+ "[-w <numWordsPerClass>] "
+ "[-q]\n");
}
void
-PostingListBM::badPostingType(const std::string &postingType)
+badPostingType(const std::string &postingType)
{
printf("Bad posting list type: %s\n", postingType.c_str());
printf("Supported types: ");
@@ -93,8 +82,7 @@ PostingListBM::badPostingType(const std::string &postingType)
}
PostingListBM::PostingListBM()
- : _verbose(false),
- _numDocs(10000000),
+ : _numDocs(10000000),
_commonDocFreq(50000),
_numWordsPerClass(100),
_postingTypes(),
@@ -109,93 +97,19 @@ PostingListBM::PostingListBM()
PostingListBM::~PostingListBM() = default;
-void
-validate_posting_for_word(const FakePosting& posting, const FakeWord& word, bool verbose)
-{
- TermFieldMatchData md;
- TermFieldMatchDataArray tfmda;
- tfmda.add(&md);
-
- std::unique_ptr<SearchIterator> iterator(posting.createIterator(tfmda));
- if (posting.hasWordPositions()) {
- word.validate(iterator.get(), tfmda, verbose);
- } else {
- word.validate(iterator.get(), verbose);
- }
-}
-
-void
-PostingListBM::testFake(const std::string &postingType,
- const Schema &schema,
- const FakeWord &word)
-{
- auto posting_factory = getFPFactory(postingType, schema);
- std::vector<const FakeWord *> words;
- words.push_back(&word);
- posting_factory->setup(words);
- auto posting = posting_factory->make(word);
-
- printf("%s.bitsize=%d+%d+%d+%d+%d\n",
- posting->getName().c_str(),
- static_cast<int>(posting->bitSize()),
- static_cast<int>(posting->l1SkipBitSize()),
- static_cast<int>(posting->l2SkipBitSize()),
- static_cast<int>(posting->l3SkipBitSize()),
- static_cast<int>(posting->l4SkipBitSize()));
-
- validate_posting_for_word(*posting, word, _verbose);
-
- uint64_t scanTime = 0;
- uint64_t scanUnpackTime = 0;
- int hits1 = FakeMatchLoop::single_posting_scan(*posting, word.getDocIdLimit(), scanTime);
- int hits2 = FakeMatchLoop::single_posting_scan_with_unpack(*posting, word.getDocIdLimit(), scanUnpackTime);
-
- printf("testFake '%s' hits1=%d, hits2=%d, scanTime=%" PRIu64
- ", scanUnpackTime=%" PRIu64 "\n",
- posting->getName().c_str(),
- hits1, hits2, scanTime, scanUnpackTime);
-}
-
-void
-testFakePair(const std::string &postingType,
- const Schema &schema,
- bool unpack,
- const FakeWord &fw1, const FakeWord &fw2)
-{
- std::unique_ptr<FPFactory> ff(getFPFactory(postingType, schema));
- std::vector<const FakeWord *> v;
- v.push_back(&fw1);
- v.push_back(&fw2);
- ff->setup(v);
- FakePosting::SP f1(ff->make(fw1));
- FakePosting::SP f2(ff->make(fw2));
-
- uint64_t scanUnpackTime = 0;
- int hits = unpack ?
- FakeMatchLoop::and_pair_posting_scan_with_unpack(*f1, *f2, fw1.getDocIdLimit(), scanUnpackTime) :
- FakeMatchLoop::and_pair_posting_scan(*f1, *f2, fw1.getDocIdLimit(), scanUnpackTime);
- printf("Fakepair %s AND %s => %d hits, %" PRIu64 " cycles\n",
- f1->getName().c_str(),
- f2->getName().c_str(),
- hits,
- scanUnpackTime);
-}
-
int
PostingListBM::Main()
{
int argi;
char c;
const char *optArg;
- bool doandstress;
- doandstress = false;
argi = 1;
bool hasElements = false;
bool hasElementWeights = false;
bool quick = false;
- while ((c = GetOpt("C:ac:d:l:s:t:uvw:T:q", optArg, argi)) != -1) {
+ while ((c = GetOpt("C:c:d:l:s:t:uw:T:q", optArg, argi)) != -1) {
switch(c) {
case 'C':
_skipCommonPairsRate = atoi(optArg);
@@ -215,9 +129,6 @@ PostingListBM::Main()
return 1;
}
break;
- case 'a':
- doandstress = true;
- break;
case 'c':
_commonDocFreq = atoi(optArg);
break;
@@ -248,9 +159,6 @@ PostingListBM::Main()
case 'u':
_unpack = true;
break;
- case 'v':
- _verbose = true;
- break;
case 'w':
_numWordsPerClass = atoi(optArg);
break;
@@ -273,65 +181,24 @@ PostingListBM::Main()
_wordSet.setupParams(hasElements, hasElementWeights);
- uint32_t w1dfreq = 10;
- uint32_t w4dfreq = 790000;
- uint32_t w5dfreq = 290000;
- uint32_t w4w5od = 100000;
uint32_t numTasks = 40000;
if (quick) {
- w1dfreq = 2;
- w4dfreq = 19000;
- w5dfreq = 5000;
- w4w5od = 1000;
numTasks = 40;
}
-
- FakeWord word1(_numDocs, w1dfreq, w1dfreq / 2, "word1", _rnd,
- _wordSet.getFieldsParams(), _wordSet.getPackedIndex());
- FakeWord word2(_numDocs, 1000, 500, "word2", word1, 4, _rnd,
- _wordSet.getFieldsParams(), _wordSet.getPackedIndex());
- FakeWord word3(_numDocs, _commonDocFreq, _commonDocFreq / 2,
- "word3", word1, 10, _rnd,
- _wordSet.getFieldsParams(), _wordSet.getPackedIndex());
- FakeWord word4(_numDocs, w4dfreq, w4dfreq / 2,
- "word4", _rnd,
- _wordSet.getFieldsParams(), _wordSet.getPackedIndex());
- FakeWord word5(_numDocs, w5dfreq, w5dfreq / 2,
- "word5", word4, w4w5od, _rnd,
- _wordSet.getFieldsParams(), _wordSet.getPackedIndex());
-
if (_postingTypes.empty()) {
_postingTypes = getPostingTypes();
}
- for (const auto& type : _postingTypes) {
- testFake(type, _wordSet.getSchema(), word1);
- testFake(type, _wordSet.getSchema(), word2);
- testFake(type, _wordSet.getSchema(), word3);
- }
-
- for (const auto& type : _postingTypes) {
- testFakePair(type, _wordSet.getSchema(), false, word1, word3);
- testFakePair(type, _wordSet.getSchema(), false, word2, word3);
- }
-
- for (const auto& type : _postingTypes) {
- testFakePair(type, _wordSet.getSchema(), false, word4, word5);
- }
+ _wordSet.setupWords(_rnd, _numDocs, _commonDocFreq, _numWordsPerClass);
- if (doandstress) {
- _wordSet.setupWords(_rnd, _numDocs, _commonDocFreq, _numWordsPerClass);
- }
- if (doandstress) {
- AndStress andstress;
- andstress.run(_rnd, _wordSet,
- _numDocs, _commonDocFreq, _postingTypes, _loops,
- _skipCommonPairsRate,
- numTasks,
- _stride,
- _unpack);
- }
+ AndStress andstress;
+ andstress.run(_rnd, _wordSet,
+ _numDocs, _commonDocFreq, _postingTypes, _loops,
+ _skipCommonPairsRate,
+ numTasks,
+ _stride,
+ _unpack);
return 0;
}
diff --git a/searchlib/src/vespa/searchlib/engine/proto_rpc_adapter.cpp b/searchlib/src/vespa/searchlib/engine/proto_rpc_adapter.cpp
index 652ef2e0889..4e637fe19cf 100644
--- a/searchlib/src/vespa/searchlib/engine/proto_rpc_adapter.cpp
+++ b/searchlib/src/vespa/searchlib/engine/proto_rpc_adapter.cpp
@@ -46,6 +46,23 @@ void encode_message(const MSG &src, FRT_Values &dst) {
dst.AddData(compressed.getData(), compressed.getDataLen());
}
+void encode_search_reply(const ProtoSearchReply &src, FRT_Values &dst) {
+ using vespalib::compression::compress;
+ auto output = src.SerializeAsString();
+ if (src.grouping_blob().empty()) {
+ dst.AddInt8(CompressionConfig::Type::NONE);
+ dst.AddInt32(output.size());
+ dst.AddData(output.data(), output.size());
+ } else {
+ ConstBufferRef buf(output.data(), output.size());
+ DataBuffer compressed(output.data(), output.size());
+ CompressionConfig::Type type = compress(get_compression_config(), buf, compressed, true);
+ dst.AddInt8(type);
+ dst.AddInt32(buf.size());
+ dst.AddData(compressed.getData(), compressed.getDataLen());
+ }
+}
+
template <typename MSG>
bool decode_message(const FRT_Values &src, MSG &dst) {
using vespalib::compression::decompress;
@@ -88,7 +105,7 @@ struct SearchCompletionHandler : SearchClient {
void searchDone(SearchReply::UP reply) override {
ProtoSearchReply msg;
ProtoConverter::search_reply_to_proto(*reply, msg);
- encode_message(msg, *req.GetReturn());
+ encode_search_reply(msg, *req.GetReturn());
req.Return();
}
};
diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
index 1dcd3e35580..55a550837e1 100644
--- a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp
@@ -30,27 +30,38 @@ VectorBase<DimensionVType, DimensionHType, ComponentType, HashMapComparator>::Ve
template <typename DimensionVType, typename DimensionHType, typename ComponentType, typename HashMapComparator>
VectorBase<DimensionVType, DimensionHType, ComponentType, HashMapComparator>::~VectorBase() = default;
-template <typename V>
-V copyAndSync(const V & v) {
- V tmp(v);
- tmp.syncMap();
- return tmp;
+template <typename DimensionVType, typename DimensionHType, typename ComponentType, typename HashMapComparator>
+VectorBase<DimensionVType, DimensionHType, ComponentType, HashMapComparator> &
+VectorBase<DimensionVType, DimensionHType, ComponentType, HashMapComparator>::syncMap() {
+ Converter<DimensionVType, DimensionHType> conv;
+ _dimMap.clear();
+ _dimMap.resize(_vector.size()*2);
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ _dimMap.insert(std::make_pair(conv.convert(_vector[i].first), _vector[i].second));
+ }
+ return *this;
}
+template VectorBase<int64_t, int64_t, double> & VectorBase<int64_t, int64_t, double>::syncMap();
+
+
template <typename Vector, typename Buffer>
-DotProductExecutor<Vector, Buffer>::DotProductExecutor(const IAttributeVector * attribute, const Vector & queryVector) :
+DotProductExecutorByCopy<Vector, Buffer>::DotProductExecutorByCopy(const IAttributeVector * attribute, Vector queryVector) :
FeatureExecutor(),
_attribute(attribute),
- _queryVector(copyAndSync(queryVector)),
- _end(_queryVector.getDimMap().end()),
+ _queryVector(std::move(queryVector)),
+ _end(_queryVector.syncMap().getDimMap().end()),
_buffer()
{
_buffer.allocate(_attribute->getMaxValueCount());
}
template <typename Vector, typename Buffer>
+DotProductExecutorByCopy<Vector, Buffer>::~DotProductExecutorByCopy() = default;
+
+template <typename Vector, typename Buffer>
void
-DotProductExecutor<Vector, Buffer>::execute(uint32_t docId)
+DotProductExecutorByCopy<Vector, Buffer>::execute(uint32_t docId)
{
feature_t val = 0;
if (!_queryVector.getDimMap().empty()) {
@@ -69,6 +80,50 @@ StringVector::StringVector() = default;
StringVector::~StringVector() = default;
+template <typename BaseType>
+DotProductExecutorBase<BaseType>::DotProductExecutorBase(V queryVector)
+ : FeatureExecutor(),
+ _queryVector(std::move(queryVector)),
+ _end(_queryVector.syncMap().getDimMap().end())
+{
+}
+
+template <typename BaseType>
+DotProductExecutorBase<BaseType>::~DotProductExecutorBase() = default;
+
+template <typename BaseType>
+void DotProductExecutorBase<BaseType>::execute(uint32_t docId) {
+ feature_t val = 0;
+ if (!_queryVector.getDimMap().empty()) {
+ const AT * values(nullptr);
+ uint32_t sz = getAttributeValues(docId, values);
+ for (size_t i = 0; i < sz; ++i) {
+ typename V::HashMap::const_iterator itr = _queryVector.getDimMap().find(values[i].value());
+ if (itr != _end) {
+ val += values[i].weight() * itr->second;
+ }
+ }
+ }
+ outputs().set_number(0, val);
+}
+
+template <typename A>
+DotProductExecutor<A>::DotProductExecutor(const A * attribute, V queryVector) :
+ DotProductExecutorBase<typename A::BaseType>(std::move(queryVector)),
+ _attribute(attribute)
+{
+}
+
+template <typename A>
+DotProductExecutor<A>::~DotProductExecutor() = default;
+
+template <typename A>
+size_t
+DotProductExecutor<A>::getAttributeValues(uint32_t docId, const AT * & values)
+{
+ return _attribute->getRawValues(docId, values);
+}
+
}
namespace dotproduct::array {
@@ -507,9 +562,8 @@ createFromObject(const IAttributeVector * attribute, const fef::Anything & objec
return stash.create<SingleZeroValueExecutor>();
}
-FeatureExecutor * createTypedArrayExecutor(const IAttributeVector * attribute,
- const Property & prop,
- vespalib::Stash & stash) {
+FeatureExecutor *
+createTypedArrayExecutor(const IAttributeVector * attribute, const Property & prop, vespalib::Stash & stash) {
if (!attribute->isImported()) {
switch (attribute->getBasicType()) {
case BasicType::INT32:
@@ -542,29 +596,55 @@ FeatureExecutor * createTypedArrayExecutor(const IAttributeVector * attribute,
return nullptr;
}
-FeatureExecutor * createTypedWsetExecutor(const IAttributeVector * attribute,
- const Property & prop,
- vespalib::Stash & stash) {
- if (attribute->isStringType()) {
- if (attribute->hasEnum()) {
- dotproduct::wset::EnumVector vector(attribute);
- WeightedSetParser::parse(prop.get(), vector);
- return &stash.create<dotproduct::wset::DotProductExecutor<dotproduct::wset::EnumVector, WeightedEnumContent>>(attribute, vector);
- } else {
- dotproduct::wset::StringVector vector;
- WeightedSetParser::parse(prop.get(), vector);
- return &stash.create<dotproduct::wset::DotProductExecutor<dotproduct::wset::StringVector, WeightedConstCharContent>>(attribute, vector);
+template <typename A, typename V>
+FeatureExecutor *
+createForDirectWSetImpl(const IAttributeVector * attribute, V vector, vespalib::Stash & stash)
+{
+ using namespace dotproduct::wset;
+ using T = typename A::BaseType;
+ const A * iattr = dynamic_cast<const A *>(attribute);
+ if (!attribute->isImported() && (iattr != nullptr) && supportsGetRawValues(*iattr)) {
+ using VT = multivalue::WeightedValue<T>;
+ using ExactA = MultiValueNumericAttribute<A, VT>;
+
+ const ExactA * exactA = dynamic_cast<const ExactA *>(iattr);
+ if (exactA != nullptr) {
+ return &stash.create<DotProductExecutor<ExactA>>(exactA, std::move(vector));
}
- } else if (attribute->isIntegerType()) {
- if (attribute->hasEnum()) {
- dotproduct::wset::EnumVector vector(attribute);
- WeightedSetParser::parse(prop.get(), vector);
- return &stash.create<dotproduct::wset::DotProductExecutor<dotproduct::wset::EnumVector, WeightedEnumContent>>(attribute, vector);
+ return &stash.create<DotProductExecutor<A>>(iattr, std::move(vector));
+ }
+ return &stash.create<DotProductExecutorByCopy<IntegerVectorT<T>, WeightedIntegerContent>>(attribute, std::move(vector));
+}
- } else {
- dotproduct::wset::IntegerVector vector;
+template <typename T>
+FeatureExecutor *
+createForDirectIntegerWSet(const IAttributeVector * attribute, const Property & prop, vespalib::Stash & stash)
+{
+ using namespace dotproduct::wset;
+ IntegerVectorT<T> vector;
+ WeightedSetParser::parse(prop.get(), vector);
+ return createForDirectWSetImpl<IntegerAttributeTemplate<T>>(attribute, std::move(vector), stash);
+}
+
+
+FeatureExecutor *
+createTypedWsetExecutor(const IAttributeVector * attribute, const Property & prop, vespalib::Stash & stash) {
+ using namespace dotproduct::wset;
+ if (attribute->hasEnum()) {
+ EnumVector vector(attribute);
+ WeightedSetParser::parse(prop.get(), vector);
+ return &stash.create<DotProductExecutorByCopy<EnumVector, WeightedEnumContent>>(attribute, std::move(vector));
+ } else {
+ if (attribute->isStringType()) {
+ StringVector vector;
WeightedSetParser::parse(prop.get(), vector);
- return &stash.create<dotproduct::wset::DotProductExecutor<dotproduct::wset::IntegerVector, WeightedIntegerContent>>(attribute, vector);
+ return &stash.create<DotProductExecutorByCopy<StringVector, WeightedConstCharContent>>(attribute, std::move(vector));
+ } else if (attribute->isIntegerType()) {
+ if (attribute->getBasicType() == BasicType::INT32) {
+ return createForDirectIntegerWSet<int32_t>(attribute, prop, stash);
+ } else if (attribute->getBasicType() == BasicType::INT64) {
+ return createForDirectIntegerWSet<int64_t>(attribute, prop, stash);
+ }
}
}
return nullptr;
diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.h b/searchlib/src/vespa/searchlib/features/dotproductfeature.h
index 089066cb5f6..38dcdd54929 100644
--- a/searchlib/src/vespa/searchlib/features/dotproductfeature.h
+++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.h
@@ -55,71 +55,106 @@ protected:
Vector _vector;
HashMap _dimMap; // dimension -> component
public:
+ VectorBase(VectorBase && rhs) = default;
+ VectorBase & operator = (VectorBase && rhs) = default;
~VectorBase();
const Vector & getVector() const { return _vector; }
- void syncMap() {
- Converter<DimensionVType, DimensionHType> conv;
- _dimMap.clear();
- _dimMap.resize(_vector.size()*2);
- for (size_t i = 0; i < _vector.size(); ++i) {
- _dimMap.insert(std::make_pair(conv.convert(_vector[i].first), _vector[i].second));
- }
- }
+ VectorBase & syncMap();
const HashMap & getDimMap() const { return _dimMap; }
};
/**
* Represents a vector where the dimensions are integers.
**/
-class IntegerVector : public VectorBase<int64_t, int64_t, feature_t> {
+template<typename T>
+class IntegerVectorT : public VectorBase<T, T, feature_t> {
public:
void insert(vespalib::stringref label, vespalib::stringref value) {
- _vector.push_back(std::make_pair(util::strToNum<int64_t>(label), util::strToNum<feature_t>(value)));
+ this->_vector.emplace_back(util::strToNum<T>(label), util::strToNum<feature_t>(value));
}
};
+using IntegerVector = IntegerVectorT<int64_t>;
+
/**
* Represents a vector where the dimensions are string values.
**/
class StringVector : public VectorBase<vespalib::string, const char *, feature_t, ConstCharComparator> {
public:
StringVector();
+ StringVector(StringVector &&) = default;
+ StringVector & operator = (StringVector &&) = default;
~StringVector();
void insert(vespalib::stringref label, vespalib::stringref value) {
- _vector.push_back(std::make_pair(label, util::strToNum<feature_t>(value)));
+ _vector.emplace_back(label, util::strToNum<feature_t>(value));
}
};
/**
* Represents a vector where the dimensions are enum values for strings.
**/
-class EnumVector : public VectorBase<search::attribute::EnumHandle, search::attribute::EnumHandle, feature_t> {
+class EnumVector : public VectorBase<attribute::EnumHandle, attribute::EnumHandle, feature_t> {
private:
const attribute::IAttributeVector * _attribute;
public:
EnumVector(const attribute::IAttributeVector * attribute) : _attribute(attribute) {}
void insert(vespalib::stringref label, vespalib::stringref value) {
- search::attribute::EnumHandle e;
+ attribute::EnumHandle e;
if (_attribute->findEnum(label.data(), e)) {
- _vector.push_back(std::make_pair(e, util::strToNum<feature_t>(value)));
+ _vector.emplace_back(e, util::strToNum<feature_t>(value));
}
}
};
+/**
+ * Common base for handling execution for all wset dot product executors.
+ * Only cares about the underlying value type, not the concrete type of the
+ * attribute vector itself.
+ */
+template <typename BaseType>
+class DotProductExecutorBase : public fef::FeatureExecutor {
+public:
+ using AT = multivalue::WeightedValue<BaseType>;
+ using V = VectorBase<BaseType, BaseType, feature_t>;
+private:
+ V _queryVector;
+ const typename V::HashMap::const_iterator _end;
+ virtual size_t getAttributeValues(uint32_t docid, const AT * & count) = 0;
+public:
+ DotProductExecutorBase(V queryVector);
+ ~DotProductExecutorBase() override;
+ void execute(uint32_t docId) override;
+};
+
+template <typename A>
+class DotProductExecutor final : public DotProductExecutorBase<typename A::BaseType> {
+public:
+ using AT = typename DotProductExecutorBase<typename A::BaseType>::AT;
+ using V = typename DotProductExecutorBase<typename A::BaseType>::V;
+protected:
+ const A * _attribute;
+private:
+ size_t getAttributeValues(uint32_t docid, const AT * & count) override;
+public:
+ DotProductExecutor(const A * attribute, V queryVector);
+ ~DotProductExecutor();
+};
+
/**
* Implements the executor for the dotproduct feature.
*/
template <typename Vector, typename Buffer>
-class DotProductExecutor : public fef::FeatureExecutor {
+class DotProductExecutorByCopy final : public fef::FeatureExecutor {
private:
const attribute::IAttributeVector * _attribute;
- const Vector _queryVector;
+ Vector _queryVector;
const typename Vector::HashMap::const_iterator _end;
Buffer _buffer;
public:
- DotProductExecutor(const attribute::IAttributeVector * attribute, const Vector & queryVector);
+ DotProductExecutorByCopy(const attribute::IAttributeVector * attribute, Vector queryVector);
+ ~DotProductExecutorByCopy() override;
void execute(uint32_t docId) override;
};
@@ -143,7 +178,7 @@ private:
virtual size_t getAttributeValues(uint32_t docid, const AT * & count) = 0;
public:
DotProductExecutorBase(const V & queryVector);
- ~DotProductExecutorBase();
+ ~DotProductExecutorBase() override;
void execute(uint32_t docId) final override;
};
diff --git a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
index 9d383e5a03a..eb73cef1f4c 100644
--- a/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
+++ b/searchlib/src/vespa/searchlib/features/internal_max_reduce_prod_join_feature.cpp
@@ -32,15 +32,15 @@ protected:
IntegerVector _queryVector;
public:
- RawExecutor(const IAttributeVector *attribute, const IntegerVector &queryVector);
+ RawExecutor(const IAttributeVector *attribute, IntegerVector queryVector);
void execute(uint32_t docId) override;
};
template <typename BaseType>
-RawExecutor<BaseType>::RawExecutor(const IAttributeVector *attribute, const IntegerVector &queryVector) :
+RawExecutor<BaseType>::RawExecutor(const IAttributeVector *attribute, IntegerVector queryVector) :
FeatureExecutor(),
_attribute(attribute),
- _queryVector(queryVector)
+ _queryVector(std::move(queryVector))
{
_queryVector.syncMap();
}
@@ -81,13 +81,13 @@ private:
WeightedIntegerContent _buffer;
public:
- BufferedExecutor(const IAttributeVector *attribute, const IntegerVector &queryVector);
+ BufferedExecutor(const IAttributeVector *attribute, IntegerVector queryVector);
void execute(uint32_t docId) override;
};
template <typename BaseType>
-BufferedExecutor<BaseType>::BufferedExecutor(const IAttributeVector *attribute, const IntegerVector &queryVector) :
- RawExecutor<BaseType>(attribute, queryVector),
+BufferedExecutor<BaseType>::BufferedExecutor(const IAttributeVector *attribute, IntegerVector queryVector) :
+ RawExecutor<BaseType>(attribute, std::move(queryVector)),
_buffer()
{
}
@@ -109,20 +109,17 @@ InternalMaxReduceProdJoinBlueprint::InternalMaxReduceProdJoinBlueprint() :
{
}
-InternalMaxReduceProdJoinBlueprint::~InternalMaxReduceProdJoinBlueprint()
-{
-}
+InternalMaxReduceProdJoinBlueprint::~InternalMaxReduceProdJoinBlueprint() = default;
void
-InternalMaxReduceProdJoinBlueprint::visitDumpFeatures(const IIndexEnvironment &,
- IDumpFeatureVisitor &) const
+InternalMaxReduceProdJoinBlueprint::visitDumpFeatures(const IIndexEnvironment &, IDumpFeatureVisitor &) const
{
}
Blueprint::UP
InternalMaxReduceProdJoinBlueprint::createInstance() const
{
- return Blueprint::UP(new InternalMaxReduceProdJoinBlueprint());
+ return std::make_unique<InternalMaxReduceProdJoinBlueprint>();
}
ParameterDescriptions
@@ -155,7 +152,7 @@ bool supportsGetRawValues(const A &attr) noexcept {
template <typename BaseType>
FeatureExecutor &
-selectTypedExecutor(const IAttributeVector *attribute, const IntegerVector &vector, vespalib::Stash &stash)
+selectTypedExecutor(const IAttributeVector *attribute, IntegerVector vector, vespalib::Stash &stash)
{
if (!attribute->isImported()) {
using A = IntegerAttributeTemplate<BaseType>;
@@ -166,22 +163,22 @@ selectTypedExecutor(const IAttributeVector *attribute, const IntegerVector &vect
if (supportsGetRawValues(*iattr)) {
const ExactA *exactA = dynamic_cast<const ExactA *>(iattr);
if (exactA != nullptr) {
- return stash.create<RawExecutor<BaseType>>(attribute, vector);
+ return stash.create<RawExecutor<BaseType>>(attribute, std::move(vector));
}
}
}
- return stash.create<BufferedExecutor<BaseType>>(attribute, vector);
+ return stash.create<BufferedExecutor<BaseType>>(attribute, std::move(vector));
}
FeatureExecutor &
-selectExecutor(const IAttributeVector *attribute, const IntegerVector &vector, vespalib::Stash &stash)
+selectExecutor(const IAttributeVector *attribute, IntegerVector vector, vespalib::Stash &stash)
{
if (attribute->getCollectionType() == CollectionType::ARRAY) {
switch (attribute->getBasicType()) {
case BasicType::INT32:
- return selectTypedExecutor<int32_t>(attribute, vector, stash);
+ return selectTypedExecutor<int32_t>(attribute, std::move(vector), stash);
case BasicType::INT64:
- return selectTypedExecutor<int64_t>(attribute, vector, stash);
+ return selectTypedExecutor<int64_t>(attribute, std::move(vector), stash);
default:
break;
}
@@ -207,7 +204,7 @@ InternalMaxReduceProdJoinBlueprint::createExecutor(const IQueryEnvironment &env,
IntegerVector vector;
WeightedSetParser::parse(prop.get(), vector);
if (!vector.getVector().empty()) {
- return selectExecutor(attribute, vector, stash);
+ return selectExecutor(attribute, std::move(vector), stash);
}
}
return stash.create<SingleZeroValueExecutor>();
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fakeword.h b/searchlib/src/vespa/searchlib/test/fakedata/fakeword.h
index 619c1760797..345d69c29f6 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fakeword.h
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fakeword.h
@@ -17,7 +17,7 @@ namespace search {
namespace fakedata {
-/*
+/**
* General representation of a faked word, containing all features used
* by any of the candidate posting list formats.
*/
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.cpp b/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.cpp
index 1f8ea9fb2ea..5c87bf88e9c 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.cpp
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.cpp
@@ -10,27 +10,23 @@ LOG_SETUP(".fakewordset");
namespace search::fakedata {
+using FakeWordVector = FakeWordSet::FakeWordVector;
using index::PostingListParams;
using index::SchemaUtil;
using index::schema::CollectionType;
using index::schema::DataType;
-static void
-clearFakeWordVector(std::vector<FakeWord *> &v)
-{
- for (unsigned int i = 0; i < v.size(); ++i)
- delete v[i];
- v.clear();
-}
-
+namespace {
-static void
-applyDocIdBiasToVector(std::vector<FakeWord *> &v, uint32_t docIdBias)
+void
+applyDocIdBiasToVector(FakeWordVector& words, uint32_t docIdBias)
{
- for (unsigned int i = 0; i < v.size(); ++i)
- v[i]->addDocIdBias(docIdBias);
+ for (auto& word : words) {
+ word->addDocIdBias(docIdBias);
+ }
}
+}
FakeWordSet::FakeWordSet()
: _words(NUM_WORDCLASSES),
@@ -40,7 +36,6 @@ FakeWordSet::FakeWordSet()
setupParams(false, false);
}
-
FakeWordSet::FakeWordSet(bool hasElements,
bool hasElementWeights)
: _words(NUM_WORDCLASSES),
@@ -50,12 +45,7 @@ FakeWordSet::FakeWordSet(bool hasElements,
setupParams(hasElements, hasElementWeights);
}
-
-FakeWordSet::~FakeWordSet()
-{
- dropWords();
-}
-
+FakeWordSet::~FakeWordSet() = default;
void
FakeWordSet::setupParams(bool hasElements,
@@ -83,7 +73,6 @@ FakeWordSet::setupParams(bool hasElements,
}
}
-
void
FakeWordSet::setupWords(search::Rand48 &rnd,
unsigned int numDocs,
@@ -93,7 +82,6 @@ FakeWordSet::setupWords(search::Rand48 &rnd,
std::string common = "common";
std::string medium = "medium";
std::string rare = "rare";
- FakeWord *fw;
FastOS_Time tv;
double before;
double after;
@@ -106,50 +94,42 @@ FakeWordSet::setupWords(search::Rand48 &rnd,
std::ostringstream vi;
vi << (i + 1);
- fw = new FakeWord(numDocs, commonDocFreq, commonDocFreq / 2,
- common + vi.str(), rnd,
- _fieldsParams[packedIndex],
- packedIndex);
- _words[COMMON_WORD].push_back(fw);
- fw = new FakeWord(numDocs, 1000, 500,
- medium + vi.str(), rnd,
- _fieldsParams[packedIndex],
- packedIndex);
- _words[MEDIUM_WORD].push_back(fw);
- fw = new FakeWord(numDocs, 10, 5,
- rare + vi.str(), rnd,
- _fieldsParams[packedIndex],
- packedIndex);
- _words[RARE_WORD].push_back(fw);
+ _words[COMMON_WORD].push_back(std::make_unique<FakeWord>(numDocs, commonDocFreq, commonDocFreq / 2,
+ common + vi.str(), rnd,
+ _fieldsParams[packedIndex],
+ packedIndex));
+
+ _words[MEDIUM_WORD].push_back(std::make_unique<FakeWord>(numDocs, 1000, 500,
+ medium + vi.str(), rnd,
+ _fieldsParams[packedIndex],
+ packedIndex));
+
+ _words[RARE_WORD].push_back(std::make_unique<FakeWord>(numDocs, 10, 5,
+ rare + vi.str(), rnd,
+ _fieldsParams[packedIndex],
+ packedIndex));
}
tv.SetNow();
after = tv.Secs();
LOG(info, "leave setupWords, elapsed %10.6f s", after - before);
}
-
-void
-FakeWordSet::dropWords()
-{
- for (unsigned int i = 0; i < _words.size(); ++i)
- clearFakeWordVector(_words[i]);
-}
-
-
int
-FakeWordSet::getNumWords()
+FakeWordSet::getNumWords() const
{
int ret = 0;
- for (unsigned int i = 0; i < _words.size(); ++i)
- ret += _words[i].size();
+ for (const auto& words : _words) {
+ ret += words.size();
+ }
return ret;
}
void
FakeWordSet::addDocIdBias(uint32_t docIdBias)
{
- for (unsigned int i = 0; i < _words.size(); ++i)
- applyDocIdBiasToVector(_words[i], docIdBias);
+ for (auto& words : _words) {
+ applyDocIdBiasToVector(words, docIdBias);
+ }
}
}
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.h b/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.h
index 754a592820c..0b7ee4db6fe 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.h
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fakewordset.h
@@ -5,25 +5,21 @@
#include <vespa/searchlib/bitcompression/compression.h>
#include <vespa/searchlib/bitcompression/posocccompression.h>
-namespace search
-{
-class Rand48;
-}
-
-namespace search
-{
+namespace search { class Rand48; }
-namespace fakedata
-{
+namespace search::fakedata {
class FakeWord;
-class FakeWordSet
-{
+/**
+ * Contains lists of fake words for 3 word classes categorized based on number of occurrences.
+ */
+class FakeWordSet {
public:
- typedef bitcompression::PosOccFieldsParams PosOccFieldsParams;
- typedef bitcompression::PosOccFieldParams PosOccFieldParams;
- typedef index::Schema Schema;
+ using PosOccFieldsParams = bitcompression::PosOccFieldsParams;
+ using Schema = index::Schema;
+ using FakeWordPtr = std::unique_ptr<FakeWord>;
+ using FakeWordVector = std::vector<FakeWordPtr>;
enum {
COMMON_WORD,
@@ -31,10 +27,13 @@ public:
RARE_WORD,
NUM_WORDCLASSES,
};
- std::vector<std::vector<FakeWord *> > _words;
+
+private:
+ std::vector<FakeWordVector> _words;
Schema _schema;
std::vector<PosOccFieldsParams> _fieldsParams;
+public:
FakeWordSet();
FakeWordSet(bool hasElements,
@@ -42,51 +41,36 @@ public:
~FakeWordSet();
- void
- setupParams(bool hasElements,
- bool hasElementWeights);
+ void setupParams(bool hasElements,
+ bool hasElementWeights);
- void
- setupWords(search::Rand48 &rnd,
- unsigned int numDocs,
- unsigned int commonDocFreq,
- unsigned int numWordsPerWordClass);
+ void setupWords(search::Rand48 &rnd,
+ unsigned int numDocs,
+ unsigned int commonDocFreq,
+ unsigned int numWordsPerWordClass);
- void
- dropWords();
+ const std::vector<FakeWordVector>& words() const { return _words; }
- int
- getNumWords();
+ int getNumWords() const;
- const PosOccFieldsParams &
- getFieldsParams() const
- {
+ const PosOccFieldsParams& getFieldsParams() const {
return _fieldsParams.back();
}
- uint32_t
- getPackedIndex() const
- {
+ uint32_t getPackedIndex() const {
return _fieldsParams.size() - 1;
}
- const std::vector<PosOccFieldsParams> &
- getAllFieldsParams() const
- {
+ const std::vector<PosOccFieldsParams>& getAllFieldsParams() const {
return _fieldsParams;
}
- const Schema &
- getSchema() const
- {
+ const Schema& getSchema() const {
return _schema;
}
- void
- addDocIdBias(uint32_t docIdBias);
+ void addDocIdBias(uint32_t docIdBias);
};
-} // namespace fakedata
-
-} // namespace search
+}
diff --git a/searchlib/src/vespa/searchlib/test/fakedata/fpfactory.cpp b/searchlib/src/vespa/searchlib/test/fakedata/fpfactory.cpp
index f10af286dd7..76bfeac759f 100644
--- a/searchlib/src/vespa/searchlib/test/fakedata/fpfactory.cpp
+++ b/searchlib/src/vespa/searchlib/test/fakedata/fpfactory.cpp
@@ -19,12 +19,9 @@ FPFactory::setup(const FakeWordSet &fws)
{
std::vector<const FakeWord *> v;
- for (uint32_t wc = 0; wc < fws._words.size(); ++wc) {
- std::vector<FakeWord *>::const_iterator fwi(fws._words[wc].begin());
- std::vector<FakeWord *>::const_iterator fwe(fws._words[wc].end());
- while (fwi != fwe) {
- v.push_back(*fwi);
- ++fwi;
+ for (const auto& words : fws.words()) {
+ for (const auto& word : words) {
+ v.push_back(word.get());
}
}
setup(v);
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
new file mode 100644
index 00000000000..b5822ff825e
--- /dev/null
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
@@ -0,0 +1,78 @@
+package ai.vespa.hosted.plugin;
+
+import ai.vespa.hosted.api.ControllerHttpClient;
+import com.yahoo.config.provision.ApplicationId;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Base class for hosted Vespa plugin mojos.
+ *
+ * @author jonmv
+ */
+public abstract class AbstractVespaMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "${project}", readonly = true)
+ protected MavenProject project;
+
+ @Parameter(property = "endpoint", defaultValue = "https://api.vespa.corp.yahoo.com:4443") // TODO jvenstad: Change default
+ protected String endpointUri;
+
+ @Parameter(property = "tenant")
+ protected String tenant;
+
+ @Parameter(property = "application")
+ protected String application;
+
+ @Parameter(property = "instance")
+ protected String instance;
+
+ @Parameter(property = "privateKeyFile", required = true)
+ protected String privateKeyFile;
+
+ @Parameter(property = "certificateFile")
+ protected String certificateFile;
+
+ // Fields set up as part of setup().
+ protected ApplicationId id;
+ protected ControllerHttpClient controller;
+
+ @Override
+ public final void execute() {
+ setup();
+ doExecute();
+ }
+
+ /** Override this in subclasses, instead of {@link #execute()}. */
+ protected abstract void doExecute();
+
+ protected void setup() {
+ tenant = firstNonBlank(tenant, project.getProperties().getProperty("tenant"));
+ application = firstNonBlank(application, project.getProperties().getProperty("application"));
+ instance = firstNonBlank(instance, project.getProperties().getProperty("instance"), "default");
+ id = ApplicationId.from(tenant, application, instance);
+
+ controller = certificateFile == null
+ ? ControllerHttpClient.withSignatureKey(URI.create(endpointUri), Paths.get(privateKeyFile), id)
+ : ControllerHttpClient.withKeyAndCertificate(URI.create(endpointUri), Paths.get(privateKeyFile), Paths.get(certificateFile));
+ }
+
+ protected String projectPathOf(String first, String... rest) {
+ return project.getBasedir().toPath().resolve(Path.of(first, rest)).toString();
+ }
+
+ /** Returns the first of the given strings which is non-null and non-blank, or throws IllegalArgumentException. */
+ protected static String firstNonBlank(String... values) {
+ for (String value : values)
+ if (value != null && ! value.isBlank())
+ return value;
+
+ throw new IllegalArgumentException("No valid value given");
+ }
+
+}
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/CompileVersionMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/CompileVersionMojo.java
new file mode 100644
index 00000000000..24e2c42d756
--- /dev/null
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/CompileVersionMojo.java
@@ -0,0 +1,18 @@
+package ai.vespa.hosted.plugin;
+
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Finds the Vespa version to compile against, for a hosted Vespa application.
+ *
+ * @author jonmv
+ */
+@Mojo(name = "compileVersion")
+public class CompileVersionMojo extends AbstractVespaMojo {
+
+ @Override
+ protected void doExecute() {
+ System.out.println(controller.compileVersion(id));
+ }
+
+}
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
new file mode 100644
index 00000000000..9a2c65ef86e
--- /dev/null
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
@@ -0,0 +1,58 @@
+package ai.vespa.hosted.plugin;
+
+import ai.vespa.hosted.api.Deployment;
+import com.yahoo.config.provision.zone.ZoneId;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+import java.nio.file.Paths;
+
+/**
+ * Deploys a Vespa application package to the hosted Vespa API.
+ *
+ * @author jonmv
+ */
+@Mojo(name = "deploy")
+public class DeployMojo extends AbstractVespaMojo {
+
+ @Parameter(property = "applicationZip")
+ private String applicationZip;
+
+ @Parameter(property = "vespaVersion")
+ private String vespaVersion;
+
+ @Parameter(property = "ignoreValidationErrors")
+ private String ignoreValidationErrors;
+
+ @Parameter(property = "environment")
+ private String environment;
+
+ @Parameter(property = "region")
+ private String region;
+
+ @Parameter(property = "repository")
+ private String repository;
+
+ @Parameter(property = "branch")
+ private String branch;
+
+ @Parameter(property = "commit")
+ private String commit;
+
+ @Parameter(property = "build")
+ private Long build;
+
+ @Override
+ protected void doExecute() {
+ Deployment deployment = build == null
+ ? Deployment.ofPackage(Paths.get(firstNonBlank(applicationZip, projectPathOf("target", "application.zip"))))
+ : Deployment.ofReference(repository, branch, commit, build);
+ if ("true".equalsIgnoreCase(ignoreValidationErrors)) deployment = deployment.ignoringValidationErrors();
+ if (vespaVersion != null) deployment = deployment.atVersion(vespaVersion);
+
+ ZoneId zone = environment == null || region == null ? controller.devZone() : ZoneId.from(environment, region);
+
+ System.out.println(controller.deploy(deployment, id, zone).json());
+ }
+
+}
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java
index 6f39c29963c..eda9ea9b799 100644
--- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java
@@ -1,17 +1,12 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.hosted.plugin;
-import ai.vespa.hosted.api.ControllerHttpClient;
import ai.vespa.hosted.api.Submission;
-import com.yahoo.config.provision.ApplicationId;
-import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
-import org.apache.maven.project.MavenProject;
-import java.net.URI;
-import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.OptionalLong;
/**
* Submits a Vespa application package and corresponding test jars to the hosted Vespa API.
@@ -19,22 +14,7 @@ import java.nio.file.Paths;
* @author jonmv
*/
@Mojo(name = "submit")
-public class SubmitMojo extends AbstractMojo {
-
- @Parameter(defaultValue = "${project}", readonly = true)
- private MavenProject project;
-
- @Parameter(property = "endpoint", defaultValue = "https://api.vespa.corp.yahoo.com:4443") // TODO jvenstad: Change default
- private String endpointUri;
-
- @Parameter(property = "tenant")
- private String tenant;
-
- @Parameter(property = "application")
- private String application;
-
- @Parameter(property = "instance")
- private String instance;
+public class SubmitMojo extends AbstractVespaMojo {
@Parameter(property = "applicationZip")
private String applicationZip;
@@ -42,12 +22,6 @@ public class SubmitMojo extends AbstractMojo {
@Parameter(property = "applicationTestZip")
private String applicationTestZip;
- @Parameter(property = "privateKeyFile", required = true)
- private String privateKeyFile;
-
- @Parameter(property = "certificateFile")
- private String certificateFile;
-
@Parameter(property = "authorEmail", required = true)
private String authorEmail;
@@ -60,39 +34,19 @@ public class SubmitMojo extends AbstractMojo {
@Parameter(property = "commit", defaultValue = "unknown")
private String commit;
- @Override
- public void execute() {
- setup();
- ApplicationId id = ApplicationId.from(tenant, application, instance);
- ControllerHttpClient controller = certificateFile == null
- ? ControllerHttpClient.withSignatureKey(URI.create(endpointUri), Paths.get(privateKeyFile), id)
- : ControllerHttpClient.withKeyAndCertificate(URI.create(endpointUri), Paths.get(privateKeyFile), Paths.get(certificateFile));
-
- Submission submission = new Submission(repository, branch, commit, authorEmail,
- Paths.get(applicationZip), Paths.get(applicationTestZip));
-
- System.out.println(controller.submit(submission, id.tenant(), id.application()));
- }
+ @Parameter(property = "projectId")
+ private Long projectId;
- private void setup() {
- tenant = firstNonBlank(tenant, project.getProperties().getProperty("tenant"));
- application = firstNonBlank(application, project.getProperties().getProperty("application"));
- instance = firstNonBlank(instance, project.getProperties().getProperty("instance"));
+ @Override
+ public void doExecute() {
applicationZip = firstNonBlank(applicationZip, projectPathOf("target", "application.zip"));
applicationTestZip = firstNonBlank(applicationTestZip, projectPathOf("target", "application-test.zip"));
- }
-
- private String projectPathOf(String first, String... rest) {
- return project.getBasedir().toPath().resolve(Path.of(first, rest)).toString();
- }
-
- /** Returns the first of the given strings which is non-null and non-blank, or throws IllegalArgumentException. */
- private static String firstNonBlank(String... values) {
- for (String value : values)
- if (value != null && ! value.isBlank())
- return value;
+ Submission submission = new Submission(repository, branch, commit, authorEmail,
+ Paths.get(applicationZip),
+ Paths.get(applicationTestZip),
+ projectId == null ? OptionalLong.empty() : OptionalLong.of(projectId));
- throw new IllegalArgumentException("No valid value given");
+ System.out.println(controller.submit(submission, id.tenant(), id.application()));
}
}