summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Musum <musum@verizonmedia.com>2020-06-10 10:50:04 +0200
committerGitHub <noreply@github.com>2020-06-10 10:50:04 +0200
commit3d1b445b10f70771cf7a4bce1ba2d285c553f930 (patch)
treeb924518f3e81d19b96124c51b242e0b4942a7e1c
parentfbc3461127b6edf235e11e231fdbde0b250dd4f4 (diff)
parentc699ecc25a03216a4d6a600b8786d8bda99baa4a (diff)
Merge pull request #13530 from vespa-engine/musum/configserver-refactoring-7-take-2
Config server refactoring, part 7, take 2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java31
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java151
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionStateWatcher.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java (renamed from configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java)236
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java29
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java15
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java51
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java17
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java106
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java145
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java221
18 files changed, 485 insertions, 549 deletions
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 dd88096c62f..af5a561aa6e 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
@@ -49,10 +49,9 @@ import com.yahoo.vespa.config.server.http.v2.PrepareResult;
import com.yahoo.vespa.config.server.metrics.ApplicationMetricsRetriever;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.session.LocalSession;
-import com.yahoo.vespa.config.server.session.LocalSessionRepo;
+import com.yahoo.vespa.config.server.session.SessionRepository;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.session.RemoteSession;
-import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
import com.yahoo.vespa.config.server.session.Session;
import com.yahoo.vespa.config.server.session.SessionFactory;
import com.yahoo.vespa.config.server.session.SilentDeployLogger;
@@ -305,7 +304,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
if (activeSession == null) return Optional.empty();
TimeoutBudget timeoutBudget = new TimeoutBudget(clock, timeout);
LocalSession newSession = tenant.getSessionFactory().createSessionFromExisting(activeSession, logger, true, timeoutBudget);
- tenant.getLocalSessionRepo().addSession(newSession);
+ tenant.getSessionRepository().addSession(newSession);
return Optional.of(Deployment.unprepared(newSession, this, hostProvisioner, tenant, timeout, clock,
false /* don't validate as this is already deployed */, bootstrap));
@@ -489,7 +488,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
Tenant tenant = tenantRepository.getTenant(applicationId.tenant());
if (tenant == null) throw new NotFoundException("Tenant '" + applicationId.tenant() + "' not found");
long sessionId = getSessionIdForApplication(tenant, applicationId);
- RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId);
+ RemoteSession session = tenant.getSessionRepo().getRemoteSession(sessionId);
if (session == null) throw new NotFoundException("Remote session " + sessionId + " not found");
return session.ensureApplicationLoaded().getForVersionOrLatest(version, clock.instant());
} catch (NotFoundException e) {
@@ -526,10 +525,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
private boolean localSessionHasBeenDeleted(ApplicationId applicationId, long sessionId, Duration waitTime) {
- RemoteSessionRepo remoteSessionRepo = tenantRepository.getTenant(applicationId.tenant()).getRemoteSessionRepo();
+ SessionRepository sessionRepository = tenantRepository.getTenant(applicationId.tenant()).getSessionRepo();
Instant end = Instant.now().plus(waitTime);
do {
- if (remoteSessionRepo.getSession(sessionId) == null) return true;
+ if (sessionRepository.getRemoteSession(sessionId) == null) return true;
try { Thread.sleep(10); } catch (InterruptedException e) { /* ignored */}
} while (Instant.now().isBefore(end));
@@ -635,11 +634,11 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
boolean internalRedeploy,
TimeoutBudget timeoutBudget) {
Tenant tenant = tenantRepository.getTenant(applicationId.tenant());
- LocalSessionRepo localSessionRepo = tenant.getLocalSessionRepo();
+ SessionRepository sessionRepository = tenant.getSessionRepository();
SessionFactory sessionFactory = tenant.getSessionFactory();
RemoteSession fromSession = getExistingSession(tenant, applicationId);
LocalSession session = sessionFactory.createSessionFromExisting(fromSession, logger, internalRedeploy, timeoutBudget);
- localSessionRepo.addSession(session);
+ sessionRepository.addSession(session);
return session.getSessionId();
}
@@ -660,13 +659,13 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
Optional<Long> activeSessionId = tenant.getApplicationRepo().activeSessionOf(applicationId);
LocalSession session = tenant.getSessionFactory().createSession(applicationDirectory, applicationId,
timeoutBudget, activeSessionId);
- tenant.getLocalSessionRepo().addSession(session);
+ tenant.getSessionRepository().addSession(session);
return session.getSessionId();
}
public void deleteExpiredLocalSessions() {
Map<Tenant, List<LocalSession>> sessionsPerTenant = new HashMap<>();
- tenantRepository.getAllTenants().forEach(tenant -> sessionsPerTenant.put(tenant, tenant.getLocalSessionRepo().getSessions()));
+ tenantRepository.getAllTenants().forEach(tenant -> sessionsPerTenant.put(tenant, tenant.getSessionRepository().getSessions()));
Set<ApplicationId> applicationIds = new HashSet<>();
sessionsPerTenant.values().forEach(sessionList -> sessionList.forEach(s -> applicationIds.add(s.getApplicationId())));
@@ -677,7 +676,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
if (activeSession != null)
activeSessions.put(applicationId, activeSession.getSessionId());
});
- sessionsPerTenant.keySet().forEach(tenant -> tenant.getLocalSessionRepo().deleteExpiredSessions(activeSessions));
+ sessionsPerTenant.keySet().forEach(tenant -> tenant.getSessionRepository().deleteExpiredSessions(activeSessions));
}
public int deleteExpiredRemoteSessions(Duration expiryTime) {
@@ -687,7 +686,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public int deleteExpiredRemoteSessions(Clock clock, Duration expiryTime) {
return tenantRepository.getAllTenants()
.stream()
- .map(tenant -> tenant.getRemoteSessionRepo().deleteExpiredSessions(clock, expiryTime))
+ .map(tenant -> tenant.getSessionRepo().deleteExpiredRemoteSessions(clock, expiryTime))
.mapToInt(i -> i)
.sum();
}
@@ -747,14 +746,14 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
private LocalSession getLocalSession(Tenant tenant, long sessionId) {
- LocalSession session = tenant.getLocalSessionRepo().getSession(sessionId);
+ LocalSession session = tenant.getSessionRepository().getSession(sessionId);
if (session == null) throw new NotFoundException("Session " + sessionId + " was not found");
return session;
}
private RemoteSession getRemoteSession(Tenant tenant, long sessionId) {
- RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId);
+ RemoteSession session = tenant.getSessionRepo().getRemoteSession(sessionId);
if (session == null) throw new NotFoundException("Session " + sessionId + " was not found");
return session;
@@ -805,7 +804,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
private RemoteSession getActiveSession(Tenant tenant, ApplicationId applicationId) {
TenantApplications applicationRepo = tenant.getApplicationRepo();
if (applicationRepo.activeApplications().contains(applicationId)) {
- return tenant.getRemoteSessionRepo().getSession(applicationRepo.requireActiveSessionOf(applicationId));
+ return tenant.getSessionRepo().getRemoteSession(applicationRepo.requireActiveSessionOf(applicationId));
}
return null;
}
@@ -813,7 +812,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public LocalSession getActiveLocalSession(Tenant tenant, ApplicationId applicationId) {
TenantApplications applicationRepo = tenant.getApplicationRepo();
if (applicationRepo.activeApplications().contains(applicationId)) {
- return tenant.getLocalSessionRepo().getSession(applicationRepo.requireActiveSessionOf(applicationId));
+ return tenant.getSessionRepository().getSession(applicationRepo.requireActiveSessionOf(applicationId));
}
return null;
}
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 a4dfec708d6..d4fe35c14b1 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
@@ -213,7 +213,7 @@ public class TenantApplications implements RequestHandler, ReloadHandler, HostVa
break;
}
// We may have lost events and may need to remove applications.
- // New applications are added when session is added, not here. See RemoteSessionRepo.
+ // New applications are added when session is added, not here. See SessionRepository.
removeUnusedApplications();
});
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java
deleted file mode 100644
index e23552dee44..00000000000
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// 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.session;
-
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.path.Path;
-import com.yahoo.transaction.NestedTransaction;
-import com.yahoo.vespa.config.server.GlobalComponentRegistry;
-import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
-import com.yahoo.vespa.curator.Curator;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- *
- * Contains state for the local instance of the configserver.
- *
- * @author Ulf Lilleengen
- */
-public class LocalSessionRepo {
-
- private static final Logger log = Logger.getLogger(LocalSessionRepo.class.getName());
- private static final FilenameFilter sessionApplicationsFilter = (dir, name) -> name.matches("\\d+");
-
- private final SessionCache<LocalSession> sessionCache;
- private final Map<Long, LocalSessionStateWatcher> sessionStateWatchers = new HashMap<>();
- private final Duration sessionLifetime;
- private final Clock clock;
- private final Curator curator;
- private final Executor zkWatcherExecutor;
- private final TenantFileSystemDirs tenantFileSystemDirs;
-
- public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry componentRegistry, SessionFactory sessionFactory) {
- sessionCache = new SessionCache<>();
- this.clock = componentRegistry.getClock();
- this.curator = componentRegistry.getCurator();
- this.sessionLifetime = Duration.ofSeconds(componentRegistry.getConfigserverConfig().sessionLifetime());
- this.zkWatcherExecutor = command -> componentRegistry.getZkWatcherExecutor().execute(tenantName, command);
- this.tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName);
- loadSessions(sessionFactory);
- }
-
- public synchronized void addSession(LocalSession session) {
- sessionCache.addSession(session);
- Path sessionsPath = TenantRepository.getSessionsPath(session.getTenantName());
- long sessionId = session.getSessionId();
- Curator.FileCache fileCache = curator.createFileCache(sessionsPath.append(String.valueOf(sessionId)).append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH).getAbsolute(), false);
- sessionStateWatchers.put(sessionId, new LocalSessionStateWatcher(fileCache, session, this, zkWatcherExecutor));
- }
-
- public LocalSession getSession(long sessionId) {
- return sessionCache.getSession(sessionId);
- }
-
- public List<LocalSession> getSessions() {
- return sessionCache.getSessions();
- }
-
- private void loadSessions(SessionFactory sessionFactory) {
- File[] sessions = tenantFileSystemDirs.sessionsPath().listFiles(sessionApplicationsFilter);
- if (sessions == null) {
- return;
- }
- for (File session : sessions) {
- try {
- addSession(sessionFactory.createSessionFromId(Long.parseLong(session.getName())));
- } catch (IllegalArgumentException e) {
- log.log(Level.WARNING, "Could not load session '" +
- session.getAbsolutePath() + "':" + e.getMessage() + ", skipping it.");
- }
- }
- }
-
- public void deleteExpiredSessions(Map<ApplicationId, Long> activeSessions) {
- log.log(Level.FINE, "Purging old sessions");
- try {
- for (LocalSession candidate : sessionCache.getSessions()) {
- Instant createTime = candidate.getCreateTime();
- log.log(Level.FINE, "Candidate session for deletion: " + candidate.getSessionId() + ", created: " + createTime);
-
- // Sessions with state other than ACTIVATED
- if (hasExpired(candidate) && !isActiveSession(candidate)) {
- deleteSession(candidate);
- } else if (createTime.plus(Duration.ofDays(1)).isBefore(clock.instant())) {
- // Sessions with state ACTIVATE, but which are not actually active
- ApplicationId applicationId = candidate.getApplicationId();
- Long activeSession = activeSessions.get(applicationId);
- if (activeSession == null || activeSession != candidate.getSessionId()) {
- deleteSession(candidate);
- log.log(Level.INFO, "Deleted inactive session " + candidate.getSessionId() + " created " +
- createTime + " for '" + applicationId + "'");
- }
- }
- }
- // Make sure to catch here, to avoid executor just dying in case of issues ...
- } catch (Throwable e) {
- log.log(Level.WARNING, "Error when purging old sessions ", e);
- }
- log.log(Level.FINE, "Done purging old sessions");
- }
-
- private boolean hasExpired(LocalSession candidate) {
- return (candidate.getCreateTime().plus(sessionLifetime).isBefore(clock.instant()));
- }
-
- private boolean isActiveSession(LocalSession candidate) {
- return candidate.getStatus() == Session.Status.ACTIVATE;
- }
-
- public void deleteSession(LocalSession session) {
- long sessionId = session.getSessionId();
- log.log(Level.FINE, "Deleting local session " + sessionId);
- LocalSessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
- if (watcher != null) watcher.close();
- sessionCache.removeSession(sessionId);
- NestedTransaction transaction = new NestedTransaction();
- session.delete(transaction);
- transaction.commit();
- }
-
- public void close() {
- deleteAllSessions();
- tenantFileSystemDirs.delete();
- }
-
- private void deleteAllSessions() {
- List<LocalSession> sessions = new ArrayList<>(sessionCache.getSessions());
- for (LocalSession session : sessions) {
- deleteSession(session);
- }
- }
-
- @Override
- public String toString() {
- return getSessions().toString();
- }
-
-}
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 662094fc0ca..1067a373f41 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
@@ -21,14 +21,14 @@ public class LocalSessionStateWatcher {
private final Curator.FileCache fileCache;
private final LocalSession session;
- private final LocalSessionRepo localSessionRepo;
+ private final SessionRepository sessionRepository;
private final Executor zkWatcherExecutor;
LocalSessionStateWatcher(Curator.FileCache fileCache, LocalSession session,
- LocalSessionRepo localSessionRepo, Executor zkWatcherExecutor) {
+ SessionRepository sessionRepository, Executor zkWatcherExecutor) {
this.fileCache = fileCache;
this.session = session;
- this.localSessionRepo = localSessionRepo;
+ this.sessionRepository = sessionRepository;
this.zkWatcherExecutor = zkWatcherExecutor;
this.fileCache.start();
this.fileCache.addListener(this::nodeChanged);
@@ -40,9 +40,9 @@ public class LocalSessionStateWatcher {
log.log(status == Session.Status.DELETE ? Level.INFO : Level.FINE,
session.logPre() + "Session change: Local session " + sessionId + " changed status to " + status);
- if (status.equals(Session.Status.DELETE) && localSessionRepo.getSession(sessionId) != null) {
+ if (status.equals(Session.Status.DELETE) && sessionRepository.getSession(sessionId) != null) {
log.log(Level.FINE, session.logPre() + "Deleting session " + sessionId);
- localSessionRepo.deleteSession(session);
+ sessionRepository.deleteSession(session);
}
}
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/SessionRepository.java
index 0e538b05931..9fe725453f4 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/SessionRepository.java
@@ -3,16 +3,14 @@ package com.yahoo.vespa.config.server.session;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
-import com.yahoo.concurrent.StripedExecutor;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
-
-import java.time.Clock;
-import java.util.logging.Level;
import com.yahoo.path.Path;
+import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.monitoring.Metrics;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
@@ -25,6 +23,9 @@ import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -33,75 +34,187 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
- * Session repository for RemoteSessions. There is one such repo per tenant.
- * Will watch/prepare sessions (applications) based on watched nodes in ZooKeeper. The zookeeper state watched in
- * this class is shared between all config servers, so it should not modify any global state, because the operation
- * will be performed on all servers. The repo can be regarded as read only from the POV of the configserver.
*
- * @author Vegard Havdal
+ * Session repository for config server. Stores session state in zookeeper and file system. There are two
+ * different session types (RemoteSession and LocalSession).
+ *
* @author Ulf Lilleengen
+ * @author hmusum
+ *
*/
-public class RemoteSessionRepo {
+public class SessionRepository {
+
+ private static final Logger log = Logger.getLogger(SessionRepository.class.getName());
+ private static final FilenameFilter sessionApplicationsFilter = (dir, name) -> name.matches("\\d+");
- private static final Logger log = Logger.getLogger(RemoteSessionRepo.class.getName());
+ private final SessionCache<LocalSession> localSessionCache;
+ private final SessionCache<RemoteSession> remoteSessionCache = new SessionCache<>();
+ private final Map<Long, LocalSessionStateWatcher> localSessionStateWatchers = new HashMap<>();
+ private final Map<Long, RemoteSessionStateWatcher> remoteSessionStateWatchers = new HashMap<>();
+ private final Duration sessionLifetime;
+ private final Clock clock;
private final Curator curator;
- private final Path sessionsPath;
- private final SessionFactory sessionFactory;
- private final Map<Long, RemoteSessionStateWatcher> sessionStateWatchers = new HashMap<>();
+ private final Executor zkWatcherExecutor;
+ private final TenantFileSystemDirs tenantFileSystemDirs;
+ private final BooleanFlag distributeApplicationPackage;
private final ReloadHandler reloadHandler;
- private final TenantName tenantName;
private final MetricUpdater metrics;
- private final BooleanFlag distributeApplicationPackage;
private final Curator.DirectoryCache directoryCache;
private final TenantApplications applicationRepo;
- private final Executor zkWatcherExecutor;
- private final SessionCache<RemoteSession> sessionCache;
-
- public RemoteSessionRepo(GlobalComponentRegistry componentRegistry,
- SessionFactory sessionFactory,
- ReloadHandler reloadHandler,
- TenantName tenantName,
- TenantApplications applicationRepo,
- FlagSource flagSource) {
- this.sessionCache = new SessionCache<>();
+ private final Path sessionsPath;
+ private final SessionFactory sessionFactory;
+ private final TenantName tenantName;
+
+ public SessionRepository(TenantName tenantName, GlobalComponentRegistry componentRegistry,
+ SessionFactory sessionFactory, TenantApplications applicationRepo,
+ ReloadHandler reloadHandler, FlagSource flagSource) {
+ this.tenantName = tenantName;
+ localSessionCache = new SessionCache<>();
+ this.clock = componentRegistry.getClock();
this.curator = componentRegistry.getCurator();
- this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
- this.applicationRepo = applicationRepo;
+ this.sessionLifetime = Duration.ofSeconds(componentRegistry.getConfigserverConfig().sessionLifetime());
+ this.zkWatcherExecutor = command -> componentRegistry.getZkWatcherExecutor().execute(tenantName, command);
+ this.tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName);
this.sessionFactory = sessionFactory;
+ this.applicationRepo = applicationRepo;
+ loadLocalSessions(sessionFactory);
+
+ this.distributeApplicationPackage = Flags.CONFIGSERVER_DISTRIBUTE_APPLICATION_PACKAGE.bindTo(flagSource);
this.reloadHandler = reloadHandler;
- this.tenantName = tenantName;
+ this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
this.metrics = componentRegistry.getMetrics().getOrCreateMetricUpdater(Metrics.createDimensions(tenantName));
- this.distributeApplicationPackage = Flags.CONFIGSERVER_DISTRIBUTE_APPLICATION_PACKAGE.bindTo(flagSource);
- StripedExecutor<TenantName> zkWatcherExecutor = componentRegistry.getZkWatcherExecutor();
- this.zkWatcherExecutor = command -> zkWatcherExecutor.execute(tenantName, command);
- initializeSessions();
+ initializeRemoteSessions();
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, componentRegistry.getZkCacheExecutor());
this.directoryCache.addListener(this::childEvent);
this.directoryCache.start();
}
- public RemoteSession getSession(long sessionId) {
- return sessionCache.getSession(sessionId);
+ // ---------------- Local sessions ----------------------------------------------------------------
+
+ public synchronized void addSession(LocalSession session) {
+ localSessionCache.addSession(session);
+ Path sessionsPath = TenantRepository.getSessionsPath(session.getTenantName());
+ long sessionId = session.getSessionId();
+ Curator.FileCache fileCache = curator.createFileCache(sessionsPath.append(String.valueOf(sessionId)).append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH).getAbsolute(), false);
+ localSessionStateWatchers.put(sessionId, new LocalSessionStateWatcher(fileCache, session, this, zkWatcherExecutor));
+ }
+
+ public LocalSession getSession(long sessionId) {
+ return localSessionCache.getSession(sessionId);
+ }
+
+ public List<LocalSession> getSessions() {
+ return localSessionCache.getSessions();
+ }
+
+ private void loadLocalSessions(SessionFactory sessionFactory) {
+ File[] sessions = tenantFileSystemDirs.sessionsPath().listFiles(sessionApplicationsFilter);
+ if (sessions == null) {
+ return;
+ }
+ for (File session : sessions) {
+ try {
+ addSession(sessionFactory.createSessionFromId(Long.parseLong(session.getName())));
+ } catch (IllegalArgumentException e) {
+ log.log(Level.WARNING, "Could not load session '" +
+ session.getAbsolutePath() + "':" + e.getMessage() + ", skipping it.");
+ }
+ }
+ }
+
+ public void deleteExpiredSessions(Map<ApplicationId, Long> activeSessions) {
+ log.log(Level.FINE, "Purging old sessions");
+ try {
+ for (LocalSession candidate : localSessionCache.getSessions()) {
+ Instant createTime = candidate.getCreateTime();
+ log.log(Level.FINE, "Candidate session for deletion: " + candidate.getSessionId() + ", created: " + createTime);
+
+ // Sessions with state other than ACTIVATED
+ if (hasExpired(candidate) && !isActiveSession(candidate)) {
+ deleteSession(candidate);
+ } else if (createTime.plus(Duration.ofDays(1)).isBefore(clock.instant())) {
+ // Sessions with state ACTIVATE, but which are not actually active
+ ApplicationId applicationId = candidate.getApplicationId();
+ Long activeSession = activeSessions.get(applicationId);
+ if (activeSession == null || activeSession != candidate.getSessionId()) {
+ deleteSession(candidate);
+ log.log(Level.INFO, "Deleted inactive session " + candidate.getSessionId() + " created " +
+ createTime + " for '" + applicationId + "'");
+ }
+ }
+ }
+ // Make sure to catch here, to avoid executor just dying in case of issues ...
+ } catch (Throwable e) {
+ log.log(Level.WARNING, "Error when purging old sessions ", e);
+ }
+ log.log(Level.FINE, "Done purging old sessions");
+ }
+
+ private boolean hasExpired(LocalSession candidate) {
+ return (candidate.getCreateTime().plus(sessionLifetime).isBefore(clock.instant()));
+ }
+
+ private boolean isActiveSession(LocalSession candidate) {
+ return candidate.getStatus() == Session.Status.ACTIVATE;
+ }
+
+ public void deleteSession(LocalSession session) {
+ long sessionId = session.getSessionId();
+ log.log(Level.FINE, "Deleting local session " + sessionId);
+ LocalSessionStateWatcher watcher = localSessionStateWatchers.remove(sessionId);
+ if (watcher != null) watcher.close();
+ localSessionCache.removeSession(sessionId);
+ NestedTransaction transaction = new NestedTransaction();
+ session.delete(transaction);
+ transaction.commit();
+ }
+
+ public void close() {
+ deleteAllSessions();
+ tenantFileSystemDirs.delete();
+ try {
+ if (directoryCache != null) {
+ directoryCache.close();
+ }
+ } catch (Exception e) {
+ log.log(Level.WARNING, "Exception when closing path cache", e);
+ } finally {
+ checkForRemovedSessions(new ArrayList<>());
+ }
}
- public List<Long> getSessions() {
+ private void deleteAllSessions() {
+ List<LocalSession> sessions = new ArrayList<>(localSessionCache.getSessions());
+ for (LocalSession session : sessions) {
+ deleteSession(session);
+ }
+ }
+
+ // ---------------- Remote sessions ----------------------------------------------------------------
+
+ public RemoteSession getRemoteSession(long sessionId) {
+ return remoteSessionCache.getSession(sessionId);
+ }
+
+ public List<Long> getRemoteSessions() {
return getSessionList(curator.getChildren(sessionsPath));
}
- public void addSession(RemoteSession session) {
- sessionCache.addSession(session);
+ public void addRemoteSession(RemoteSession session) {
+ remoteSessionCache.addSession(session);
metrics.incAddedSessions();
}
- public int deleteExpiredSessions(Clock clock, Duration expiryTime) {
+ public int deleteExpiredRemoteSessions(Clock clock, Duration expiryTime) {
int deleted = 0;
- for (long sessionId : getSessions()) {
- RemoteSession session = sessionCache.getSession(sessionId);
+ for (long sessionId : getRemoteSessions()) {
+ RemoteSession session = remoteSessionCache.getSession(sessionId);
if (session == null) continue; // Internal sessions not in synch with zk, continue
if (session.getStatus() == Session.Status.ACTIVATE) continue;
if (sessionHasExpired(session.getCreateTime(), expiryTime, clock)) {
@@ -127,8 +240,8 @@ public class RemoteSessionRepo {
return children.stream().map(Long::parseLong).collect(Collectors.toList());
}
- private void initializeSessions() throws NumberFormatException {
- getSessions().forEach(this::sessionAdded);
+ private void initializeRemoteSessions() throws NumberFormatException {
+ getRemoteSessions().forEach(this::sessionAdded);
}
private synchronized void sessionsChanged() throws NumberFormatException {
@@ -138,14 +251,14 @@ public class RemoteSessionRepo {
}
private void checkForRemovedSessions(List<Long> sessions) {
- for (RemoteSession session : sessionCache.getSessions())
+ for (RemoteSession session : remoteSessionCache.getSessions())
if ( ! sessions.contains(session.getSessionId()))
sessionRemoved(session.getSessionId());
}
-
+
private void checkForAddedSessions(List<Long> sessions) {
for (Long sessionId : sessions)
- if (sessionCache.getSession(sessionId) == null)
+ if (remoteSessionCache.getSession(sessionId) == null)
sessionAdded(sessionId);
}
@@ -155,22 +268,22 @@ public class RemoteSessionRepo {
* @param sessionId session id for the new session
*/
private void sessionAdded(long sessionId) {
- log.log(Level.FINE, () -> "Adding session to RemoteSessionRepo: " + sessionId);
+ log.log(Level.FINE, () -> "Adding session to SessionRepository: " + sessionId);
RemoteSession session = sessionFactory.createRemoteSession(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);
loadSessionIfActive(session);
- addSession(session);
- sessionStateWatchers.put(sessionId, new RemoteSessionStateWatcher(fileCache, reloadHandler, session, metrics, zkWatcherExecutor));
+ addRemoteSession(session);
+ remoteSessionStateWatchers.put(sessionId, new RemoteSessionStateWatcher(fileCache, reloadHandler, session, metrics, zkWatcherExecutor));
if (distributeApplicationPackage.value())
sessionFactory.createLocalSessionUsingDistributedApplicationPackage(sessionId);
}
private void sessionRemoved(long sessionId) {
- RemoteSessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
+ RemoteSessionStateWatcher watcher = remoteSessionStateWatchers.remove(sessionId);
if (watcher != null) watcher.close();
- sessionCache.removeSession(sessionId);
+ remoteSessionCache.removeSession(sessionId);
metrics.incRemovedSessions();
}
@@ -185,22 +298,10 @@ public class RemoteSessionRepo {
}
}
- public synchronized void close() {
- try {
- if (directoryCache != null) {
- directoryCache.close();
- }
- } catch (Exception e) {
- log.log(Level.WARNING, "Exception when closing path cache", e);
- } finally {
- checkForRemovedSessions(new ArrayList<>());
- }
- }
-
private void nodeChanged() {
zkWatcherExecutor.execute(() -> {
Multiset<Session.Status> sessionMetrics = HashMultiset.create();
- for (RemoteSession session : sessionCache.getSessions()) {
+ for (RemoteSession session : remoteSessionCache.getSessions()) {
sessionMetrics.add(session.getStatus());
}
metrics.setNewSessions(sessionMetrics.count(Session.Status.NEW));
@@ -231,11 +332,16 @@ public class RemoteSessionRepo {
private void synchronizeOnNew(List<Long> sessionList) {
for (long sessionId : sessionList) {
- RemoteSession session = sessionCache.getSession(sessionId);
+ RemoteSession session = remoteSessionCache.getSession(sessionId);
if (session == null) continue; // session might have been deleted after getting session list
log.log(Level.FINE, () -> session.logPre() + "Confirming upload for session " + sessionId);
session.confirmUpload();
}
}
+ @Override
+ public String toString() {
+ return getSessions().toString();
+ }
+
}
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 f0aab8b2312..097c3146b81 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
@@ -6,8 +6,7 @@ import com.yahoo.path.Path;
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.session.LocalSessionRepo;
-import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
+import com.yahoo.vespa.config.server.session.SessionRepository;
import com.yahoo.vespa.config.server.session.SessionFactory;
import com.yahoo.vespa.curator.Curator;
import org.apache.zookeeper.data.Stat;
@@ -28,10 +27,9 @@ public class Tenant implements TenantHandlerProvider {
static final String APPLICATIONS = "applications";
private final TenantName name;
- private final RemoteSessionRepo remoteSessionRepo;
private final Path path;
private final SessionFactory sessionFactory;
- private final LocalSessionRepo localSessionRepo;
+ private final SessionRepository sessionRepository;
private final TenantApplications applicationRepo;
private final RequestHandler requestHandler;
private final ReloadHandler reloadHandler;
@@ -39,8 +37,7 @@ public class Tenant implements TenantHandlerProvider {
Tenant(TenantName name,
SessionFactory sessionFactory,
- LocalSessionRepo localSessionRepo,
- RemoteSessionRepo remoteSessionRepo,
+ SessionRepository sessionRepository,
RequestHandler requestHandler,
ReloadHandler reloadHandler,
TenantApplications applicationRepo,
@@ -49,9 +46,8 @@ public class Tenant implements TenantHandlerProvider {
this.path = TenantRepository.getTenantPath(name);
this.requestHandler = requestHandler;
this.reloadHandler = reloadHandler;
- this.remoteSessionRepo = remoteSessionRepo;
this.sessionFactory = sessionFactory;
- this.localSessionRepo = localSessionRepo;
+ this.sessionRepository = sessionRepository;
this.applicationRepo = applicationRepo;
this.curator = curator;
}
@@ -74,13 +70,8 @@ public class Tenant implements TenantHandlerProvider {
return requestHandler;
}
- /**
- * The RemoteSessionRepo for this
- *
- * @return repo
- */
- public RemoteSessionRepo getRemoteSessionRepo() {
- return remoteSessionRepo;
+ public SessionRepository getSessionRepo() {
+ return sessionRepository;
}
public TenantName getName() {
@@ -95,8 +86,8 @@ public class Tenant implements TenantHandlerProvider {
return sessionFactory;
}
- public LocalSessionRepo getLocalSessionRepo() {
- return localSessionRepo;
+ public SessionRepository getSessionRepository() {
+ return sessionRepository;
}
@Override
@@ -140,9 +131,9 @@ public class Tenant implements TenantHandlerProvider {
* Called by watchers as a reaction to {@link #delete()}.
*/
void close() {
- remoteSessionRepo.close(); // Closes watchers and clears memory.
+ sessionRepository.close(); // Closes watchers and clears memory.
applicationRepo.close(); // Closes watchers.
- localSessionRepo.close(); // Closes watchers, clears memory, and deletes local files and ZK session state.
+ sessionRepository.close(); // Closes watchers, clears memory, and deletes local files and ZK session state.
}
/** Deletes the tenant tree from ZooKeeper (application and session status for the tenant) and triggers {@link #close()}. */
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 d34f89a179b..caf9982e034 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,8 +14,7 @@ import com.yahoo.vespa.config.server.RequestHandler;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
-import com.yahoo.vespa.config.server.session.LocalSessionRepo;
-import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
+import com.yahoo.vespa.config.server.session.SessionRepository;
import com.yahoo.vespa.config.server.session.SessionFactory;
import com.yahoo.vespa.curator.Curator;
import org.apache.curator.framework.CuratorFramework;
@@ -49,7 +48,7 @@ import java.util.stream.Collectors;
* This component will monitor the set of tenants in the config server by watching in ZooKeeper.
* It will set up Tenant objects accordingly, which will manage the config sessions per tenant.
* This class will read the preexisting set of tenants from ZooKeeper at startup. (For now it will also
- * create a default tenant since that will be used for API that do no know about tenants or have not yet
+ * create a default tenant since that will be used for APIs that do no know about tenants or have not yet
* implemented support for it).
*
* This instance is called from two different threads, the http handler threads and the zookeeper watcher threads.
@@ -224,15 +223,11 @@ public class TenantRepository {
if (reloadHandler == null)
reloadHandler = applicationRepo;
SessionFactory sessionFactory = new SessionFactory(componentRegistry, applicationRepo, applicationRepo, tenantName);
- LocalSessionRepo localSessionRepo = new LocalSessionRepo(tenantName, componentRegistry, sessionFactory);
- RemoteSessionRepo remoteSessionRepo = new RemoteSessionRepo(componentRegistry,
- sessionFactory,
- reloadHandler,
- tenantName,
- applicationRepo,
+ SessionRepository sessionRepository = new SessionRepository(tenantName, componentRegistry, sessionFactory,
+ applicationRepo, reloadHandler,
componentRegistry.getFlagSource());
log.log(Level.INFO, "Creating tenant '" + tenantName + "'");
- Tenant tenant = new Tenant(tenantName, sessionFactory, localSessionRepo, remoteSessionRepo, requestHandler,
+ Tenant tenant = new Tenant(tenantName, sessionFactory, sessionRepository, requestHandler,
reloadHandler, applicationRepo, componentRegistry.getCurator());
notifyNewTenant(tenant);
tenants.putIfAbsent(tenantName, tenant);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index a3d072be38b..2c07d50b470 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -37,7 +37,7 @@ import com.yahoo.vespa.config.server.http.InternalServerException;
import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.http.v2.PrepareResult;
import com.yahoo.vespa.config.server.session.LocalSession;
-import com.yahoo.vespa.config.server.session.LocalSessionRepo;
+import com.yahoo.vespa.config.server.session.SessionRepository;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.session.RemoteSession;
import com.yahoo.vespa.config.server.session.Session;
@@ -47,6 +47,9 @@ import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.mock.MockCurator;
+import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.model.VespaModelFactory;
import org.hamcrest.core.Is;
import org.jetbrains.annotations.NotNull;
@@ -113,16 +116,22 @@ public class ApplicationRepositoryTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
+
@Before
public void setup() throws IOException {
+ setup(new InMemoryFlagSource());
+ }
+
+ public void setup(FlagSource flagSource) throws IOException {
Curator curator = new MockCurator();
TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder()
.curator(curator)
.configServerConfig(new ConfigserverConfig.Builder()
.payloadCompressionType(ConfigserverConfig.PayloadCompressionType.Enum.UNCOMPRESSED)
- .configServerDBDir(tempFolder.newFolder("configserverdb").getAbsolutePath())
- .configDefinitionsDir(tempFolder.newFolder("configdefinitions").getAbsolutePath())
+ .configServerDBDir(tempFolder.newFolder().getAbsolutePath())
+ .configDefinitionsDir(tempFolder.newFolder().getAbsolutePath())
.build())
+ .flagSource(flagSource)
.build();
tenantRepository = new TenantRepository(componentRegistry, false);
tenantRepository.addTenant(TenantRepository.HOSTED_VESPA_TENANT);
@@ -146,7 +155,7 @@ public class ApplicationRepositoryTest {
TenantName tenantName = applicationId().tenant();
Tenant tenant = tenantRepository.getTenant(tenantName);
- LocalSession session = tenant.getLocalSessionRepo().getSession(tenant.getApplicationRepo()
+ LocalSession session = tenant.getSessionRepository().getSession(tenant.getApplicationRepo()
.requireActiveSessionOf(applicationId()));
session.getAllocatedHosts();
}
@@ -178,7 +187,7 @@ public class ApplicationRepositoryTest {
TenantName tenantName = applicationId().tenant();
Tenant tenant = tenantRepository.getTenant(tenantName);
- LocalSession session = tenant.getLocalSessionRepo().getSession(
+ LocalSession session = tenant.getSessionRepository().getSession(
tenant.getApplicationRepo().requireActiveSessionOf(applicationId()));
assertEquals(firstSessionId, session.getMetaData().getPreviousActiveGeneration());
}
@@ -295,17 +304,17 @@ public class ApplicationRepositoryTest {
PrepareResult result = deployApp(testApp);
long sessionId = result.sessionId();
Tenant tenant = tenantRepository.getTenant(applicationId().tenant());
- LocalSession applicationData = tenant.getLocalSessionRepo().getSession(sessionId);
+ LocalSession applicationData = tenant.getSessionRepository().getSession(sessionId);
assertNotNull(applicationData);
assertNotNull(applicationData.getApplicationId());
- assertNotNull(tenant.getRemoteSessionRepo().getSession(sessionId));
+ assertNotNull(tenant.getSessionRepo().getSession(sessionId));
assertNotNull(applicationRepository.getActiveSession(applicationId()));
// Delete app and verify that it has been deleted from repos and provisioner
assertTrue(applicationRepository.delete(applicationId()));
assertNull(applicationRepository.getActiveSession(applicationId()));
- assertNull(tenant.getLocalSessionRepo().getSession(sessionId));
- assertNull(tenant.getRemoteSessionRepo().getSession(sessionId));
+ assertNull(tenant.getSessionRepository().getSession(sessionId));
+ assertNull(tenant.getSessionRepo().getSession(sessionId));
assertTrue(provisioner.removed);
assertEquals(tenant.getName(), provisioner.lastApplicationId.tenant());
assertEquals(applicationId(), provisioner.lastApplicationId);
@@ -346,7 +355,7 @@ public class ApplicationRepositoryTest {
RemoteSession activeSession = applicationRepository.getActiveSession(applicationId());
assertNull(activeSession);
Tenant tenant = tenantRepository.getTenant(applicationId().tenant());
- assertNull(tenant.getRemoteSessionRepo().getSession(prepareResult.sessionId()));
+ assertNull(tenant.getSessionRepo().getSession(prepareResult.sessionId()));
assertTrue(applicationRepository.delete(applicationId()));
}
@@ -379,14 +388,14 @@ public class ApplicationRepositoryTest {
assertNotEquals(activeSessionId, deployment3session);
// No change to active session id
assertEquals(activeSessionId, tester.tenant().getApplicationRepo().requireActiveSessionOf(tester.applicationId()));
- LocalSessionRepo localSessionRepo = tester.tenant().getLocalSessionRepo();
- assertEquals(3, localSessionRepo.getSessions().size());
+ SessionRepository sessionRepository = tester.tenant().getSessionRepository();
+ assertEquals(3, sessionRepository.getSessions().size());
clock.advance(Duration.ofHours(1)); // longer than session lifetime
// All sessions except 3 should be removed after the call to deleteExpiredLocalSessions
tester.applicationRepository().deleteExpiredLocalSessions();
- Collection<LocalSession> sessions = localSessionRepo.getSessions();
+ Collection<LocalSession> sessions = sessionRepository.getSessions();
assertEquals(1, sessions.size());
ArrayList<LocalSession> localSessions = new ArrayList<>(sessions);
LocalSession localSession = localSessions.get(0);
@@ -400,9 +409,9 @@ public class ApplicationRepositoryTest {
assertTrue(deployment4.isPresent());
deployment4.get().prepare(); // session 5 (not activated)
- assertEquals(2, localSessionRepo.getSessions().size());
- localSessionRepo.deleteSession(localSession);
- assertEquals(1, localSessionRepo.getSessions().size());
+ assertEquals(2, sessionRepository.getSessions().size());
+ sessionRepository.deleteSession(localSession);
+ assertEquals(1, sessionRepository.getSessions().size());
// Check that trying to expire when there are no active sessions works
tester.applicationRepository().deleteExpiredLocalSessions();
@@ -457,7 +466,7 @@ public class ApplicationRepositoryTest {
TenantName tenantName = applicationId().tenant();
Tenant tenant = tenantRepository.getTenant(tenantName);
- LocalSession session = tenant.getLocalSessionRepo().getSession(tenant.getApplicationRepo().requireActiveSessionOf(applicationId()));
+ LocalSession session = tenant.getSessionRepository().getSession(tenant.getApplicationRepo().requireActiveSessionOf(applicationId()));
List<NetworkPorts.Allocation> list = new ArrayList<>();
list.add(new NetworkPorts.Allocation(8080, "container", "container/container.0", "http"));
@@ -658,6 +667,14 @@ public class ApplicationRepositoryTest {
resolve(SimpletypesConfig.class, requestHandler, applicationId(), vespaVersion);
}
+ @Test
+ public void testDistributionOfApplicationPackage() throws IOException {
+ FlagSource flagSource = new InMemoryFlagSource()
+ .withBooleanFlag(Flags.CONFIGSERVER_DISTRIBUTE_APPLICATION_PACKAGE.id(), true);
+ setup(flagSource);
+ applicationRepository.deploy(app1, prepareParams());
+ }
+
private ApplicationRepository createApplicationRepository() {
return new ApplicationRepository(tenantRepository,
provisioner,
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
index ec5648757f1..bc16f44b405 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java
@@ -60,6 +60,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
private final StripedExecutor<TenantName> zkWatcherExecutor;
private final ExecutorService zkCacheExecutor;
private final SecretStore secretStore;
+ private final FlagSource flagSource;
private TestComponentRegistry(Curator curator, ConfigCurator configCurator, Metrics metrics,
ModelFactoryRegistry modelFactoryRegistry,
@@ -74,7 +75,8 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
TenantListener tenantListener,
Zone zone,
Clock clock,
- SecretStore secretStore) {
+ SecretStore secretStore,
+ FlagSource flagSource) {
this.curator = curator;
this.configCurator = configCurator;
this.metrics = metrics;
@@ -94,6 +96,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
this.zkWatcherExecutor = new StripedExecutor<>(new InThreadExecutorService());
this.zkCacheExecutor = new InThreadExecutorService();
this.secretStore = secretStore;
+ this.flagSource = flagSource;
}
public static class Builder {
@@ -115,6 +118,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
private Optional<Provisioner> hostProvisioner = Optional.empty();
private Zone zone = Zone.defaultZone();
private Clock clock = Clock.systemUTC();
+ private FlagSource flagSource = new InMemoryFlagSource();
public Builder configServerConfig(ConfigserverConfig configserverConfig) {
this.configserverConfig = configserverConfig;
@@ -161,6 +165,11 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
return this;
}
+ public Builder flagSource(FlagSource flagSource) {
+ this.flagSource = flagSource;
+ return this;
+ }
+
public TestComponentRegistry build() {
final PermanentApplicationPackage permApp = this.permanentApplicationPackage
.orElse(new PermanentApplicationPackage(configserverConfig));
@@ -172,11 +181,11 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
SessionPreparer sessionPreparer = new SessionPreparer(modelFactoryRegistry, fileDistributionProvider,
hostProvisionerProvider, permApp,
configserverConfig, defRepo, curator,
- zone, new InMemoryFlagSource(), secretStore);
+ zone, flagSource, secretStore);
return new TestComponentRegistry(curator, ConfigCurator.create(curator), metrics, modelFactoryRegistry,
permApp, fileDistributionProvider, hostRegistries, configserverConfig,
sessionPreparer, hostProvisioner, defRepo, reloadListener, tenantListener,
- zone, clock, secretStore);
+ zone, clock, secretStore, flagSource);
}
}
@@ -221,7 +230,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry {
}
@Override
- public FlagSource getFlagSource() { return new InMemoryFlagSource(); }
+ public FlagSource getFlagSource() { return flagSource; }
@Override
public ExecutorService getZkCacheExecutor() {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
index b2091d6e537..bc9f45698e9 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java
@@ -249,7 +249,7 @@ public class DeployTester {
public AllocatedHosts getAllocatedHostsOf(ApplicationId applicationId) {
Tenant tenant = tenant();
- LocalSession session = tenant.getLocalSessionRepo().getSession(tenant.getApplicationRepo()
+ LocalSession session = tenant.getSessionRepository().getSession(tenant.getApplicationRepo()
.requireActiveSessionOf(applicationId));
return session.getAllocatedHosts();
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
index 36467a2ca64..156200a061c 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java
@@ -33,11 +33,11 @@ public class RedeployTest {
assertTrue(deployment.isPresent());
long activeSessionIdBefore = tester.applicationRepository().getActiveSession(tester.applicationId()).getSessionId();
- assertEquals(tester.applicationId(), tester.tenant().getLocalSessionRepo().getSession(activeSessionIdBefore).getApplicationId());
+ assertEquals(tester.applicationId(), tester.tenant().getSessionRepository().getSession(activeSessionIdBefore).getApplicationId());
deployment.get().activate();
long activeSessionIdAfter = tester.applicationRepository().getActiveSession(tester.applicationId()).getSessionId();
assertEquals(activeSessionIdAfter, activeSessionIdBefore + 1);
- assertEquals(tester.applicationId(), tester.tenant().getLocalSessionRepo().getSession(activeSessionIdAfter).getApplicationId());
+ assertEquals(tester.applicationId(), tester.tenant().getSessionRepository().getSession(activeSessionIdAfter).getApplicationId());
}
/** No deployment is done because there is no local active session. */
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
index 078dc47af51..4cf81d22e3c 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java
@@ -54,13 +54,13 @@ public class ApplicationContentHandlerTest extends ContentHandlerTestBase {
session2 = new MockLocalSession(2, FilesApplicationPackage.fromFile(new File("src/test/apps/content")));
Tenant tenant1 = tenantRepository.getTenant(tenantName1);
- tenant1.getLocalSessionRepo().addSession(session2);
+ tenant1.getSessionRepository().addSession(session2);
tenant1.getApplicationRepo().createApplication(idTenant1);
tenant1.getApplicationRepo().createPutTransaction(idTenant1, 2).commit();
MockLocalSession session3 = new MockLocalSession(3, FilesApplicationPackage.fromFile(new File("src/test/apps/content2")));
Tenant tenant2 = tenantRepository.getTenant(tenantName2);
- tenant2.getLocalSessionRepo().addSession(session3);
+ tenant2.getSessionRepository().addSession(session3);
tenant2.getApplicationRepo().createApplication(idTenant2);
tenant2.getApplicationRepo().createPutTransaction(idTenant2, 3).commit();
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
index ff8f7a291ad..ec141a61ee6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
@@ -286,13 +286,13 @@ public class ApplicationHandlerTest {
private void deleteAndAssertOKResponseMocked(ApplicationId applicationId, boolean fullAppIdInUrl) throws IOException {
long sessionId = tenantRepository.getTenant(applicationId.tenant()).getApplicationRepo().requireActiveSessionOf(applicationId);
deleteAndAssertResponse(applicationId, Zone.defaultZone(), Response.Status.OK, null, fullAppIdInUrl);
- assertNull(tenantRepository.getTenant(applicationId.tenant()).getLocalSessionRepo().getSession(sessionId));
+ assertNull(tenantRepository.getTenant(applicationId.tenant()).getSessionRepository().getSession(sessionId));
}
private void deleteAndAssertOKResponse(Tenant tenant, ApplicationId applicationId) throws IOException {
long sessionId = tenant.getApplicationRepo().requireActiveSessionOf(applicationId);
deleteAndAssertResponse(applicationId, Zone.defaultZone(), Response.Status.OK, null, true);
- assertNull(tenant.getLocalSessionRepo().getSession(sessionId));
+ assertNull(tenant.getSessionRepository().getSession(sessionId));
}
private void deleteAndAssertResponse(ApplicationId applicationId, Zone zone, int expectedStatus, HttpErrorResponse.errorCodes errorCode, boolean fullAppIdInUrl) throws IOException {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java
index 2eaa5d75ba7..457acf8c376 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java
@@ -52,11 +52,11 @@ public class HostHandlerTest {
tenant.getApplicationRepo().createApplication(applicationId);
tenant.getApplicationRepo().createPutTransaction(applicationId, sessionId).commit();
ApplicationPackage app = FilesApplicationPackage.fromFile(testApp);
- tenant.getLocalSessionRepo().addSession(new SessionHandlerTest.MockLocalSession(sessionId, app, applicationId));
+ tenant.getSessionRepository().addSession(new SessionHandlerTest.MockLocalSession(sessionId, app, applicationId));
TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder()
.modelFactoryRegistry(new ModelFactoryRegistry(Collections.singletonList(new VespaModelFactory(new NullConfigModelRegistry()))))
.build();
- tenant.getRemoteSessionRepo().addSession(new RemoteSession(tenant.getName(), sessionId, componentRegistry, new MockSessionZKClient(app)));
+ tenant.getSessionRepo().addRemoteSession(new RemoteSession(tenant.getName(), sessionId, componentRegistry, new MockSessionZKClient(app)));
}
@Before
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
index 88bf6fb7172..f639843ac08 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionContentHandlerTest.java
@@ -43,7 +43,7 @@ public class SessionContentHandlerTest extends ContentHandlerTestBase {
public void setupHandler() throws Exception {
tenantRepository = new TenantRepository(componentRegistry, false);
tenantRepository.addTenant(tenant);
- tenantRepository.getTenant(tenant).getLocalSessionRepo().addSession(new MockLocalSession(1L, FilesApplicationPackage.fromFile(createTestApp())));
+ tenantRepository.getTenant(tenant).getSessionRepository().addSession(new MockLocalSession(1L, FilesApplicationPackage.fromFile(createTestApp())));
handler = createHandler();
pathPrefix = "/application/v2/tenant/" + tenant + "/session/";
baseUrl = "http://foo:1337/application/v2/tenant/" + tenant + "/session/1/content/";
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
deleted file mode 100644
index a758698d3b5..00000000000
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.session;
-
-import com.yahoo.cloud.config.ConfigserverConfig;
-import com.yahoo.config.model.application.provider.FilesApplicationPackage;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.io.IOUtils;
-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.host.HostRegistry;
-import com.yahoo.vespa.config.server.http.SessionHandlerTest;
-import com.yahoo.vespa.curator.mock.MockCurator;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-/**
- * @author Ulf Lilleengen
- */
-public class LocalSessionRepoTest {
-
- private File testApp = new File("src/test/apps/app");
- private LocalSessionRepo repo;
- private static final TenantName tenantName = TenantName.defaultName();
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Before
- public void setupSessions() throws Exception {
- setupSessions(tenantName, true);
- }
-
- private void setupSessions(TenantName tenantName, boolean createInitialSessions) throws Exception {
- File configserverDbDir = temporaryFolder.newFolder().getAbsoluteFile();
- if (createInitialSessions) {
- Path sessionsPath = Paths.get(configserverDbDir.getAbsolutePath(), "tenants", tenantName.value(), "sessions");
- IOUtils.copyDirectory(testApp, sessionsPath.resolve("1").toFile());
- IOUtils.copyDirectory(testApp, sessionsPath.resolve("2").toFile());
- IOUtils.copyDirectory(testApp, sessionsPath.resolve("3").toFile());
- }
- GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder()
- .curator(new MockCurator())
- .configServerConfig(new ConfigserverConfig.Builder()
- .configServerDBDir(configserverDbDir.getAbsolutePath())
- .configDefinitionsDir(temporaryFolder.newFolder().getAbsolutePath())
- .sessionLifetime(5)
- .build())
- .build();
- SessionFactory sessionFactory = new SessionFactory(globalComponentRegistry,
- TenantApplications.create(globalComponentRegistry, tenantName),
- new HostRegistry<>(),
- tenantName);
- repo = new LocalSessionRepo(tenantName, globalComponentRegistry, sessionFactory);
- }
-
- @Test
- public void require_that_sessions_can_be_loaded_from_disk() {
- assertNotNull(repo.getSession(1L));
- assertNotNull(repo.getSession(2L));
- assertNotNull(repo.getSession(3L));
- assertNull(repo.getSession(4L));
- }
-
- @Test
- public void require_that_all_sessions_are_deleted() {
- repo.close();
- assertNull(repo.getSession(1L));
- assertNull(repo.getSession(2L));
- assertNull(repo.getSession(3L));
- }
-
- @Test
- public void require_that_sessions_belong_to_a_tenant() {
- // tenant is "default"
- assertNotNull(repo.getSession(1L));
- assertNotNull(repo.getSession(2L));
- assertNotNull(repo.getSession(3L));
- assertNull(repo.getSession(4L));
-
- // tenant is "newTenant"
- try {
- setupSessions(TenantName.from("newTenant"), false);
- } catch (Exception e) {
- fail();
- }
- assertNull(repo.getSession(1L));
-
- repo.addSession(new SessionHandlerTest.MockLocalSession(1L, FilesApplicationPackage.fromFile(testApp)));
- repo.addSession(new SessionHandlerTest.MockLocalSession(2L, FilesApplicationPackage.fromFile(testApp)));
- assertNotNull(repo.getSession(1L));
- assertNotNull(repo.getSession(2L));
- assertNull(repo.getSession(3L));
- }
-}
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
deleted file mode 100644
index 468dd5a15a7..00000000000
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-// 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.session;
-
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.path.Path;
-import com.yahoo.text.Utf8;
-import com.yahoo.vespa.config.server.TestComponentRegistry;
-import com.yahoo.vespa.config.server.tenant.Tenant;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.curator.mock.MockCurator;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.function.LongPredicate;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Ulf Lilleengen
- */
-public class RemoteSessionRepoTest {
-
- private static final TenantName tenantName = TenantName.defaultName();
-
- private RemoteSessionRepo remoteSessionRepo;
- private Curator curator;
- TenantRepository tenantRepository;
-
- @Before
- public void setupFacade() {
- curator = new MockCurator();
- TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder()
- .curator(curator)
- .build();
- tenantRepository = new TenantRepository(componentRegistry, false);
- tenantRepository.addTenant(tenantName);
- this.remoteSessionRepo = tenantRepository.getTenant(tenantName).getRemoteSessionRepo();
- curator.create(TenantRepository.getTenantPath(tenantName).append("/applications"));
- curator.create(TenantRepository.getSessionsPath(tenantName));
- createSession(1L, false);
- createSession(2L, false);
- }
-
- private void createSession(long sessionId, boolean wait) {
- createSession(sessionId, wait, tenantName);
- }
-
- private void createSession(long sessionId, boolean wait, TenantName tenantName) {
- Path sessionsPath = TenantRepository.getSessionsPath(tenantName);
- SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, sessionsPath.append(String.valueOf(sessionId)));
- zkc.createNewSession(Instant.now());
- if (wait) {
- Curator.CompletionWaiter waiter = zkc.getUploadWaiter();
- waiter.awaitCompletion(Duration.ofSeconds(120));
- }
- }
-
- @Test
- public void testInitialize() {
- assertSessionExists(1L);
- assertSessionExists(2L);
- }
-
- @Test
- public void testCreateSession() {
- createSession(3L, true);
- assertSessionExists(3L);
- }
-
- @Test
- public void testSessionStateChange() throws Exception {
- long sessionId = 3L;
- createSession(sessionId, true);
- assertSessionStatus(sessionId, Session.Status.NEW);
- assertStatusChange(sessionId, Session.Status.PREPARE);
- assertStatusChange(sessionId, Session.Status.ACTIVATE);
-
- Path session = TenantRepository.getSessionsPath(tenantName).append("" + sessionId);
- curator.delete(session);
- assertSessionRemoved(sessionId);
- assertNull(remoteSessionRepo.getSession(sessionId));
- }
-
- // If reading a session throws an exception it should be handled and not prevent other applications
- // from loading. In this test we just show that we end up with one session in remote session
- // repo even if it had bad data (by making getSessionIdForApplication() in FailingTenantApplications
- // throw an exception).
- @Test
- public void testBadApplicationRepoOnActivate() {
- long sessionId = 3L;
- TenantName mytenant = TenantName.from("mytenant");
- curator.set(TenantRepository.getApplicationsPath(mytenant).append("mytenant:appX:default"), new byte[0]); // Invalid data
- tenantRepository.addTenant(mytenant);
- Tenant tenant = tenantRepository.getTenant(mytenant);
- curator.create(TenantRepository.getSessionsPath(mytenant));
- remoteSessionRepo = tenant.getRemoteSessionRepo();
- assertThat(remoteSessionRepo.getSessions().size(), is(0));
- createSession(sessionId, true, mytenant);
- assertThat(remoteSessionRepo.getSessions().size(), is(1));
- }
-
- private void assertStatusChange(long sessionId, Session.Status status) throws Exception {
- Path statePath = TenantRepository.getSessionsPath(tenantName).append("" + sessionId).append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH);
- curator.create(statePath);
- curator.framework().setData().forPath(statePath.getAbsolute(), Utf8.toBytes(status.toString()));
- assertSessionStatus(sessionId, status);
- }
-
- private void assertSessionRemoved(long sessionId) {
- waitFor(p -> remoteSessionRepo.getSession(sessionId) == null, sessionId);
- assertNull(remoteSessionRepo.getSession(sessionId));
- }
-
- private void assertSessionExists(long sessionId) {
- assertSessionStatus(sessionId, Session.Status.NEW);
- }
-
- private void assertSessionStatus(long sessionId, Session.Status status) {
- waitFor(p -> remoteSessionRepo.getSession(sessionId) != null &&
- remoteSessionRepo.getSession(sessionId).getStatus() == status, sessionId);
- assertNotNull(remoteSessionRepo.getSession(sessionId));
- assertThat(remoteSessionRepo.getSession(sessionId).getStatus(), is(status));
- }
-
- private void waitFor(LongPredicate predicate, long sessionId) {
- long endTime = System.currentTimeMillis() + 60_000;
- boolean ok;
- do {
- ok = predicate.test(sessionId);
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- } while (System.currentTimeMillis() < endTime && !ok);
- }
-
-}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java
new file mode 100644
index 00000000000..3c1404b5048
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java
@@ -0,0 +1,221 @@
+// 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.session;
+
+import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.config.model.application.provider.FilesApplicationPackage;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.io.IOUtils;
+import com.yahoo.text.Utf8;
+import com.yahoo.vespa.config.server.GlobalComponentRegistry;
+import com.yahoo.vespa.config.server.TestComponentRegistry;
+import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.host.HostRegistry;
+import com.yahoo.vespa.config.server.http.SessionHandlerTest;
+import com.yahoo.vespa.config.server.tenant.Tenant;
+import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.function.LongPredicate;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Ulf Lilleengen
+ */
+public class SessionRepositoryTest {
+
+ private File testApp = new File("src/test/apps/app");
+ private MockCurator curator;
+ private SessionRepository sessionRepository;
+ private TenantRepository tenantRepository;
+ private static final TenantName tenantName = TenantName.defaultName();
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setupSessions() throws Exception {
+ setupSessions(tenantName, true);
+ }
+
+ private void setupSessions(TenantName tenantName, boolean createInitialSessions) throws Exception {
+ File configserverDbDir = temporaryFolder.newFolder().getAbsoluteFile();
+ if (createInitialSessions) {
+ Path sessionsPath = Paths.get(configserverDbDir.getAbsolutePath(), "tenants", tenantName.value(), "sessions");
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("1").toFile());
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("2").toFile());
+ IOUtils.copyDirectory(testApp, sessionsPath.resolve("3").toFile());
+ }
+ curator = new MockCurator();
+ curator.create(TenantRepository.getTenantPath(tenantName).append("/applications"));
+ curator.create(TenantRepository.getSessionsPath(tenantName));
+ GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder()
+ .curator(curator)
+ .configServerConfig(new ConfigserverConfig.Builder()
+ .configServerDBDir(configserverDbDir.getAbsolutePath())
+ .configDefinitionsDir(temporaryFolder.newFolder().getAbsolutePath())
+ .sessionLifetime(5)
+ .build())
+ .build();
+ tenantRepository = new TenantRepository(globalComponentRegistry, false);
+ TenantApplications applicationRepo = TenantApplications.create(globalComponentRegistry, tenantName);
+ SessionFactory sessionFactory = new SessionFactory(globalComponentRegistry,
+ applicationRepo,
+ new HostRegistry<>(),
+ tenantName);
+ sessionRepository = new SessionRepository(tenantName, globalComponentRegistry, sessionFactory,
+ applicationRepo, applicationRepo, new InMemoryFlagSource());
+ }
+
+ @Test
+ public void require_that_sessions_can_be_loaded_from_disk() {
+ assertNotNull(sessionRepository.getSession(1L));
+ assertNotNull(sessionRepository.getSession(2L));
+ assertNotNull(sessionRepository.getSession(3L));
+ assertNull(sessionRepository.getSession(4L));
+ }
+
+ @Test
+ public void require_that_all_sessions_are_deleted() {
+ sessionRepository.close();
+ assertNull(sessionRepository.getSession(1L));
+ assertNull(sessionRepository.getSession(2L));
+ assertNull(sessionRepository.getSession(3L));
+ }
+
+ @Test
+ public void require_that_sessions_belong_to_a_tenant() {
+ // tenant is "default"
+ assertNotNull(sessionRepository.getSession(1L));
+ assertNotNull(sessionRepository.getSession(2L));
+ assertNotNull(sessionRepository.getSession(3L));
+ assertNull(sessionRepository.getSession(4L));
+
+ // tenant is "newTenant"
+ try {
+ setupSessions(TenantName.from("newTenant"), false);
+ } catch (Exception e) {
+ fail();
+ }
+ assertNull(sessionRepository.getSession(1L));
+
+ sessionRepository.addSession(new SessionHandlerTest.MockLocalSession(1L, FilesApplicationPackage.fromFile(testApp)));
+ sessionRepository.addSession(new SessionHandlerTest.MockLocalSession(2L, FilesApplicationPackage.fromFile(testApp)));
+ assertNotNull(sessionRepository.getSession(1L));
+ assertNotNull(sessionRepository.getSession(2L));
+ assertNull(sessionRepository.getSession(3L));
+ }
+
+ private void createSession(long sessionId, boolean wait) {
+ createSession(sessionId, wait, tenantName);
+ }
+
+ private void createSession(long sessionId, boolean wait, TenantName tenantName) {
+ com.yahoo.path.Path sessionsPath = TenantRepository.getSessionsPath(tenantName);
+ SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, sessionsPath.append(String.valueOf(sessionId)));
+ zkc.createNewSession(Instant.now());
+ if (wait) {
+ Curator.CompletionWaiter waiter = zkc.getUploadWaiter();
+ waiter.awaitCompletion(Duration.ofSeconds(120));
+ }
+ }
+
+ @Test
+ public void testInitialize() {
+ createSession(10L, false);
+ createSession(11L, false);
+ assertSessionExists(10L);
+ assertSessionExists(11L);
+ }
+
+ @Test
+ public void testCreateSession() {
+ createSession(12L, true);
+ assertSessionExists(12L);
+ }
+
+ @Test
+ public void testSessionStateChange() throws Exception {
+ long sessionId = 3L;
+ createSession(sessionId, true);
+ assertSessionStatus(sessionId, Session.Status.NEW);
+ assertStatusChange(sessionId, Session.Status.PREPARE);
+ assertStatusChange(sessionId, Session.Status.ACTIVATE);
+
+ com.yahoo.path.Path session = TenantRepository.getSessionsPath(tenantName).append("" + sessionId);
+ curator.delete(session);
+ assertSessionRemoved(sessionId);
+ assertNull(sessionRepository.getRemoteSession(sessionId));
+ }
+
+ // If reading a session throws an exception it should be handled and not prevent other applications
+ // from loading. In this test we just show that we end up with one session in remote session
+ // repo even if it had bad data (by making getSessionIdForApplication() in FailingTenantApplications
+ // throw an exception).
+ @Test
+ public void testBadApplicationRepoOnActivate() {
+ long sessionId = 3L;
+ TenantName mytenant = TenantName.from("mytenant");
+ curator.set(TenantRepository.getApplicationsPath(mytenant).append("mytenant:appX:default"), new byte[0]); // Invalid data
+ tenantRepository.addTenant(mytenant);
+ Tenant tenant = tenantRepository.getTenant(mytenant);
+ curator.create(TenantRepository.getSessionsPath(mytenant));
+ sessionRepository = tenant.getSessionRepo();
+ assertThat(sessionRepository.getRemoteSessions().size(), is(0));
+ createSession(sessionId, true, mytenant);
+ assertThat(sessionRepository.getRemoteSessions().size(), is(1));
+ }
+
+ private void assertStatusChange(long sessionId, Session.Status status) throws Exception {
+ com.yahoo.path.Path statePath = TenantRepository.getSessionsPath(tenantName).append("" + sessionId).append(ConfigCurator.SESSIONSTATE_ZK_SUBPATH);
+ curator.create(statePath);
+ curator.framework().setData().forPath(statePath.getAbsolute(), Utf8.toBytes(status.toString()));
+ assertSessionStatus(sessionId, status);
+ }
+
+ private void assertSessionRemoved(long sessionId) {
+ waitFor(p -> sessionRepository.getRemoteSession(sessionId) == null, sessionId);
+ assertNull(sessionRepository.getRemoteSession(sessionId));
+ }
+
+ private void assertSessionExists(long sessionId) {
+ assertSessionStatus(sessionId, Session.Status.NEW);
+ }
+
+ private void assertSessionStatus(long sessionId, Session.Status status) {
+ waitFor(p -> sessionRepository.getRemoteSession(sessionId) != null &&
+ sessionRepository.getRemoteSession(sessionId).getStatus() == status, sessionId);
+ assertNotNull(sessionRepository.getRemoteSession(sessionId));
+ assertThat(sessionRepository.getRemoteSession(sessionId).getStatus(), is(status));
+ }
+
+ private void waitFor(LongPredicate predicate, long sessionId) {
+ long endTime = System.currentTimeMillis() + 60_000;
+ boolean ok;
+ do {
+ ok = predicate.test(sessionId);
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (System.currentTimeMillis() < endTime && !ok);
+ }
+
+}