summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java149
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ZKTenantApplications.java160
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListApplicationsHandler.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java15
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java27
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/MemoryTenantApplications.java66
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java34
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java12
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java8
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java5
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java22
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java40
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java16
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/LogReader.java6
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java3
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/DefaultSslContextFactoryProvider.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java16
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java24
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java1
25 files changed, 407 insertions, 230 deletions
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 2c4d3d99408..82231fbf5d8 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
@@ -1,109 +1,32 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.application;
-import com.google.common.collect.ImmutableSet;
-import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.log.LogLevel;
-import com.yahoo.path.Path;
-import com.yahoo.text.Utf8;
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.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.util.ArrayList;
import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.logging.Logger;
/**
- * The applications of a tenant, backed by ZooKeeper.
- *
- * Each application is stored as a single node under /config/v2/tenants/<tenant>/applications/<applications>,
- * named the same as the application id and containing the id of the session storing the content of the application.
+ * The applications of a tenant
*
* @author Ulf Lilleengen
*/
-public class TenantApplications {
-
- private static final Logger log = Logger.getLogger(TenantApplications.class.getName());
-
- private final Curator curator;
- private final Path applicationsPath;
- // 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 TenantName tenant;
-
- private TenantApplications(Curator curator, Path applicationsPath, ReloadHandler reloadHandler, TenantName tenant) {
- this.curator = curator;
- this.applicationsPath = applicationsPath;
- curator.create(applicationsPath);
- this.reloadHandler = reloadHandler;
- this.tenant = tenant;
- this.directoryCache = curator.createDirectoryCache(applicationsPath.getAbsolute(), false, false, pathChildrenExecutor);
- this.directoryCache.start();
- this.directoryCache.addListener(this::childEvent);
- }
-
- public static TenantApplications create(Curator curator, ReloadHandler reloadHandler, TenantName tenant) {
- try {
- return new TenantApplications(curator, TenantRepository.getApplicationsPath(tenant), reloadHandler, tenant);
- } catch (Exception e) {
- throw new RuntimeException(TenantRepository.logPre(tenant) + "Error creating application repo", e);
- }
- }
+public interface TenantApplications {
/**
* List the active applications of a tenant in this config server.
*
- * @return a list of {@link ApplicationId}s that are active.
+ * @return a list of {@link com.yahoo.config.provision.ApplicationId}s that are active.
*/
- public List<ApplicationId> listApplications() {
- try {
- List<String> appNodes = curator.framework().getChildren().forPath(applicationsPath.getAbsolute());
- List<ApplicationId> applicationIds = new ArrayList<>();
- for (String appNode : appNodes) {
- parseApplication(appNode).ifPresent(applicationIds::add);
- }
- return applicationIds;
- } catch (Exception e) {
- throw new RuntimeException(TenantRepository.logPre(tenant)+"Unable to list applications", e);
- }
- }
-
- private Optional<ApplicationId> parseApplication(String appNode) {
- try {
- return Optional.of(ApplicationId.fromSerializedForm(appNode));
- } catch (IllegalArgumentException e) {
- log.log(LogLevel.INFO, TenantRepository.logPre(tenant)+"Unable to parse application with id '" + appNode + "', ignoring.");
- return Optional.empty();
- }
- }
+ List<ApplicationId> listApplications();
/**
* Register active application and adds it to the repo. If it already exists it is overwritten.
*
- * @param applicationId An {@link ApplicationId} that represents an active application.
+ * @param applicationId An {@link com.yahoo.config.provision.ApplicationId} that represents an active application.
* @param sessionId Id of the session containing the application package for this id.
*/
- public Transaction createPutApplicationTransaction(ApplicationId applicationId, long sessionId) {
- if (listApplications().contains(applicationId)) {
- return new CuratorTransaction(curator).add(CuratorOperations.setData(applicationsPath.append(applicationId.serializedForm()).getAbsolute(), Utf8.toAsciiBytes(sessionId)));
- } else {
- return new CuratorTransaction(curator).add(CuratorOperations.create(applicationsPath.append(applicationId.serializedForm()).getAbsolute(), Utf8.toAsciiBytes(sessionId)));
- }
- }
+ Transaction createPutApplicationTransaction(ApplicationId applicationId, long sessionId);
/**
* Return the stored session id for a given application.
@@ -112,68 +35,24 @@ public class TenantApplications {
* @return session id of given application id.
* @throws IllegalArgumentException if the application does not exist
*/
- public long getSessionIdForApplication(ApplicationId applicationId) {
- String path = applicationsPath.append(applicationId.serializedForm()).getAbsolute();
- try {
- return Long.parseLong(Utf8.toString(curator.framework().getData().forPath(path)));
- } catch (Exception e) {
- throw new IllegalArgumentException(TenantRepository.logPre(applicationId) + "Unable to read the session id from '" + path + "'", e);
- }
- }
+ long getSessionIdForApplication(ApplicationId applicationId);
/**
* Returns a transaction which deletes this application
*
* @param applicationId an {@link ApplicationId} to delete.
*/
- public CuratorTransaction deleteApplication(ApplicationId applicationId) {
- Path path = applicationsPath.append(applicationId.serializedForm());
- return CuratorTransaction.from(CuratorOperations.delete(path.getAbsolute()), curator);
- }
-
- /**
- * Closes the application repo. Once a repo has been closed, it should not be used again.
- */
- public void close() {
- directoryCache.close();
- }
-
- private void childEvent(CuratorFramework client, PathChildrenCacheEvent event) {
- switch (event.getType()) {
- case CHILD_ADDED:
- applicationAdded(ApplicationId.fromSerializedForm(Path.fromString(event.getData().getPath()).getName()));
- break;
- // Event CHILD_REMOVED will be triggered on all config servers if deleteApplication() above is called on one of them
- case CHILD_REMOVED:
- applicationRemoved(ApplicationId.fromSerializedForm(Path.fromString(event.getData().getPath()).getName()));
- break;
- case CHILD_UPDATED:
- // do nothing, application just got redeployed
- break;
- default:
- break;
- }
- // We may have lost events and may need to remove applications.
- // New applications are added when session is added, not here. See RemoteSessionRepo.
- removeUnusedApplications();
- }
-
- private void applicationRemoved(ApplicationId applicationId) {
- reloadHandler.removeApplication(applicationId);
- log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Application removed: " + applicationId);
- }
-
- private void applicationAdded(ApplicationId applicationId) {
- log.log(LogLevel.DEBUG, TenantRepository.logPre(applicationId) + "Application added: " + applicationId);
- }
+ Transaction deleteApplication(ApplicationId applicationId);
/**
* Removes unused applications
*
*/
- public void removeUnusedApplications() {
- ImmutableSet<ApplicationId> activeApplications = ImmutableSet.copyOf(listApplications());
- reloadHandler.removeApplicationsExcept(activeApplications);
- }
+ void removeUnusedApplications();
+
+ /**
+ * Closes the application repo. Once a repo has been closed, it should not be used again.
+ */
+ void close();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ZKTenantApplications.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ZKTenantApplications.java
new file mode 100644
index 00000000000..466df60d688
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ZKTenantApplications.java
@@ -0,0 +1,160 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.application;
+
+import com.google.common.collect.ImmutableSet;
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.log.LogLevel;
+import com.yahoo.path.Path;
+import com.yahoo.text.Utf8;
+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.transaction.CuratorOperations;
+import com.yahoo.vespa.curator.transaction.CuratorTransaction;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+/**
+ * The applications of a tenant, backed by ZooKeeper.
+ * Each application is stored as a single node under /config/v2/tenants/&lt;tenant&gt;/applications/&lt;applications&gt;,
+ * named the same as the application id and containing the id of the session storing the content of the application.
+ *
+ * @author Ulf Lilleengen
+ */
+// TODO: Merge into interface and separate out curator layer instead
+public class ZKTenantApplications implements TenantApplications, PathChildrenCacheListener {
+
+ private static final Logger log = Logger.getLogger(ZKTenantApplications.class.getName());
+
+ private final Curator curator;
+ private final Path applicationsPath;
+ // One thread pool for all instances of this class
+ private static final ExecutorService pathChildrenExecutor =
+ Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory(ZKTenantApplications.class.getName()));
+ private final Curator.DirectoryCache directoryCache;
+ private final ReloadHandler reloadHandler;
+ private final TenantName tenant;
+
+ private ZKTenantApplications(Curator curator, Path applicationsPath, ReloadHandler reloadHandler, TenantName tenant) {
+ this.curator = curator;
+ this.applicationsPath = applicationsPath;
+ curator.create(applicationsPath);
+ this.reloadHandler = reloadHandler;
+ this.tenant = tenant;
+ this.directoryCache = curator.createDirectoryCache(applicationsPath.getAbsolute(), false, false, pathChildrenExecutor);
+ this.directoryCache.start();
+ this.directoryCache.addListener(this);
+ }
+
+ public static TenantApplications create(Curator curator, ReloadHandler reloadHandler, TenantName tenant) {
+ try {
+ return new ZKTenantApplications(curator, TenantRepository.getApplicationsPath(tenant), reloadHandler, tenant);
+ } catch (Exception e) {
+ throw new RuntimeException(TenantRepository.logPre(tenant) + "Error creating application repo", e);
+ }
+ }
+
+ private long readSessionId(ApplicationId appId, String appNode) {
+ String path = applicationsPath.append(appNode).getAbsolute();
+ try {
+ return Long.parseLong(Utf8.toString(curator.framework().getData().forPath(path)));
+ } catch (Exception e) {
+ throw new IllegalArgumentException(TenantRepository.logPre(appId) + "Unable to read the session id from '" + path + "'", e);
+ }
+ }
+
+ @Override
+ public List<ApplicationId> listApplications() {
+ try {
+ List<String> appNodes = curator.framework().getChildren().forPath(applicationsPath.getAbsolute());
+ List<ApplicationId> applicationIds = new ArrayList<>();
+ for (String appNode : appNodes) {
+ parseApplication(appNode).ifPresent(applicationIds::add);
+ }
+ return applicationIds;
+ } catch (Exception e) {
+ throw new RuntimeException(TenantRepository.logPre(tenant)+"Unable to list applications", e);
+ }
+ }
+
+ private Optional<ApplicationId> parseApplication(String appNode) {
+ try {
+ return Optional.of(ApplicationId.fromSerializedForm(appNode));
+ } catch (IllegalArgumentException e) {
+ log.log(LogLevel.INFO, TenantRepository.logPre(tenant)+"Unable to parse application with id '" + appNode + "', ignoring.");
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Transaction createPutApplicationTransaction(ApplicationId applicationId, long sessionId) {
+ if (listApplications().contains(applicationId)) {
+ return new CuratorTransaction(curator).add(CuratorOperations.setData(applicationsPath.append(applicationId.serializedForm()).getAbsolute(), Utf8.toAsciiBytes(sessionId)));
+ } else {
+ return new CuratorTransaction(curator).add(CuratorOperations.create(applicationsPath.append(applicationId.serializedForm()).getAbsolute(), Utf8.toAsciiBytes(sessionId)));
+ }
+ }
+
+ @Override
+ public long getSessionIdForApplication(ApplicationId applicationId) {
+ return readSessionId(applicationId, applicationId.serializedForm());
+ }
+
+ @Override
+ public CuratorTransaction deleteApplication(ApplicationId applicationId) {
+ Path path = applicationsPath.append(applicationId.serializedForm());
+ return CuratorTransaction.from(CuratorOperations.delete(path.getAbsolute()), curator);
+ }
+
+ @Override
+ public void close() {
+ directoryCache.close();
+ }
+
+ @Override
+ public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) {
+ switch (event.getType()) {
+ case CHILD_ADDED:
+ applicationAdded(ApplicationId.fromSerializedForm(Path.fromString(event.getData().getPath()).getName()));
+ break;
+ // Event CHILD_REMOVED will be triggered on all config servers if deleteApplication() above is called on one of them
+ case CHILD_REMOVED:
+ applicationRemoved(ApplicationId.fromSerializedForm(Path.fromString(event.getData().getPath()).getName()));
+ break;
+ case CHILD_UPDATED:
+ // do nothing, application just got redeployed
+ break;
+ default:
+ break;
+ }
+ // We might have lost events and might need to remove applications (new applications are
+ // not added by listening for events here, they are added when session is added, see RemoteSessionRepo)
+ removeUnusedApplications();
+ }
+
+ private void applicationRemoved(ApplicationId applicationId) {
+ reloadHandler.removeApplication(applicationId);
+ log.log(LogLevel.INFO, TenantRepository.logPre(applicationId) + "Application removed: " + applicationId);
+ }
+
+ private void applicationAdded(ApplicationId applicationId) {
+ log.log(LogLevel.DEBUG, TenantRepository.logPre(applicationId) + "Application added: " + applicationId);
+ }
+
+ public void removeUnusedApplications() {
+ ImmutableSet<ApplicationId> activeApplications = ImmutableSet.copyOf(listApplications());
+ reloadHandler.removeApplicationsExcept(activeApplications);
+ }
+
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListApplicationsHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListApplicationsHandler.java
index 6345532d4ff..56895e3516e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListApplicationsHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ListApplicationsHandler.java
@@ -9,10 +9,10 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
-import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.http.HttpHandler;
import com.yahoo.vespa.config.server.http.Utils;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
index 37082888d70..198f8e8e917 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java
@@ -18,7 +18,7 @@ import java.util.logging.Logger;
*
* @author Harald Musum
*/
-public class LocalSessionStateWatcher {
+public class LocalSessionStateWatcher implements NodeCacheListener {
private static final Logger log = Logger.getLogger(LocalSessionStateWatcher.class.getName());
// One thread pool for all instances of this class
@@ -33,7 +33,7 @@ public class LocalSessionStateWatcher {
this.session = session;
this.localSessionRepo = localSessionRepo;
this.fileCache.start();
- this.fileCache.addListener(this::nodeChanged);
+ this.fileCache.addListener(this);
}
// Will delete session if it exists in local session repo
@@ -59,6 +59,7 @@ public class LocalSessionStateWatcher {
}
}
+ @Override
public void nodeChanged() {
executor.execute(() -> {
try {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
index 15182813a22..fe29c27abbe 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
@@ -17,12 +17,12 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
import com.yahoo.vespa.config.server.application.ApplicationSet;
-import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.yolean.Exceptions;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
@@ -38,7 +38,7 @@ import org.apache.curator.framework.recipes.cache.*;
* @author Vegard Havdal
* @author Ulf Lilleengen
*/
-public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
+public class RemoteSessionRepo extends SessionRepo<RemoteSession> implements NodeCacheListener, PathChildrenCacheListener {
private static final Logger log = Logger.getLogger(RemoteSessionRepo.class.getName());
// One thread pool for all instances of this class
@@ -77,7 +77,7 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
this.metrics = metricUpdater;
initializeSessions();
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, pathChildrenExecutor);
- this.directoryCache.addListener(this::childEvent);
+ this.directoryCache.addListener(this);
this.directoryCache.start();
}
@@ -172,7 +172,7 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
RemoteSession session = remoteSessionFactory.createSession(sessionId);
Path sessionPath = sessionsPath.append(String.valueOf(sessionId));
Curator.FileCache fileCache = curator.createFileCache(sessionPath.append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH).getAbsolute(), false);
- fileCache.addListener(this::nodeChanged);
+ fileCache.addListener(this);
loadSessionIfActive(session);
sessionStateWatchers.put(sessionId, new RemoteSessionStateWatcher(fileCache, reloadHandler, session, metrics));
addSession(session);
@@ -215,7 +215,8 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
}
}
- private void nodeChanged() {
+ @Override
+ public void nodeChanged() {
Multiset<Session.Status> sessionMetrics = HashMultiset.create();
for (RemoteSession session : listSessions()) {
sessionMetrics.add(session.getStatus());
@@ -226,7 +227,8 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
metrics.setDeactivatedSessions(sessionMetrics.count(Session.Status.DEACTIVATE));
}
- private void childEvent(CuratorFramework framework, PathChildrenCacheEvent event) {
+ @Override
+ public void childEvent(CuratorFramework framework, PathChildrenCacheEvent event) {
if (log.isLoggable(LogLevel.DEBUG)) {
log.log(LogLevel.DEBUG, "Got child event: " + event);
}
@@ -252,5 +254,4 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
session.confirmUpload();
}
}
-
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
index ef59e28f458..1a891c65c49 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionStateWatcher.java
@@ -20,7 +20,7 @@ import java.util.logging.Logger;
*
* @author Vegard Havdal
*/
-public class RemoteSessionStateWatcher {
+public class RemoteSessionStateWatcher implements NodeCacheListener {
private static final Logger log = Logger.getLogger(RemoteSessionStateWatcher.class.getName());
// One thread pool for all instances of this class
@@ -41,7 +41,7 @@ public class RemoteSessionStateWatcher {
this.session = session;
this.metrics = metrics;
this.fileCache.start();
- this.fileCache.addListener(this::nodeChanged);
+ this.fileCache.addListener(this);
}
private void sessionChanged(Session.Status status) {
@@ -72,6 +72,7 @@ public class RemoteSessionStateWatcher {
}
}
+ @Override
public void nodeChanged() {
executor.execute(() -> {
try {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
index 078b6e861a9..95d58ecd5bb 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantBuilder.java
@@ -10,6 +10,7 @@ import com.yahoo.vespa.config.server.host.HostValidator;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.RequestHandler;
import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.application.ZKTenantApplications;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.monitoring.Metrics;
import com.yahoo.vespa.config.server.session.*;
@@ -120,7 +121,7 @@ public class TenantBuilder {
private void createApplicationRepo() {
if (applicationRepo == null) {
- applicationRepo = TenantApplications.create(componentRegistry.getCurator(), reloadHandler, tenant);
+ applicationRepo = ZKTenantApplications.create(componentRegistry.getCurator(), reloadHandler, tenant);
}
}
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..bcdd14f8344 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
@@ -14,7 +14,9 @@ import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.curator.Curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.zookeeper.KeeperException;
import java.time.Duration;
@@ -35,7 +37,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -56,7 +57,7 @@ import java.util.stream.Collectors;
* @author Vegard Havdal
* @author Ulf Lilleengen
*/
-public class TenantRepository {
+public class TenantRepository implements ConnectionStateListener, PathChildrenCacheListener {
public static final TenantName HOSTED_VESPA_TENANT = TenantName.from("hosted-vespa");
private static final TenantName DEFAULT_TENANT = TenantName.defaultName();
@@ -103,7 +104,7 @@ public class TenantRepository {
this.curator = globalComponentRegistry.getCurator();
metricUpdater = globalComponentRegistry.getMetrics().getOrCreateMetricUpdater(Collections.emptyMap());
this.tenantListeners.add(globalComponentRegistry.getTenantListener());
- curator.framework().getConnectionStateListenable().addListener(this::stateChanged);
+ curator.framework().getConnectionStateListenable().addListener(this);
curator.create(tenantsPath);
createSystemTenants(configserverConfig);
@@ -112,7 +113,7 @@ public class TenantRepository {
if (useZooKeeperWatchForTenantChanges) {
this.directoryCache = Optional.of(curator.createDirectoryCache(tenantsPath.getAbsolute(), false, false, pathChildrenExecutor));
this.directoryCache.get().start();
- this.directoryCache.get().addListener(this::childEvent);
+ this.directoryCache.get().addListener(this);
} else {
this.directoryCache = Optional.empty();
}
@@ -317,7 +318,8 @@ public class TenantRepository {
return ret.toString();
}
- private void stateChanged(CuratorFramework framework, ConnectionState connectionState) {
+ @Override
+ public void stateChanged(CuratorFramework framework, ConnectionState connectionState) {
switch (connectionState) {
case CONNECTED:
metricUpdater.incZKConnected();
@@ -337,7 +339,8 @@ public class TenantRepository {
}
}
- private void childEvent(CuratorFramework framework, PathChildrenCacheEvent event) {
+ @Override
+ public void childEvent(CuratorFramework framework, PathChildrenCacheEvent event) {
switch (event.getType()) {
case CHILD_ADDED:
case CHILD_REMOVED:
@@ -348,16 +351,8 @@ public class TenantRepository {
public void close() {
directoryCache.ifPresent(Curator.DirectoryCache::close);
- try {
- pathChildrenExecutor.shutdown();
- checkForRemovedApplicationsService.shutdown();
- pathChildrenExecutor.awaitTermination(50, TimeUnit.SECONDS);
- checkForRemovedApplicationsService.awaitTermination(50, TimeUnit.SECONDS);
- }
- catch (InterruptedException e) {
- log.log(Level.WARNING, "Interrupted while shutting down.", e);
- Thread.currentThread().interrupt();
- }
+ pathChildrenExecutor.shutdown();
+ checkForRemovedApplicationsService.shutdown();
}
public boolean checkThatTenantExists(TenantName tenant) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/MemoryTenantApplications.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/MemoryTenantApplications.java
new file mode 100644
index 00000000000..ce4b5af7650
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/MemoryTenantApplications.java
@@ -0,0 +1,66 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.application;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.transaction.Transaction;
+import com.yahoo.vespa.config.server.session.DummyTransaction;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * In memory {@link TenantApplications} to be used when testing.
+ *
+ * @author Ulf Lilleengen
+ * @since 5.1
+ */
+public class MemoryTenantApplications implements TenantApplications {
+
+ private final Map<ApplicationId, Long> applications = new LinkedHashMap<>();
+ private boolean isOpen = true;
+
+ @Override
+ public List<ApplicationId> listApplications() {
+ List<ApplicationId> lst = new ArrayList<>();
+ lst.addAll(applications.keySet());
+ return lst;
+ }
+
+ @Override
+ public Transaction createPutApplicationTransaction(ApplicationId applicationId, long sessionId) {
+ return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> {
+ applications.put(applicationId, sessionId);
+ });
+ }
+
+ @Override
+ public long getSessionIdForApplication(ApplicationId id) {
+ if (applications.containsKey(id)) {
+ return applications.get(id);
+ }
+ return 0;
+ }
+
+ @Override
+ public Transaction deleteApplication(ApplicationId id) {
+ return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> {
+ applications.remove(id);
+ });
+ }
+
+ @Override
+ public void removeUnusedApplications() {
+ // do nothing
+ }
+
+ @Override
+ public void close() {
+ isOpen = false;
+ }
+
+ public boolean isOpen() {
+ return isOpen;
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
index a708e4d8ace..3fa1b3fdb5e 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java
@@ -13,6 +13,8 @@ import org.apache.curator.framework.CuratorFramework;
import org.junit.Before;
import org.junit.Test;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.is;
@@ -101,6 +103,36 @@ public class TenantApplicationsTest {
}
@Test
+ public void require_that_repos_behave_similarly() throws Exception {
+ TenantApplications zkRepo = createZKAppRepo();
+ TenantApplications memRepo = new MemoryTenantApplications();
+ for (TenantApplications repo : Arrays.asList(zkRepo, memRepo)) {
+ ApplicationId id1 = createApplicationId("myapp");
+ ApplicationId id2 = createApplicationId("myapp2");
+ repo.createPutApplicationTransaction(id1, 4).commit();
+ repo.createPutApplicationTransaction(id2, 5).commit();
+ List<ApplicationId> lst = repo.listApplications();
+ Collections.sort(lst);
+ assertThat(lst.size(), is(2));
+ assertThat(lst.get(0).application(), is(id1.application()));
+ assertThat(lst.get(1).application(), is(id2.application()));
+ assertThat(repo.getSessionIdForApplication(id1), is(4l));
+ assertThat(repo.getSessionIdForApplication(id2), is(5l));
+ repo.createPutApplicationTransaction(id1, 6).commit();
+ lst = repo.listApplications();
+ Collections.sort(lst);
+ assertThat(lst.size(), is(2));
+ assertThat(lst.get(0).application(), is(id1.application()));
+ assertThat(lst.get(1).application(), is(id2.application()));
+ assertThat(repo.getSessionIdForApplication(id1), is(6l));
+ assertThat(repo.getSessionIdForApplication(id2), is(5l));
+ repo.deleteApplication(id1).commit();
+ assertThat(repo.listApplications().size(), is(1));
+ repo.deleteApplication(id2).commit();
+ }
+ }
+
+ @Test
public void require_that_reload_handler_is_called_when_apps_are_removed() throws Exception {
ApplicationId foo = createApplicationId("foo");
writeApplicationData(foo, 3L);
@@ -122,7 +154,7 @@ public class TenantApplicationsTest {
}
private TenantApplications createZKAppRepo(MockReloadHandler reloadHandler) {
- return TenantApplications.create(curator, reloadHandler, tenantName);
+ return ZKTenantApplications.create(curator, reloadHandler, tenantName);
}
private static ApplicationId createApplicationId(String name) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
index 380b76c30af..f384fda8796 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java
@@ -20,9 +20,9 @@ import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.HttpRequest;
import com.yahoo.slime.JsonFormat;
import com.yahoo.vespa.config.server.ApplicationRepository;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.SuperModelGenerationCounter;
import com.yahoo.vespa.config.server.TestComponentRegistry;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
@@ -98,22 +98,22 @@ public class SessionActiveHandlerTest extends SessionHandlerTest {
@Before
public void setup() {
remoteSessionRepo = new RemoteSessionRepo(tenantName);
+ applicationRepo = new MemoryTenantApplications();
curator = new MockCurator();
+ localRepo = new LocalSessionRepo(clock, curator);
+ pathPrefix = "/application/v2/tenant/" + tenantName + "/session/";
+ hostProvisioner = new MockProvisioner();
modelFactory = new VespaModelFactory(new NullConfigModelRegistry());
componentRegistry = new TestComponentRegistry.Builder()
.curator(curator)
.modelFactoryRegistry(new ModelFactoryRegistry(Collections.singletonList(modelFactory)))
.build();
- tenantRepository = new TenantRepository(componentRegistry, false);
- applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), tenantName);
- localRepo = new LocalSessionRepo(clock, curator);
- pathPrefix = "/application/v2/tenant/" + tenantName + "/session/";
- hostProvisioner = new MockProvisioner();
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenantName)
.withSessionFactory(new MockSessionFactory())
.withLocalSessionRepo(localRepo)
.withRemoteSessionRepo(remoteSessionRepo)
.withApplicationRepo(applicationRepo);
+ tenantRepository = new TenantRepository(componentRegistry, false);
tenantRepository.addTenant(tenantBuilder);
handler = createHandler();
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
index 94d3b126bd7..803a87ada1c 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
@@ -7,8 +7,8 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.vespa.config.server.ApplicationRepository;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.http.CompressedApplicationInputStreamTest;
@@ -18,7 +18,6 @@ import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
import com.yahoo.vespa.config.server.tenant.TenantBuilder;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
import org.junit.Before;
import org.junit.Ignore;
@@ -71,9 +70,8 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
@Before
public void setupRepo() {
- Curator curator = new MockCurator();
- applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), tenant);
- localSessionRepo = new LocalSessionRepo(Clock.systemUTC(), curator);
+ applicationRepo = new MemoryTenantApplications();
+ localSessionRepo = new LocalSessionRepo(Clock.systemUTC(), new MockCurator());
tenantRepository = new TenantRepository(componentRegistry, false);
sessionFactory = new MockSessionFactory();
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenant)
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
index 11c0cf057cc..330d6592a2d 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java
@@ -19,12 +19,11 @@ import com.yahoo.slime.Slime;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.ApplicationRepository;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
-import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.host.HostRegistry;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
import com.yahoo.vespa.config.server.configchange.MockRefeedAction;
import com.yahoo.vespa.config.server.configchange.MockRestartAction;
@@ -87,7 +86,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest {
.withSessionFactory(new MockSessionFactory())
.withLocalSessionRepo(localRepo)
.withRemoteSessionRepo(remoteSessionRepo)
- .withApplicationRepo(TenantApplications.create(curator, new MockReloadHandler(), tenant));
+ .withApplicationRepo(new MemoryTenantApplications());
tenantRepository.addTenant(tenantBuilder);
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
index 3626c6269cc..7b9389ada9b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
@@ -5,9 +5,8 @@ import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.test.ManualClock;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
-import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.io.IOUtils;
import com.yahoo.vespa.config.server.host.HostRegistry;
@@ -55,7 +54,7 @@ public class LocalSessionRepoTest {
}
clock = new ManualClock(Instant.ofEpochSecond(1));
LocalSessionLoader loader = new SessionFactoryImpl(globalComponentRegistry,
- TenantApplications.create(new MockCurator(), new MockReloadHandler(), tenantName),
+ new MemoryTenantApplications(),
tenantFileSystemDirs, new HostRegistry<>(),
tenantName);
repo = new LocalSessionRepo(tenantFileSystemDirs, loader, clock, 5, globalComponentRegistry.getCurator());
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
index e7db4dcf58f..37784b313b6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java
@@ -15,9 +15,8 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.path.Path;
import com.yahoo.slime.Slime;
import com.yahoo.transaction.NestedTransaction;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.SuperModelGenerationCounter;
-import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.deploy.ZooKeeperClient;
@@ -201,7 +200,7 @@ public class LocalSessionTest {
FilesApplicationPackage.fromFile(testApp),
zkc,
sessionDir,
- TenantApplications.create(curator, new MockReloadHandler(), tenant),
+ new MemoryTenantApplications(),
new HostRegistry<>(),
superModelGenerationCounter,
flagSource));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
index e22185ae69b..2290ee5890b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/MockSessionZKClient.java
@@ -7,7 +7,6 @@ import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.TenantName;
import com.yahoo.transaction.Transaction;
import com.yahoo.path.Path;
-import com.yahoo.vespa.config.server.session.Session.Status;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
@@ -21,12 +20,12 @@ import java.util.Optional;
*/
public class MockSessionZKClient extends SessionZooKeeperClient {
- private ApplicationPackage app;
+ private ApplicationPackage app = null;
private Optional<AllocatedHosts> info = Optional.empty();
- private Status sessionStatus;
+ private Session.Status sessionStatus;
public MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId) {
- this(curator, tenantName, sessionId, (ApplicationPackage) null);
+ this(curator, tenantName, sessionId, (ApplicationPackage)null);
}
public MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId, Optional<AllocatedHosts> allocatedHosts) {
@@ -37,11 +36,11 @@ public class MockSessionZKClient extends SessionZooKeeperClient {
public MockSessionZKClient(Curator curator, TenantName tenantName, long sessionId, ApplicationPackage application) {
super(curator, TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId)));
this.app = application;
- curator.create(TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId)));
}
public MockSessionZKClient(ApplicationPackage app) {
- this(new MockCurator(), TenantName.defaultName(), 123, app);
+ super(new MockCurator(), Path.createRoot());
+ this.app = app;
}
@Override
@@ -55,4 +54,15 @@ public class MockSessionZKClient extends SessionZooKeeperClient {
return info.orElseThrow(() -> new IllegalStateException("Could not find allocated hosts"));
}
+ @Override
+ public Transaction createWriteStatusTransaction(Session.Status status) {
+ return new DummyTransaction().add((DummyTransaction.RunnableOperation) () -> {
+ sessionStatus = status;
+ });
+ }
+
+ @Override
+ public Session.Status readStatus() {
+ return sessionStatus;
+ }
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
index 9dda653dbc1..4bbfea48254 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
@@ -6,11 +6,12 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.path.Path;
import com.yahoo.text.Utf8;
+import com.yahoo.transaction.Transaction;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.tenant.Tenant;
@@ -24,6 +25,8 @@ import org.junit.Test;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.LongPredicate;
@@ -99,9 +102,8 @@ public class RemoteSessionRepoTest {
@Test
public void testBadApplicationRepoOnActivate() {
long sessionId = 3L;
+ TenantApplications applicationRepo = new FailingTenantApplications();
TenantName mytenant = TenantName.from("mytenant");
- TenantApplications applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), mytenant);
- curator.set(TenantRepository.getApplicationsPath(mytenant).append("mytenant:appX:default"), new byte[0]); // Invalid data
Tenant tenant = TenantBuilder.create(new TestComponentRegistry.Builder().curator(curator).build(), mytenant)
.withApplicationRepo(applicationRepo)
.build();
@@ -149,4 +151,36 @@ public class RemoteSessionRepoTest {
} while (System.currentTimeMillis() < endTime && !ok);
}
+ private class FailingTenantApplications implements TenantApplications {
+
+ @Override
+ public List<ApplicationId> listApplications() {
+ return Collections.singletonList(ApplicationId.defaultId());
+ }
+
+ @Override
+ public Transaction createPutApplicationTransaction(ApplicationId applicationId, long sessionId) {
+ return null;
+ }
+
+ @Override
+ public long getSessionIdForApplication(ApplicationId applicationId) {
+ throw new IllegalArgumentException("Bad id " + applicationId);
+ }
+
+ @Override
+ public Transaction deleteApplication(ApplicationId applicationId) {
+ return null;
+ }
+
+ @Override
+ public void removeUnusedApplications() {
+ // do nothing
+ }
+
+ @Override
+ public void close() {
+
+ }
+ }
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
index 9ad90e84d86..ac7d6948b22 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
@@ -15,12 +15,11 @@ import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
import com.yahoo.slime.Slime;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.SuperModelGenerationCounter;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.TimeoutBudgetTest;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import com.yahoo.vespa.config.server.application.PermanentApplicationPackage;
-import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
import com.yahoo.vespa.config.server.host.HostRegistry;
import com.yahoo.vespa.config.server.http.InvalidApplicationException;
@@ -217,8 +216,7 @@ public class SessionPreparerTest {
return new SessionContext(app,
new SessionZooKeeperClient(curator, sessionsPath),
app.getAppDir(),
- TenantApplications.create(curator, new MockReloadHandler(), TenantName.from("tenant")),
- new HostRegistry<>(),
+ new MemoryTenantApplications(), new HostRegistry<>(),
new SuperModelGenerationCounter(curator),
flagSource);
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
index baab250a508..1b3afeb353b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java
@@ -3,22 +3,19 @@ package com.yahoo.vespa.config.server.tenant;
import com.google.common.testing.EqualsTester;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.vespa.config.server.MockReloadHandler;
import com.yahoo.vespa.config.server.TestComponentRegistry;
-import com.yahoo.vespa.config.server.application.TenantApplications;
-import com.yahoo.vespa.curator.mock.MockCurator;
+import com.yahoo.vespa.config.server.application.MemoryTenantApplications;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.*;
/**
* @author Ulf Lilleengen
*/
public class TenantTest {
-
private final TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder().build();
private Tenant t1;
@@ -38,7 +35,7 @@ public class TenantTest {
TenantRepository tenantRepository = new TenantRepository(componentRegistry, false);
TenantName tenantName = TenantName.from(name);
TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenantName)
- .withApplicationRepo(TenantApplications.create(new MockCurator(), new MockReloadHandler(), tenantName));
+ .withApplicationRepo(new MemoryTenantApplications());
tenantRepository.addTenant(tenantBuilder);
return tenantRepository.getTenant(tenantName);
}
@@ -59,4 +56,11 @@ public class TenantTest {
assertThat(t1.hashCode(), is(not(t4.hashCode())));
}
+ @Test
+ public void close() {
+ MemoryTenantApplications repo = (MemoryTenantApplications) t1.getApplicationRepo();
+ assertTrue(repo.isOpen());
+ t1.close();
+ assertFalse(repo.isOpen());
+ }
}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/LogReader.java b/container-core/src/main/java/com/yahoo/container/handler/LogReader.java
index 76ea4579244..c6ae20973fb 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/LogReader.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/LogReader.java
@@ -4,11 +4,11 @@ package com.yahoo.container.handler;
import org.json.JSONException;
import org.json.JSONObject;
+import java.time.Duration;
import java.util.Base64;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.attribute.BasicFileAttributes;
public class LogReader {
@@ -17,7 +17,7 @@ public class LogReader {
protected JSONObject readLogs(String logDirectory, long earliestLogThreshold, long latestLogThreshold) throws IOException, JSONException {
this.earliestLogThreshold = earliestLogThreshold;
- this.latestLogThreshold = latestLogThreshold;
+ this.latestLogThreshold = latestLogThreshold + Duration.ofMinutes(5).toMillis(); // Add some time to allow retrieving logs currently being modified
JSONObject json = new JSONObject();
File root = new File(logDirectory);
traverse_folder(root, json, "");
@@ -27,7 +27,7 @@ public class LogReader {
private void traverse_folder(File root, JSONObject json, String filename) throws IOException, JSONException {
File[] files = root.listFiles();
for(File child : files) {
- long logTime = Files.readAttributes(child.toPath(), BasicFileAttributes.class).creationTime().toMillis();
+ long logTime = Files.getLastModifiedTime(child.toPath()).toMillis();
if(child.isFile() && earliestLogThreshold < logTime && logTime < latestLogThreshold) {
json.put(filename + child.getName(), Base64.getEncoder().encodeToString(Files.readAllBytes(child.toPath())));
}
diff --git a/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java b/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java
index 3b1be62dfb1..d7c802faa3b 100644
--- a/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java
+++ b/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java
@@ -6,6 +6,7 @@ import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
+import java.time.Instant;
import static org.junit.Assert.*;
@@ -22,7 +23,7 @@ public class LogReaderTest {
public void testThatFilesAreWrittenCorrectlyToOutputStream() throws Exception{
String logDirectory = "src/test/resources/logfolder/";
LogReader logReader = new LogReader();
- JSONObject json = logReader.readLogs(logDirectory, 21, Long.MAX_VALUE);
+ JSONObject json = logReader.readLogs(logDirectory, 21, Instant.now().toEpochMilli());
String expected = "{\"subfolder-log2.log\":\"VGhpcyBpcyBhbm90aGVyIGxvZyBmaWxl\",\"log1.log\":\"VGhpcyBpcyBvbmUgbG9nIGZpbGU=\"}";
String actual = json.toString();
assertEquals(expected, actual);
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/DefaultSslContextFactoryProvider.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/DefaultSslContextFactoryProvider.java
index 03796c551e5..0bbe6207294 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/DefaultSslContextFactoryProvider.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/DefaultSslContextFactoryProvider.java
@@ -30,6 +30,6 @@ public class DefaultSslContextFactoryProvider extends AbstractComponent implemen
@Override
public void deconstruct() {
- tlsContext.close();
+ if (tlsContext != null) tlsContext.close();
}
} \ No newline at end of file
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
index f729c30de20..5e5b3546ced 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
@@ -12,15 +12,12 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import java.time.Duration;
import java.time.Instant;
-import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* @author bratseth
@@ -75,17 +72,8 @@ public abstract class ApplicationMaintainer extends Maintainer {
protected Deployer deployer() { return deployer; }
- protected Set<ApplicationId> applicationsNeedingMaintenance() {
- return nodesNeedingMaintenance().stream()
- .map(node -> node.allocation().get().owner())
- .collect(Collectors.toCollection(LinkedHashSet::new));
- }
-
- /**
- * Returns the nodes whose applications should be maintained by this now.
- * This should be some subset of the allocated nodes.
- */
- protected abstract List<Node> nodesNeedingMaintenance();
+ /** Returns the applications that should be maintained by this now. */
+ protected abstract Set<ApplicationId> applicationsNeedingMaintenance();
/** Redeploy this application. A lock will be taken for the duration of the deployment activation */
protected final void deployWithLock(ApplicationId application) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
index 45db9ea90d9..b9e27e5d4ae 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainer.java
@@ -3,14 +3,18 @@ package com.yahoo.vespa.hosted.provision.maintenance;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Deployer;
+import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
+import com.yahoo.vespa.hosted.provision.node.Allocation;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
-import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -26,6 +30,8 @@ import java.util.stream.Collectors;
*/
public class OperatorChangeApplicationMaintainer extends ApplicationMaintainer {
+ private static final ApplicationId ZONE_APPLICATION_ID = ApplicationId.from("hosted-vespa", "routing", "default");
+
private final Clock clock;
private Instant previousRun;
@@ -38,22 +44,28 @@ public class OperatorChangeApplicationMaintainer extends ApplicationMaintainer {
}
@Override
- protected List<Node> nodesNeedingMaintenance() {
+ protected Set<ApplicationId> applicationsNeedingMaintenance() {
Instant windowEnd = clock.instant();
Instant windowStart = previousRun;
previousRun = windowEnd;
return nodeRepository().getNodes().stream()
- .filter(node -> node.allocation().isPresent())
.filter(node -> hasManualStateChangeSince(windowStart, node))
- .collect(Collectors.toList());
+ .flatMap(node -> owner(node).stream())
+ .collect(Collectors.toCollection(LinkedHashSet::new));
}
-
+
private boolean hasManualStateChangeSince(Instant instant, Node node) {
return node.history().events().stream()
.anyMatch(event -> event.agent() == Agent.operator && event.at().isAfter(instant));
}
- /**
+ private Optional<ApplicationId> owner(Node node) {
+ if (node.allocation().isPresent()) return node.allocation().map(Allocation::owner);
+
+ return node.type() == NodeType.host ? Optional.of(ZONE_APPLICATION_ID) : Optional.empty();
+ }
+
+ /**
* Deploy in the maintenance thread to avoid scheduling multiple deployments of the same application if it takes
* longer to deploy than the (short) maintenance interval of this
*/
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
index ff0773fde71..2525dbecca2 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
@@ -75,7 +75,6 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
return clock.instant().isBefore(start.plus(minTimeBetweenRedeployments));
}
- @Override
protected List<Node> nodesNeedingMaintenance() {
return nodeRepository().getNodes(Node.State.active);
}