summaryrefslogtreecommitdiffstats
path: root/service-monitor
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@verizonmedia.com>2020-02-28 22:26:39 +0100
committerHåkon Hallingstad <hakon@verizonmedia.com>2020-02-28 22:26:39 +0100
commit7d515c403035aeef430507acb6a166b6cfdba9ee (patch)
tree0bf33cfd0430ecfb56916bb2fd049da197cf260c /service-monitor
parente4a06ace71acac5aa1e18176f7253649883fbaed (diff)
Add host indices to duper model
Diffstat (limited to 'service-monitor')
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java80
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java8
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java34
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java23
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java62
5 files changed, 157 insertions, 50 deletions
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
index aa0b9e81c44..9bfba36a2d3 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
@@ -2,16 +2,20 @@
package com.yahoo.vespa.service.duper;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.service.monitor.DuperModelListener;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TreeMap;
+import java.util.Set;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
/**
* A non-thread-safe mutable container of ApplicationInfo, also taking care of listeners on changes.
@@ -21,41 +25,83 @@ import java.util.logging.Logger;
public class DuperModel {
private static Logger logger = Logger.getLogger(DuperModel.class.getName());
- private final Map<ApplicationId, ApplicationInfo> applications = new TreeMap<>();
+ private final Map<ApplicationId, ApplicationInfo> applicationsById = new HashMap<>();
+ private final Map<HostName, ApplicationInfo> applicationsByHostname = new HashMap<>();
+ private final Map<ApplicationId, Set<HostName>> hostnamesById = new HashMap<>();
+
private final List<DuperModelListener> listeners = new ArrayList<>();
private boolean isComplete = false;
public void registerListener(DuperModelListener listener) {
- applications.values().forEach(listener::applicationActivated);
+ applicationsById.values().forEach(listener::applicationActivated);
listeners.add(listener);
}
- public void setCompleteness(boolean isComplete) { this.isComplete = isComplete; }
+ void setComplete() { this.isComplete = true; }
public boolean isComplete() { return isComplete; }
+ public int numberOfApplications() {
+ return applicationsById.size();
+ }
+
+ public int numberOfHosts() {
+ return applicationsByHostname.size();
+ }
+
public boolean contains(ApplicationId applicationId) {
- return applications.containsKey(applicationId);
+ return applicationsById.containsKey(applicationId);
+ }
+
+ public Optional<ApplicationInfo> getApplicationInfo(ApplicationId applicationId) {
+ return Optional.ofNullable(applicationsById.get(applicationId));
+ }
+
+ public Optional<ApplicationInfo> getApplicationInfo(HostName hostName) {
+ return Optional.ofNullable(applicationsByHostname.get(hostName));
+ }
+
+ public List<ApplicationInfo> getApplicationInfos() {
+ return List.copyOf(applicationsById.values());
}
public void add(ApplicationInfo applicationInfo) {
- applications.put(applicationInfo.getApplicationId(), applicationInfo);
- logger.log(LogLevel.DEBUG, "Added " + applicationInfo.getApplicationId());
+ ApplicationInfo oldApplicationInfo = applicationsById.put(applicationInfo.getApplicationId(), applicationInfo);
+
+ final String logPrefix;
+ if (oldApplicationInfo == null) {
+ logPrefix = isComplete ? "New application " : "Bootstrapped application ";
+ } else {
+ logPrefix = isComplete ? "Reactivated application " : "Rebootstrapped application ";
+ }
+ logger.log(LogLevel.INFO, logPrefix + applicationInfo.getApplicationId());
+
+ Set<HostName> oldHostnames = hostnamesById.get(applicationInfo.getApplicationId());
+ if (oldHostnames != null) {
+ oldHostnames.forEach(applicationsByHostname::remove);
+ }
+
+ Set<HostName> hostnames = applicationInfo.getModel().getHosts().stream()
+ .map(HostInfo::getHostname)
+ .map(HostName::from)
+ .collect(Collectors.toSet());
+
+ hostnamesById.put(applicationInfo.getApplicationId(), hostnames);
+ hostnames.forEach(hostname -> applicationsByHostname.put(hostname, applicationInfo));
+
listeners.forEach(listener -> listener.applicationActivated(applicationInfo));
}
public void remove(ApplicationId applicationId) {
- if (applications.remove(applicationId) != null) {
- logger.log(LogLevel.DEBUG, "Removed " + applicationId);
- listeners.forEach(listener -> listener.applicationRemoved(applicationId));
+ Set<HostName> hostnames = hostnamesById.remove(applicationId);
+ if (hostnames != null) {
+ hostnames.forEach(applicationsByHostname::remove);
}
- }
- public Optional<ApplicationInfo> getApplicationInfo(ApplicationId applicationId) {
- return Optional.ofNullable(applications.get(applicationId));
- }
+ ApplicationInfo application = applicationsById.remove(applicationId);
- public List<ApplicationInfo> getApplicationInfos() {
- logger.log(LogLevel.DEBUG, "Applications in duper model: " + applications.values().size());
- return List.copyOf(applications.values());
+ if (application != null || hostnames != null) {
+ logger.log(LogLevel.INFO, "Removed application " + applicationId);
+ listeners.forEach(listener -> listener.applicationRemoved(applicationId));
+ }
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
index 9c819d89fb2..9c93bc1d390 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
@@ -179,6 +179,12 @@ public class DuperModelManager implements DuperModelProvider, DuperModelInfraApi
}
}
+ public Optional<ApplicationInfo> getApplicationInfo(HostName hostname) {
+ synchronized (monitor) {
+ return duperModel.getApplicationInfo(hostname);
+ }
+ }
+
public List<ApplicationInfo> getApplicationInfos() {
synchronized (monitor) {
return duperModel.getApplicationInfos();
@@ -187,7 +193,7 @@ public class DuperModelManager implements DuperModelProvider, DuperModelInfraApi
private void maybeSetDuperModelAsComplete() {
if (superModelIsComplete && infraApplicationsIsComplete) {
- duperModel.setCompleteness(true);
+ duperModel.setComplete();
}
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
index 45a9d1c77c5..86bb892b162 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
@@ -12,7 +12,6 @@ import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -50,40 +49,25 @@ public class ModelGenerator {
.collect(Collectors.toSet());
}
- public Optional<ApplicationInstance> toApplication(List<ApplicationInfo> applicationInfos,
- HostName hostname,
- ServiceStatusProvider serviceStatusProvider) {
- for (var applicationInfo : applicationInfos) {
- var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
- if (generator.containsHostname(hostname)) {
- return Optional.of(generator.makeApplicationInstance(serviceStatusProvider));
- }
- }
-
- return Optional.empty();
- }
-
public ApplicationInstance toApplication(ApplicationInfo applicationInfo,
ServiceStatusProvider serviceStatusProvider) {
var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
return generator.makeApplicationInstance(serviceStatusProvider);
}
- public List<ServiceInstance> toServices(List<ApplicationInfo> applicationInfos,
+ public List<ServiceInstance> toServices(ApplicationInfo applicationInfo,
HostName hostname,
ServiceStatusProvider serviceStatusProvider) {
- for (var applicationInfo : applicationInfos) {
- var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
- ApplicationInstance applicationInstance = generator.makeApplicationInstanceLimitedTo(
- hostname, serviceStatusProvider);
+ var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
+ ApplicationInstance applicationInstance = generator.makeApplicationInstanceLimitedTo(
+ hostname, serviceStatusProvider);
- List<ServiceInstance> serviceInstances = applicationInstance.serviceClusters().stream()
- .flatMap(cluster -> cluster.serviceInstances().stream())
- .collect(Collectors.toList());
+ List<ServiceInstance> serviceInstances = applicationInstance.serviceClusters().stream()
+ .flatMap(cluster -> cluster.serviceInstances().stream())
+ .collect(Collectors.toList());
- if (serviceInstances.size() > 0) {
- return serviceInstances;
- }
+ if (serviceInstances.size() > 0) {
+ return serviceInstances;
}
return List.of();
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
index 641bc5ca7ff..d3d7c151a9b 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
@@ -56,8 +56,13 @@ public class ServiceModelProvider implements ServiceMonitor {
@Override
public Optional<ApplicationInstance> getApplication(HostName hostname) {
- // TODO(hakonhall): Lookup the applicationInfo from the hostname.
- return modelGenerator.toApplication(applicationInfos(), hostname, serviceStatusProvider);
+ Optional<ApplicationInfo> applicationInfo =
+ duperModelManager.getApplicationInfo(toConfigProvisionHostName(hostname));
+ if (applicationInfo.isEmpty()) {
+ return Optional.empty();
+ }
+
+ return Optional.of(modelGenerator.toApplication(applicationInfo.get(), serviceStatusProvider));
}
@Override
@@ -68,8 +73,13 @@ public class ServiceModelProvider implements ServiceMonitor {
@Override
public List<ServiceInstance> getServiceInstancesOn(HostName hostname) {
- // TODO(hakonhall): Lookup the applicationInfo from the hostname.
- return modelGenerator.toServices(applicationInfos(), hostname, serviceStatusProvider);
+ Optional<ApplicationInfo> applicationInfo =
+ duperModelManager.getApplicationInfo(toConfigProvisionHostName(hostname));
+ if (applicationInfo.isEmpty()) {
+ return List.of();
+ }
+
+ return modelGenerator.toServices(applicationInfo.get(), hostname, serviceStatusProvider);
}
@Override
@@ -85,4 +95,9 @@ public class ServiceModelProvider implements ServiceMonitor {
private List<ApplicationInfo> applicationInfos() {
return duperModelManager.getApplicationInfos();
}
+
+ /** The duper model uses HostName from config.provision, which is more natural than applicationmodel. */
+ private com.yahoo.config.provision.HostName toConfigProvisionHostName(HostName hostname) {
+ return com.yahoo.config.provision.HostName.from(hostname.s());
+ }
}
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
index dc90035be71..0cc108bae9e 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
@@ -2,12 +2,18 @@
package com.yahoo.vespa.service.duper;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.model.api.HostInfo;
+import com.yahoo.config.model.api.Model;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.service.monitor.DuperModelListener;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -23,32 +29,55 @@ import static org.mockito.Mockito.when;
*/
public class DuperModelTest {
private final DuperModel duperModel = new DuperModel();
- private final ApplicationInfo application1 = mock(ApplicationInfo.class);
+
private final ApplicationId id1 = ApplicationId.fromSerializedForm("tenant:app1:default");
+ private final ApplicationInfo application1 = mock(ApplicationInfo.class);
+ private final HostName hostname1_1 = HostName.from("hostname1-1");
+ private final HostName hostname1_2 = HostName.from("hostname1-2");
+
private final ApplicationId id2 = ApplicationId.fromSerializedForm("tenant:app2:default");
private final ApplicationInfo application2 = mock(ApplicationInfo.class);
+ private final HostName hostname2_1 = HostName.from("hostname2-1");
+
private final DuperModelListener listener1 = mock(DuperModelListener.class);
@Before
public void setUp() {
- when(application1.getApplicationId()).thenReturn(id1);
- when(application2.getApplicationId()).thenReturn(id2);
+ setUpApplication(id1, application1, hostname1_1, hostname1_2);
+ setUpApplication(id2, application2, hostname2_1);
+ }
+
+ private void setUpApplication(ApplicationId id, ApplicationInfo info, HostName... hostnames) {
+ when(info.getApplicationId()).thenReturn(id);
+
+ Model model = mock(Model.class);
+ when(info.getModel()).thenReturn(model);
+
+ List<HostInfo> hostInfos = Arrays.stream(hostnames)
+ .map(hostname -> new HostInfo(hostname.value(), List.of()))
+ .collect(Collectors.toList());
+ when(model.getHosts()).thenReturn(hostInfos);
}
@Test
public void test() {
+ assertEquals(0, duperModel.numberOfApplications());
+
duperModel.add(application1);
assertTrue(duperModel.contains(id1));
assertEquals(Arrays.asList(application1), duperModel.getApplicationInfos());
+ assertEquals(1, duperModel.numberOfApplications());
duperModel.registerListener(listener1);
verify(listener1, times(1)).applicationActivated(application1);
verifyNoMoreInteractions(listener1);
duperModel.remove(id2);
+ assertEquals(1, duperModel.numberOfApplications());
verifyNoMoreInteractions(listener1);
duperModel.add(application2);
+ assertEquals(2, duperModel.numberOfApplications());
verify(listener1, times(1)).applicationActivated(application2);
verifyNoMoreInteractions(listener1);
@@ -61,4 +90,31 @@ public class DuperModelTest {
duperModel.remove(id1);
verifyNoMoreInteractions(listener1);
}
+
+ @Test
+ public void hostIndices() {
+ assertEquals(0, duperModel.numberOfHosts());
+
+ duperModel.add(application1);
+ assertEquals(2, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application1), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname2_1));
+
+ duperModel.add(application2);
+ assertEquals(3, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application1), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname2_1));
+
+ duperModel.remove(application1.getApplicationId());
+ assertEquals(1, duperModel.numberOfHosts());
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname2_1));
+
+ // Remove hostname2_1 and add hostname1_1 added to id2
+ setUpApplication(id2, application2, hostname1_1);
+ duperModel.add(application2);
+ assertEquals(1, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname2_1));
+ }
} \ No newline at end of file