aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build_settings.cmake4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java26
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionLoader.java13
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionRepo.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java19
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionFactory.java43
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java21
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java196
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java202
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java10
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java7
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationContentHandlerTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/HostHandlerTest.java2
-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.java8
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java186
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/threadpool/ContainerThreadPool.java87
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/threadpool/ExecutorServiceWrapper.java94
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/threadpool/WorkerCompletionTimingThreadPoolExecutor.java63
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/threadpool/package-info.java8
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/threadpool/ContainerThreadPoolTest.java (renamed from container-core/src/test/java/com/yahoo/container/handler/ThreadPoolProviderTestCase.java)76
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java6
-rw-r--r--default_build_settings.cmake2
-rw-r--r--dist/vespa.spec11
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java7
-rw-r--r--model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java18
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java21
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Status.java14
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringUpgrader.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java38
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java43
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json (renamed from node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-2.json)66
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json90
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json85
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json (renamed from node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports.json)68
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-3.json66
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp15
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.h5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/matchview.cpp5
-rw-r--r--searchlib/src/tests/nativerank/nativerank.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/common/allocatedbitvector.cpp36
-rw-r--r--searchlib/src/vespa/searchlib/common/allocatedbitvector.h2
-rw-r--r--searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.cpp97
-rw-r--r--searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.h40
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java2
59 files changed, 1056 insertions, 837 deletions
diff --git a/build_settings.cmake b/build_settings.cmake
index 0028935ad18..d7dd26f5ee7 100644
--- a/build_settings.cmake
+++ b/build_settings.cmake
@@ -55,7 +55,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" ST
else()
set(CXX_SPECIFIC_WARN_OPTS "-Wsuggest-override -Wnon-virtual-dtor -Wformat-security")
if(VESPA_OS_DISTRO_COMBINED STREQUAL "centos 8" OR
- VESPA_OS_DISTRO_COMBINED STREQUAL "rhel 8.1")
+ (VESPA_OS_DISTRO STREQUAL "rhel" AND
+ VESPA_OS_DISTRO_VERSION VERSION_GREATER_EQUAL "8" AND
+ VESPA_OS_DISTRO_VERSION VERSION_LESS "9"))
set(VESPA_ATOMIC_LIB "")
else()
set(VESPA_ATOMIC_LIB "atomic")
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
index 502cf280e60..afcfe04f4ac 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintenance.java
@@ -34,7 +34,7 @@ public class ConfigServerMaintenance extends AbstractComponent {
// TODO: Disabled until we have application metadata
//tenantsMaintainer = new TenantsMaintainer(applicationRepository, curator, defaults.tenantsMaintainerInterval);
fileDistributionMaintainer = new FileDistributionMaintainer(applicationRepository, curator, defaults.defaultInterval, configserverConfig);
- sessionsMaintainer = new SessionsMaintainer(applicationRepository, curator, defaults.defaultInterval, flagSource);
+ sessionsMaintainer = new SessionsMaintainer(applicationRepository, curator, defaults.defaultInterval);
}
@Override
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
index 250548d5e91..4975b82a801 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
@@ -4,9 +4,6 @@ package com.yahoo.vespa.config.server.maintenance;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.flags.FlagSource;
-import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.LongFlag;
import java.time.Duration;
@@ -19,14 +16,12 @@ import java.time.Duration;
*/
public class SessionsMaintainer extends ConfigServerMaintainer {
private final boolean hostedVespa;
- private final LongFlag expiryTimeFlag;
- SessionsMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval, FlagSource flagSource) {
+ SessionsMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval) {
// Start this maintainer immediately. It frees disk space, so if disk goes full and config server
// restarts this makes sure that cleanup will happen as early as possible
super(applicationRepository, curator, Duration.ZERO, interval);
this.hostedVespa = applicationRepository.configserverConfig().hostedVespa();
- this.expiryTimeFlag = Flags.CONFIGSERVER_SESSIONS_EXPIRY_INTERVAL_IN_DAYS.bindTo(flagSource);
}
@Override
@@ -36,7 +31,7 @@ public class SessionsMaintainer extends ConfigServerMaintainer {
// Expired remote sessions are sessions that belong to an application that have external deployments that
// are no longer active
if (hostedVespa) {
- Duration expiryTime = Duration.ofDays(expiryTimeFlag.value());
+ Duration expiryTime = Duration.ofDays(1);
int deleted = applicationRepository.deleteExpiredRemoteSessions(expiryTime);
log.log(LogLevel.FINE, "Deleted " + deleted + " expired remote sessions, expiry time " + expiryTime);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
index 831f4ba3679..56e32f7d802 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSession.java
@@ -5,7 +5,6 @@ import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
-import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.io.IOUtils;
@@ -18,7 +17,6 @@ import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
import com.yahoo.vespa.config.server.host.HostValidator;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import java.io.File;
@@ -35,7 +33,7 @@ import java.util.logging.Level;
*/
// This is really the store of an application, whether it is active or in an edit session
// TODO: Separate the "application store" and "session" aspects - the latter belongs in the HTTP layer -bratseth
-public class LocalSession extends Session implements Comparable<LocalSession> {
+public class LocalSession extends Session {
protected final ApplicationPackage applicationPackage;
private final TenantApplications applicationRepo;
@@ -118,13 +116,6 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
transaction.add(FileTransaction.from(FileOperations.delete(serverDBSessionDir.getAbsolutePath())));
}
- @Override
- public int compareTo(LocalSession rhs) {
- Long lhsId = getSessionId();
- Long rhsId = rhs.getSessionId();
- return lhsId.compareTo(rhsId);
- }
-
public void waitUntilActivated(TimeoutBudget timeoutBudget) {
zooKeeperClient.getActiveWaiter().awaitCompletion(timeoutBudget.timeLeft());
}
@@ -137,21 +128,6 @@ public class LocalSession extends Session implements Comparable<LocalSession> {
return applicationPackage.getMetaData();
}
- public AllocatedHosts getAllocatedHosts() {
- return zooKeeperClient.getAllocatedHosts();
- }
-
- public TenantName getTenantName() { return tenant; }
-
- @Override
- public String logPre() {
- if (getApplicationId().equals(ApplicationId.defaultId())) {
- return TenantRepository.logPre(getTenant());
- } else {
- return TenantRepository.logPre(getApplicationId());
- }
- }
-
// The rest of this class should be moved elsewhere ...
private static class FileTransaction extends AbstractTransaction {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionLoader.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionLoader.java
deleted file mode 100644
index b82ac22e88e..00000000000
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/LocalSessionLoader.java
+++ /dev/null
@@ -1,13 +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;
-
-/**
- * Interface of a component that is able to load a session given a session id.
- *
- * @author Ulf Lilleengen
- */
-public interface LocalSessionLoader {
-
- LocalSession loadSession(long sessionId);
-
-}
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
index 3f20d3669cb..b6a9c8c0854 100644
--- 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
@@ -46,7 +46,7 @@ public class LocalSessionRepo {
private final TenantFileSystemDirs tenantFileSystemDirs;
private final LongFlag expiryTimeFlag;
- public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry componentRegistry, LocalSessionLoader loader) {
+ public LocalSessionRepo(TenantName tenantName, GlobalComponentRegistry componentRegistry, SessionFactory sessionFactory) {
sessionCache = new SessionCache<>();
this.clock = componentRegistry.getClock();
this.curator = componentRegistry.getCurator();
@@ -54,7 +54,7 @@ public class LocalSessionRepo {
this.zkWatcherExecutor = command -> componentRegistry.getZkWatcherExecutor().execute(tenantName, command);
this.tenantFileSystemDirs = new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName);
this.expiryTimeFlag = Flags.CONFIGSERVER_LOCAL_SESSIONS_EXPIRY_INTERVAL_IN_DAYS.bindTo(componentRegistry.getFlagSource());
- loadSessions(loader);
+ loadSessions(sessionFactory);
}
public synchronized void addSession(LocalSession session) {
@@ -73,14 +73,14 @@ public class LocalSessionRepo {
return sessionCache.getSessions();
}
- private void loadSessions(LocalSessionLoader loader) {
+ private void loadSessions(SessionFactory sessionFactory) {
File[] sessions = tenantFileSystemDirs.sessionsPath().listFiles(sessionApplicationsFilter);
if (sessions == null) {
return;
}
for (File session : sessions) {
try {
- addSession(loader.loadSession(Long.parseLong(session.getName())));
+ addSession(sessionFactory.createSessionFromId(Long.parseLong(session.getName())));
} catch (IllegalArgumentException e) {
log.log(Level.WARNING, "Could not load session '" +
session.getAbsolutePath() + "':" + e.getMessage() + ", skipping it.");
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
index d0082d34114..c1179a2dd17 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java
@@ -4,7 +4,6 @@ package com.yahoo.vespa.config.server.session;
import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.provision.AllocatedHosts;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.lang.SettableOptional;
import com.yahoo.transaction.Transaction;
@@ -12,7 +11,6 @@ import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.ReloadHandler;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.modelfactory.ActivatedModelsBuilder;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import org.apache.zookeeper.KeeperException;
@@ -75,10 +73,6 @@ public class RemoteSession extends Session {
return applicationSet == null ? applicationSet = loadApplication() : applicationSet;
}
- public Session.Status getStatus() {
- return zooKeeperClient.readStatus();
- }
-
public synchronized void deactivate() {
applicationSet = null;
}
@@ -98,15 +92,6 @@ public class RemoteSession extends Session {
log.log(Level.INFO, logPre() + "Session activated: " + getSessionId());
}
- @Override
- public String logPre() {
- if (getApplicationId().equals(ApplicationId.defaultId())) {
- return TenantRepository.logPre(getTenant());
- } else {
- return TenantRepository.logPre(getApplicationId());
- }
- }
-
void confirmUpload() {
Curator.CompletionWaiter waiter = zooKeeperClient.getUploadWaiter();
log.log(Level.FINE, "Notifying upload waiter for session " + getSessionId());
@@ -136,10 +121,6 @@ public class RemoteSession extends Session {
transaction.close();
}
- public AllocatedHosts getAllocatedHosts() {
- return zooKeeperClient.getAllocatedHosts();
- }
-
public ApplicationMetaData getMetaData() {
return zooKeeperClient.loadApplicationPackage().getMetaData();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionFactory.java
deleted file mode 100644
index 0707260dffd..00000000000
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionFactory.java
+++ /dev/null
@@ -1,43 +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.cloud.config.ConfigserverConfig;
-import com.yahoo.vespa.config.server.GlobalComponentRegistry;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
-import com.yahoo.vespa.curator.Curator;
-
-/**
- * @author Ulf Lilleengen
- */
-public class RemoteSessionFactory {
-
- private final GlobalComponentRegistry componentRegistry;
- private final Curator curator;
- private final ConfigCurator configCurator;
- private final Path sessionsPath;
- private final TenantName tenant;
- private final ConfigserverConfig configserverConfig;
-
- public RemoteSessionFactory(GlobalComponentRegistry componentRegistry, TenantName tenant) {
- this.componentRegistry = componentRegistry;
- this.curator = componentRegistry.getCurator();
- this.configCurator = componentRegistry.getConfigCurator();
- this.sessionsPath = TenantRepository.getSessionsPath(tenant);
- this.tenant = tenant;
- this.configserverConfig = componentRegistry.getConfigserverConfig();
- }
-
- public RemoteSession createSession(long sessionId) {
- Path sessionPath = this.sessionsPath.append(String.valueOf(sessionId));
- SessionZooKeeperClient sessionZKClient = new SessionZooKeeperClient(curator,
- configCurator,
- sessionPath,
- configserverConfig.serverId(),
- componentRegistry.getZone().nodeFlavors());
- return new RemoteSession(tenant, sessionId, componentRegistry, sessionZKClient);
- }
-
-}
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 de5af7994ec..316f7f7778d 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
@@ -48,7 +48,7 @@ public class RemoteSessionRepo {
private final Curator curator;
private final Path sessionsPath;
- private final RemoteSessionFactory remoteSessionFactory;
+ private final SessionFactory sessionFactory;
private final Map<Long, RemoteSessionStateWatcher> sessionStateWatchers = new HashMap<>();
private final ReloadHandler reloadHandler;
private final TenantName tenantName;
@@ -59,7 +59,7 @@ public class RemoteSessionRepo {
private final SessionCache<RemoteSession> sessionCache;
public RemoteSessionRepo(GlobalComponentRegistry componentRegistry,
- RemoteSessionFactory remoteSessionFactory,
+ SessionFactory sessionFactory,
ReloadHandler reloadHandler,
TenantName tenantName,
TenantApplications applicationRepo) {
@@ -67,7 +67,7 @@ public class RemoteSessionRepo {
this.curator = componentRegistry.getCurator();
this.sessionsPath = TenantRepository.getSessionsPath(tenantName);
this.applicationRepo = applicationRepo;
- this.remoteSessionFactory = remoteSessionFactory;
+ this.sessionFactory = sessionFactory;
this.reloadHandler = reloadHandler;
this.tenantName = tenantName;
this.metrics = componentRegistry.getMetrics().getOrCreateMetricUpdater(Metrics.createDimensions(tenantName));
@@ -149,7 +149,7 @@ public class RemoteSessionRepo {
*/
private void sessionAdded(long sessionId) {
log.log(Level.FINE, () -> "Adding session to RemoteSessionRepo: " + sessionId);
- RemoteSession session = remoteSessionFactory.createSession(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);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
index 7803bd05e0a..8b078f152f3 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
@@ -21,7 +21,7 @@ import java.util.Optional;
*
* @author Ulf Lilleengen
*/
-public abstract class Session {
+public abstract class Session implements Comparable<Session> {
private final long sessionId;
protected final TenantName tenant;
@@ -64,17 +64,19 @@ public abstract class Session {
return Status.NEW;
}
}
-
- public TenantName getTenant() {
- return tenant;
- }
+
+ public TenantName getTenantName() { return tenant; }
/**
* Helper to provide a log message preamble for code dealing with sessions
* @return log preamble
*/
public String logPre() {
- return TenantRepository.logPre(getTenant());
+ if (getApplicationId().equals(ApplicationId.defaultId())) {
+ return TenantRepository.logPre(getTenantName());
+ } else {
+ return TenantRepository.logPre(getApplicationId());
+ }
}
public Instant getCreateTime() {
@@ -128,4 +130,11 @@ public abstract class Session {
// Note: Assumes monotonically increasing session ids
public boolean isNewerThan(long sessionId) { return getSessionId() > sessionId; }
+ @Override
+ public int compareTo(Session rhs) {
+ Long lhsId = getSessionId();
+ Long rhsId = rhs.getSessionId();
+ return lhsId.compareTo(rhsId);
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java
index 6b51abb7cca..16bb32a19f2 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactory.java
@@ -1,18 +1,76 @@
// 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.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.config.model.application.provider.DeployData;
+import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.NodeFlavors;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.io.IOUtils;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.TimeoutBudget;
+import com.yahoo.vespa.config.server.application.TenantApplications;
+import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
+import com.yahoo.vespa.config.server.host.HostValidator;
+import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
+import com.yahoo.vespa.config.server.zookeeper.SessionCounter;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.flags.BooleanFlag;
+import com.yahoo.vespa.flags.Flags;
import java.io.File;
+import java.time.Clock;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
- * A session factory responsible for creating deploy sessions.
+ * Serves as the factory of sessions. Takes care of copying files to the correct folder and initializing the
+ * session state.
*
* @author Ulf Lilleengen
*/
-public interface SessionFactory {
+public class SessionFactory {
+
+ private static final Logger log = Logger.getLogger(SessionFactory.class.getName());
+ private static final long nonExistingActiveSession = 0;
+
+ private final SessionPreparer sessionPreparer;
+ private final Curator curator;
+ private final ConfigCurator configCurator;
+ private final TenantApplications applicationRepo;
+ private final Path sessionsPath;
+ private final GlobalComponentRegistry componentRegistry;
+ private final HostValidator<ApplicationId> hostRegistry;
+ private final TenantName tenant;
+ private final String serverId;
+ private final Optional<NodeFlavors> nodeFlavors;
+ private final Clock clock;
+ private final BooleanFlag distributeApplicationPackage;
+
+ public SessionFactory(GlobalComponentRegistry globalComponentRegistry,
+ TenantApplications applicationRepo,
+ HostValidator<ApplicationId> hostRegistry,
+ TenantName tenant) {
+ this.hostRegistry = hostRegistry;
+ this.tenant = tenant;
+ this.sessionPreparer = globalComponentRegistry.getSessionPreparer();
+ this.curator = globalComponentRegistry.getCurator();
+ this.configCurator = globalComponentRegistry.getConfigCurator();
+ this.sessionsPath = TenantRepository.getSessionsPath(tenant);
+ this.applicationRepo = applicationRepo;
+ this.componentRegistry = globalComponentRegistry;
+ this.serverId = globalComponentRegistry.getConfigserverConfig().serverId();
+ this.nodeFlavors = globalComponentRegistry.getZone().nodeFlavors();
+ this.clock = globalComponentRegistry.getClock();
+ this.distributeApplicationPackage = Flags.CONFIGSERVER_DISTRIBUTE_APPLICATION_PACKAGE
+ .bindTo(globalComponentRegistry.getFlagSource());
+ }
/**
* Creates a new deployment session from an application package.
@@ -22,7 +80,52 @@ public interface SessionFactory {
* @param timeoutBudget Timeout for creating session and waiting for other servers.
* @return a new session
*/
- LocalSession createSession(File applicationDirectory, ApplicationId applicationId, TimeoutBudget timeoutBudget);
+ public LocalSession createSession(File applicationDirectory, ApplicationId applicationId, TimeoutBudget timeoutBudget) {
+ return create(applicationDirectory, applicationId, nonExistingActiveSession, false, timeoutBudget);
+ }
+
+
+ public RemoteSession createRemoteSession(long sessionId) {
+ Path sessionPath = sessionsPath.append(String.valueOf(sessionId));
+ SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionPath);
+ return new RemoteSession(tenant, sessionId, componentRegistry, sessionZKClient);
+ }
+
+ private void ensureSessionPathDoesNotExist(long sessionId) {
+ Path sessionPath = getSessionPath(sessionId);
+ if (configCurator.exists(sessionPath.getAbsolute())) {
+ throw new IllegalArgumentException("Path " + sessionPath.getAbsolute() + " already exists in ZooKeeper");
+ }
+ }
+
+ private ApplicationPackage createApplication(File userDir,
+ File configApplicationDir,
+ ApplicationId applicationId,
+ long sessionId,
+ long currentlyActiveSessionId,
+ boolean internalRedeploy) {
+ long deployTimestamp = System.currentTimeMillis();
+ String user = System.getenv("USER");
+ if (user == null) {
+ user = "unknown";
+ }
+ DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationId, deployTimestamp, internalRedeploy, sessionId, currentlyActiveSessionId);
+ return FilesApplicationPackage.fromFileWithDeployData(configApplicationDir, deployData);
+ }
+
+ private LocalSession createSessionFromApplication(ApplicationPackage applicationPackage,
+ long sessionId,
+ SessionZooKeeperClient sessionZKClient,
+ TimeoutBudget timeoutBudget,
+ Clock clock) {
+ log.log(Level.FINE, TenantRepository.logPre(tenant) + "Creating session " + sessionId + " in ZooKeeper");
+ sessionZKClient.createNewSession(clock.instant());
+ Curator.CompletionWaiter waiter = sessionZKClient.getUploadWaiter();
+ LocalSession session = new LocalSession(tenant, sessionId, sessionPreparer, applicationPackage, sessionZKClient,
+ getSessionAppDir(sessionId), applicationRepo, hostRegistry);
+ waiter.awaitCompletion(timeoutBudget.timeLeft());
+ return session;
+ }
/**
* Creates a new deployment session from an already existing session.
@@ -33,7 +136,90 @@ public interface SessionFactory {
* @param timeoutBudget timeout for creating session and waiting for other servers.
* @return a new session
*/
- LocalSession createSessionFromExisting(Session existingSession, DeployLogger logger,
- boolean internalRedeploy, TimeoutBudget timeoutBudget);
+ public LocalSession createSessionFromExisting(Session existingSession,
+ DeployLogger logger,
+ boolean internalRedeploy,
+ TimeoutBudget timeoutBudget) {
+ File existingApp = getSessionAppDir(existingSession.getSessionId());
+ ApplicationId existingApplicationId = existingSession.getApplicationId();
+
+ long activeSessionId = getActiveSessionId(existingApplicationId);
+ logger.log(Level.FINE, "Create new session for application id '" + existingApplicationId + "' from existing active session " + activeSessionId);
+ LocalSession session = create(existingApp, existingApplicationId, activeSessionId, internalRedeploy, timeoutBudget);
+ // Note: Needs to be kept in sync with calls in SessionPreparer.writeStateToZooKeeper()
+ session.setApplicationId(existingApplicationId);
+ if (distributeApplicationPackage.value() && existingSession.getApplicationPackageReference() != null) {
+ session.setApplicationPackageReference(existingSession.getApplicationPackageReference());
+ }
+ session.setVespaVersion(existingSession.getVespaVersion());
+ session.setDockerImageRepository(existingSession.getDockerImageRepository());
+ session.setAthenzDomain(existingSession.getAthenzDomain());
+ return session;
+ }
+
+ private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSessionId,
+ boolean internalRedeploy, TimeoutBudget timeoutBudget) {
+ long sessionId = getNextSessionId();
+ try {
+ ensureSessionPathDoesNotExist(sessionId);
+ SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(getSessionPath(sessionId));
+ File userApplicationDir = getSessionAppDir(sessionId);
+ IOUtils.copyDirectory(applicationFile, userApplicationDir);
+ ApplicationPackage applicationPackage = createApplication(applicationFile,
+ userApplicationDir,
+ applicationId,
+ sessionId,
+ currentlyActiveSessionId,
+ internalRedeploy);
+ applicationPackage.writeMetaData();
+ return createSessionFromApplication(applicationPackage, sessionId, sessionZooKeeperClient, timeoutBudget, clock);
+ } catch (Exception e) {
+ throw new RuntimeException("Error creating session " + sessionId, e);
+ }
+ }
+
+ /**
+ * Returns a new session instance for the given session id.
+ */
+ LocalSession createSessionFromId(long sessionId) {
+ File sessionDir = getAndValidateExistingSessionAppDir(sessionId);
+ ApplicationPackage applicationPackage = FilesApplicationPackage.fromFile(sessionDir);
+ Path sessionIdPath = sessionsPath.append(String.valueOf(sessionId));
+ SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionIdPath);
+ return new LocalSession(tenant, sessionId, sessionPreparer, applicationPackage, sessionZKClient,
+ getSessionAppDir(sessionId), applicationRepo, hostRegistry);
+ }
+
+ private long getActiveSessionId(ApplicationId applicationId) {
+ List<ApplicationId> applicationIds = applicationRepo.activeApplications();
+ if (applicationIds.contains(applicationId)) {
+ return applicationRepo.requireActiveSessionOf(applicationId);
+ }
+ return nonExistingActiveSession;
+ }
+
+ private long getNextSessionId() {
+ return new SessionCounter(componentRegistry.getConfigCurator(), tenant).nextSessionId();
+ }
+
+ private Path getSessionPath(long sessionId) {
+ return sessionsPath.append(String.valueOf(sessionId));
+ }
+
+ private SessionZooKeeperClient createSessionZooKeeperClient(Path sessionPath) {
+ return new SessionZooKeeperClient(curator, configCurator, sessionPath, serverId, nodeFlavors);
+ }
+
+ private File getAndValidateExistingSessionAppDir(long sessionId) {
+ File appDir = getSessionAppDir(sessionId);
+ if (!appDir.exists() || !appDir.isDirectory()) {
+ throw new IllegalArgumentException("Unable to find correct application directory for session " + sessionId);
+ }
+ return appDir;
+ }
+
+ private File getSessionAppDir(long sessionId) {
+ return new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenant).getUserApplicationDir(sessionId);
+ }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java
deleted file mode 100644
index 558b17131a3..00000000000
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionFactoryImpl.java
+++ /dev/null
@@ -1,202 +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.application.api.ApplicationPackage;
-import com.yahoo.config.application.api.DeployLogger;
-import com.yahoo.config.model.application.provider.*;
-import com.yahoo.config.provision.NodeFlavors;
-import com.yahoo.io.IOUtils;
-import java.util.logging.Level;
-import com.yahoo.path.Path;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.vespa.config.server.GlobalComponentRegistry;
-import com.yahoo.vespa.config.server.TimeoutBudget;
-import com.yahoo.vespa.config.server.application.TenantApplications;
-import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
-import com.yahoo.vespa.config.server.host.HostValidator;
-import com.yahoo.vespa.config.server.tenant.TenantRepository;
-import com.yahoo.vespa.config.server.zookeeper.SessionCounter;
-import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.Flags;
-
-import java.io.File;
-import java.time.Clock;
-import java.util.List;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-/**
- * Serves as the factory of sessions. Takes care of copying files to the correct folder and initializing the
- * session state.
- *
- * @author Ulf Lilleengen
- */
-public class SessionFactoryImpl implements SessionFactory, LocalSessionLoader {
-
- private static final Logger log = Logger.getLogger(SessionFactoryImpl.class.getName());
- private static final long nonExistingActiveSession = 0;
-
- private final SessionPreparer sessionPreparer;
- private final Curator curator;
- private final ConfigCurator configCurator;
- private final TenantApplications applicationRepo;
- private final Path sessionsPath;
- private final GlobalComponentRegistry componentRegistry;
- private final HostValidator<ApplicationId> hostRegistry;
- private final TenantName tenant;
- private final String serverId;
- private final Optional<NodeFlavors> nodeFlavors;
- private final Clock clock;
- private final BooleanFlag distributeApplicationPackage;
-
- public SessionFactoryImpl(GlobalComponentRegistry globalComponentRegistry,
- TenantApplications applicationRepo,
- HostValidator<ApplicationId> hostRegistry,
- TenantName tenant) {
- this.hostRegistry = hostRegistry;
- this.tenant = tenant;
- this.sessionPreparer = globalComponentRegistry.getSessionPreparer();
- this.curator = globalComponentRegistry.getCurator();
- this.configCurator = globalComponentRegistry.getConfigCurator();
- this.sessionsPath = TenantRepository.getSessionsPath(tenant);
- this.applicationRepo = applicationRepo;
- this.componentRegistry = globalComponentRegistry;
- this.serverId = globalComponentRegistry.getConfigserverConfig().serverId();
- this.nodeFlavors = globalComponentRegistry.getZone().nodeFlavors();
- this.clock = globalComponentRegistry.getClock();
- this.distributeApplicationPackage = Flags.CONFIGSERVER_DISTRIBUTE_APPLICATION_PACKAGE
- .bindTo(globalComponentRegistry.getFlagSource());
- }
-
- /** Create a session for a true application package change */
- @Override
- public LocalSession createSession(File applicationFile, ApplicationId applicationId, TimeoutBudget timeoutBudget) {
- return create(applicationFile, applicationId, nonExistingActiveSession, false, timeoutBudget);
- }
-
- private void ensureSessionPathDoesNotExist(long sessionId) {
- Path sessionPath = getSessionPath(sessionId);
- if (configCurator.exists(sessionPath.getAbsolute())) {
- throw new IllegalArgumentException("Path " + sessionPath.getAbsolute() + " already exists in ZooKeeper");
- }
- }
-
- private ApplicationPackage createApplication(File userDir,
- File configApplicationDir,
- ApplicationId applicationId,
- long sessionId,
- long currentlyActiveSessionId,
- boolean internalRedeploy) {
- long deployTimestamp = System.currentTimeMillis();
- String user = System.getenv("USER");
- if (user == null) {
- user = "unknown";
- }
- DeployData deployData = new DeployData(user, userDir.getAbsolutePath(), applicationId, deployTimestamp, internalRedeploy, sessionId, currentlyActiveSessionId);
- return FilesApplicationPackage.fromFileWithDeployData(configApplicationDir, deployData);
- }
-
- private LocalSession createSessionFromApplication(ApplicationPackage applicationPackage,
- long sessionId,
- SessionZooKeeperClient sessionZKClient,
- TimeoutBudget timeoutBudget,
- Clock clock) {
- log.log(Level.FINE, TenantRepository.logPre(tenant) + "Creating session " + sessionId + " in ZooKeeper");
- sessionZKClient.createNewSession(clock.instant());
- Curator.CompletionWaiter waiter = sessionZKClient.getUploadWaiter();
- LocalSession session = new LocalSession(tenant, sessionId, sessionPreparer, applicationPackage, sessionZKClient,
- getSessionAppDir(sessionId), applicationRepo, hostRegistry);
- waiter.awaitCompletion(timeoutBudget.timeLeft());
- return session;
- }
-
- @Override
- public LocalSession createSessionFromExisting(Session existingSession,
- DeployLogger logger,
- boolean internalRedeploy,
- TimeoutBudget timeoutBudget) {
- File existingApp = getSessionAppDir(existingSession.getSessionId());
- ApplicationId existingApplicationId = existingSession.getApplicationId();
-
- long activeSessionId = getActiveSessionId(existingApplicationId);
- logger.log(Level.FINE, "Create new session for application id '" + existingApplicationId + "' from existing active session " + activeSessionId);
- LocalSession session = create(existingApp, existingApplicationId, activeSessionId, internalRedeploy, timeoutBudget);
- // Note: Needs to be kept in sync with calls in SessionPreparer.writeStateToZooKeeper()
- session.setApplicationId(existingApplicationId);
- if (distributeApplicationPackage.value() && existingSession.getApplicationPackageReference() != null) {
- session.setApplicationPackageReference(existingSession.getApplicationPackageReference());
- }
- session.setVespaVersion(existingSession.getVespaVersion());
- session.setDockerImageRepository(existingSession.getDockerImageRepository());
- session.setAthenzDomain(existingSession.getAthenzDomain());
- return session;
- }
-
- private LocalSession create(File applicationFile, ApplicationId applicationId, long currentlyActiveSessionId,
- boolean internalRedeploy, TimeoutBudget timeoutBudget) {
- long sessionId = getNextSessionId();
- try {
- ensureSessionPathDoesNotExist(sessionId);
- SessionZooKeeperClient sessionZooKeeperClient =
- new SessionZooKeeperClient(curator, configCurator, getSessionPath(sessionId), serverId, nodeFlavors);
- File userApplicationDir = getSessionAppDir(sessionId);
- IOUtils.copyDirectory(applicationFile, userApplicationDir);
- ApplicationPackage applicationPackage = createApplication(applicationFile,
- userApplicationDir,
- applicationId,
- sessionId,
- currentlyActiveSessionId,
- internalRedeploy);
- applicationPackage.writeMetaData();
- return createSessionFromApplication(applicationPackage, sessionId, sessionZooKeeperClient, timeoutBudget, clock);
- } catch (Exception e) {
- throw new RuntimeException("Error creating session " + sessionId, e);
- }
- }
-
- private File getAndValidateExistingSessionAppDir(long sessionId) {
- File appDir = getSessionAppDir(sessionId);
- if (!appDir.exists() || !appDir.isDirectory()) {
- throw new IllegalArgumentException("Unable to find correct application directory for session " + sessionId);
- }
- return appDir;
- }
-
- private File getSessionAppDir(long sessionId) {
- return new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenant).getUserApplicationDir(sessionId);
- }
-
- @Override
- public LocalSession loadSession(long sessionId) {
- File sessionDir = getAndValidateExistingSessionAppDir(sessionId);
- ApplicationPackage applicationPackage = FilesApplicationPackage.fromFile(sessionDir);
- Path sessionIdPath = sessionsPath.append(String.valueOf(sessionId));
- SessionZooKeeperClient sessionZKClient = new SessionZooKeeperClient(curator,
- configCurator,
- sessionIdPath,
- serverId,
- nodeFlavors);
- return new LocalSession(tenant, sessionId, sessionPreparer, applicationPackage, sessionZKClient,
- getSessionAppDir(sessionId), applicationRepo, hostRegistry);
- }
-
- private long getActiveSessionId(ApplicationId applicationId) {
- List<ApplicationId> applicationIds = applicationRepo.activeApplications();
- if (applicationIds.contains(applicationId)) {
- return applicationRepo.requireActiveSessionOf(applicationId);
- }
- return nonExistingActiveSession;
- }
-
- long getNextSessionId() {
- return new SessionCounter(componentRegistry.getConfigCurator(), tenant).nextSessionId();
- }
-
- Path getSessionPath(long sessionId) {
- return sessionsPath.append(String.valueOf(sessionId));
- }
-
-}
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 457d5538d5c..43b25826507 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
@@ -15,12 +15,9 @@ import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.host.HostValidator;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.rpc.ConfigResponseFactory;
-import com.yahoo.vespa.config.server.session.LocalSessionLoader;
import com.yahoo.vespa.config.server.session.LocalSessionRepo;
-import com.yahoo.vespa.config.server.session.RemoteSessionFactory;
import com.yahoo.vespa.config.server.session.RemoteSessionRepo;
import com.yahoo.vespa.config.server.session.SessionFactory;
-import com.yahoo.vespa.config.server.session.SessionFactoryImpl;
import com.yahoo.vespa.curator.Curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
@@ -231,11 +228,10 @@ public class TenantRepository {
reloadHandler,
tenantName);
- SessionFactory sessionFactory = new SessionFactoryImpl(globalComponentRegistry, applicationRepo, hostValidator, tenantName);
- // TODO: Fix the casting
- LocalSessionRepo localSessionRepo = new LocalSessionRepo(tenantName, globalComponentRegistry, (LocalSessionLoader) sessionFactory);
+ SessionFactory sessionFactory = new SessionFactory(globalComponentRegistry, applicationRepo, hostValidator, tenantName);
+ LocalSessionRepo localSessionRepo = new LocalSessionRepo(tenantName, globalComponentRegistry, sessionFactory);
RemoteSessionRepo remoteSessionRepo = new RemoteSessionRepo(globalComponentRegistry,
- new RemoteSessionFactory(globalComponentRegistry, tenantName),
+ sessionFactory,
reloadHandler,
tenantName,
applicationRepo);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
index b939f1ab4c5..91a40bd6083 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java
@@ -25,6 +25,7 @@ import com.yahoo.vespa.config.server.session.DummyTransaction;
import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.MockSessionZKClient;
import com.yahoo.vespa.config.server.session.PrepareParams;
+import com.yahoo.vespa.config.server.session.RemoteSession;
import com.yahoo.vespa.config.server.session.Session;
import java.io.ByteArrayOutputStream;
@@ -86,7 +87,7 @@ public class SessionHandlerTest {
return baos.toString(StandardCharsets.UTF_8);
}
- public static class MockSession extends LocalSession {
+ public static class MockLocalSession extends LocalSession {
public Session.Status status;
private ConfigChangeActions actions = new ConfigChangeActions();
@@ -94,11 +95,11 @@ public class SessionHandlerTest {
private ApplicationId applicationId;
private Optional<DockerImage> dockerImageRepository;
- public MockSession(long sessionId, ApplicationPackage app) {
+ public MockLocalSession(long sessionId, ApplicationPackage app) {
super(TenantName.defaultName(), sessionId, null, app, new MockSessionZKClient(app), null, null, new HostRegistry<>());
}
- public MockSession(long sessionId, ApplicationPackage app, ApplicationId applicationId) {
+ public MockLocalSession(long sessionId, ApplicationPackage app, ApplicationId applicationId) {
this(sessionId, app);
this.applicationId = applicationId;
}
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 09e84deb1a5..078dc47af51 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
@@ -44,7 +44,7 @@ public class ApplicationContentHandlerTest extends ContentHandlerTestBase {
private ApplicationId idTenant2 = new ApplicationId.Builder()
.tenant(tenantName2)
.applicationName("foo").instanceName("quux").build();
- private MockSession session2;
+ private MockLocalSession session2;
@Before
public void setupHandler() {
@@ -52,13 +52,13 @@ public class ApplicationContentHandlerTest extends ContentHandlerTestBase {
tenantRepository.addTenant(tenantName1);
tenantRepository.addTenant(tenantName2);
- session2 = new MockSession(2, FilesApplicationPackage.fromFile(new File("src/test/apps/content")));
+ session2 = new MockLocalSession(2, FilesApplicationPackage.fromFile(new File("src/test/apps/content")));
Tenant tenant1 = tenantRepository.getTenant(tenantName1);
tenant1.getLocalSessionRepo().addSession(session2);
tenant1.getApplicationRepo().createApplication(idTenant1);
tenant1.getApplicationRepo().createPutTransaction(idTenant1, 2).commit();
- MockSession session3 = new MockSession(3, FilesApplicationPackage.fromFile(new File("src/test/apps/content2")));
+ MockLocalSession session3 = new MockLocalSession(3, FilesApplicationPackage.fromFile(new File("src/test/apps/content2")));
Tenant tenant2 = tenantRepository.getTenant(tenantName2);
tenant2.getLocalSessionRepo().addSession(session3);
tenant2.getApplicationRepo().createApplication(idTenant2);
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 28fe4a7aa2c..2eaa5d75ba7 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,7 +52,7 @@ public class HostHandlerTest {
tenant.getApplicationRepo().createApplication(applicationId);
tenant.getApplicationRepo().createPutTransaction(applicationId, sessionId).commit();
ApplicationPackage app = FilesApplicationPackage.fromFile(testApp);
- tenant.getLocalSessionRepo().addSession(new SessionHandlerTest.MockSession(sessionId, app, applicationId));
+ tenant.getLocalSessionRepo().addSession(new SessionHandlerTest.MockLocalSession(sessionId, app, applicationId));
TestComponentRegistry componentRegistry = new TestComponentRegistry.Builder()
.modelFactoryRegistry(new ModelFactoryRegistry(Collections.singletonList(new VespaModelFactory(new NullConfigModelRegistry()))))
.build();
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 bc1650ce923..88bf6fb7172 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 MockSession(1L, FilesApplicationPackage.fromFile(createTestApp())));
+ tenantRepository.getTenant(tenant).getLocalSessionRepo().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
index 69132186abc..2c119a119b6 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
@@ -58,11 +58,11 @@ public class LocalSessionRepoTest {
.sessionLifetime(5)
.build())
.build();
- LocalSessionLoader loader = new SessionFactoryImpl(globalComponentRegistry,
+ SessionFactory sessionFactory = new SessionFactory(globalComponentRegistry,
TenantApplications.create(globalComponentRegistry, new MockReloadHandler(), tenantName),
new HostRegistry<>(),
tenantName);
- repo = new LocalSessionRepo(tenantName, globalComponentRegistry, loader);
+ repo = new LocalSessionRepo(tenantName, globalComponentRegistry, sessionFactory);
}
@Test
@@ -97,8 +97,8 @@ public class LocalSessionRepoTest {
}
assertNull(repo.getSession(1L));
- repo.addSession(new SessionHandlerTest.MockSession(1L, FilesApplicationPackage.fromFile(testApp)));
- repo.addSession(new SessionHandlerTest.MockSession(2L, FilesApplicationPackage.fromFile(testApp)));
+ 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/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java b/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
index 0e786cfbc8f..958e958456c 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/ThreadPoolProvider.java
@@ -1,26 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler;
-import com.google.common.util.concurrent.ForwardingExecutorService;
-import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.concurrent.ThreadFactoryFactory;
import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.handler.threadpool.ContainerThreadPool;
import com.yahoo.container.protect.ProcessTerminator;
import com.yahoo.jdisc.Metric;
-import java.util.Map;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
/**
* A configurable thread pool provider. This provides the worker threads used for normal request processing.
@@ -32,40 +19,14 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class ThreadPoolProvider extends AbstractComponent implements Provider<Executor> {
- private final ExecutorServiceWrapper threadpool;
+ private final ContainerThreadPool threadpool;
- private static BlockingQueue<Runnable> createQ(int queueSize, int maxThreads) {
- return (queueSize == 0)
- ? new SynchronousQueue<>(false)
- : (queueSize < 0)
- ? new ArrayBlockingQueue<>(maxThreads*4)
- : new ArrayBlockingQueue<>(queueSize);
- }
-
- private static int computeThreadPoolSize(int maxNumThreads) {
- return (maxNumThreads <= 0)
- ? Runtime.getRuntime().availableProcessors() * 4
- : maxNumThreads;
- }
- @Inject
public ThreadPoolProvider(ThreadpoolConfig threadpoolConfig, Metric metric) {
- this(threadpoolConfig, metric, new ProcessTerminator());
+ this.threadpool = new ContainerThreadPool(threadpoolConfig, metric);
}
public ThreadPoolProvider(ThreadpoolConfig threadpoolConfig, Metric metric, ProcessTerminator processTerminator) {
- int maxNumThreads = computeThreadPoolSize(threadpoolConfig.maxthreads());
- WorkerCompletionTimingThreadPoolExecutor executor =
- new WorkerCompletionTimingThreadPoolExecutor(maxNumThreads, maxNumThreads,
- 0L, TimeUnit.SECONDS,
- createQ(threadpoolConfig.queueSize(), maxNumThreads),
- ThreadFactoryFactory.getThreadFactory("threadpool"),
- metric);
- // Prestart needed, if not all threads will be created by the fist N tasks and hence they might also
- // get the dreaded thread locals initialized even if they will never run.
- // That counters what we we want to achieve with the Q that will prefer thread locality.
- executor.prestartAllCoreThreads();
- threadpool = new ExecutorServiceWrapper(executor, metric, processTerminator,
- threadpoolConfig.maxThreadExecutionTimeSeconds() * 1000L);
+ this.threadpool = new ContainerThreadPool(threadpoolConfig, metric, processTerminator);
}
/**
@@ -75,7 +36,7 @@ public class ThreadPoolProvider extends AbstractComponent implements Provider<Ex
* @return a possibly shared executor
*/
@Override
- public Executor get() { return threadpool; }
+ public Executor get() { return threadpool.executor(); }
/**
* Shutdown the thread pool, give a grace period of 1 second before forcibly
@@ -83,142 +44,7 @@ public class ThreadPoolProvider extends AbstractComponent implements Provider<Ex
*/
@Override
public void deconstruct() {
- boolean terminated;
-
- super.deconstruct();
- threadpool.shutdown();
- try {
- terminated = threadpool.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- if (!terminated) {
- threadpool.shutdownNow();
- }
- }
-
- /**
- * A service executor wrapper which emits metrics and
- * shuts down the vm when no workers are available for too long to avoid containers lingering in a blocked state.
- * Package private for testing
- */
- final static class ExecutorServiceWrapper extends ForwardingExecutorService {
-
- private final WorkerCompletionTimingThreadPoolExecutor wrapped;
- private final Metric metric;
- private final ProcessTerminator processTerminator;
- private final long maxThreadExecutionTimeMillis;
- private final Thread metricReporter;
- private final AtomicBoolean closed = new AtomicBoolean(false);
-
- private ExecutorServiceWrapper(WorkerCompletionTimingThreadPoolExecutor wrapped,
- Metric metric, ProcessTerminator processTerminator,
- long maxThreadExecutionTimeMillis) {
- this.wrapped = wrapped;
- this.metric = metric;
- this.processTerminator = processTerminator;
- this.maxThreadExecutionTimeMillis = maxThreadExecutionTimeMillis;
-
- metric.set(MetricNames.THREAD_POOL_SIZE, wrapped.getPoolSize(), null);
- metric.set(MetricNames.ACTIVE_THREADS, wrapped.getActiveCount(), null);
- metric.add(MetricNames.REJECTED_REQUEST, 0, null);
- metricReporter = new Thread(this::reportMetrics);
- metricReporter.setDaemon(true);
- metricReporter.start();
- }
-
- private final void reportMetrics() {
- try {
- while (!closed.get()) {
- metric.set(MetricNames.THREAD_POOL_SIZE, wrapped.getPoolSize(), null);
- metric.set(MetricNames.ACTIVE_THREADS, wrapped.getActiveCount(), null);
- Thread.sleep(100);
- }
- } catch (InterruptedException e) { }
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- closed.set(true);
- }
-
- /**
- * Tracks all instances of RejectedExecutionException.
- * ThreadPoolProvider returns an executor, so external uses will not
- * have access to the methods declared by ExecutorService.
- * (execute(Runnable) is declared by Executor.)
- */
- @Override
- public void execute(Runnable command) {
- try {
- super.execute(command);
- } catch (RejectedExecutionException e) {
- metric.add(MetricNames.REJECTED_REQUEST, 1, null);
- long timeSinceLastReturnedThreadMillis = System.currentTimeMillis() - wrapped.lastThreadAssignmentTimeMillis;
- if (timeSinceLastReturnedThreadMillis > maxThreadExecutionTimeMillis)
- processTerminator.logAndDie("No worker threads have been available for " +
- timeSinceLastReturnedThreadMillis + " ms. Shutting down.", true);
- throw e;
- }
- }
-
- @Override
- protected ExecutorService delegate() { return wrapped; }
-
- private static final class MetricNames {
- private static final String REJECTED_REQUEST = "serverRejectedRequests";
- private static final String THREAD_POOL_SIZE = "serverThreadPoolSize";
- private static final String ACTIVE_THREADS = "serverActiveThreads";
- }
-
- }
-
- /**
- * A thread pool executor which maintains the last time a worker completed
- * package private for testing
- **/
- final static class WorkerCompletionTimingThreadPoolExecutor extends ThreadPoolExecutor {
-
- private static final String UNHANDLED_EXCEPTIONS_METRIC = "jdisc.thread_pool.unhandled_exceptions";
-
- volatile long lastThreadAssignmentTimeMillis = System.currentTimeMillis();
- private final AtomicLong startedCount = new AtomicLong(0);
- private final AtomicLong completedCount = new AtomicLong(0);
- private final Metric metric;
-
- public WorkerCompletionTimingThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- Metric metric) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
- this.metric = metric;
- }
-
- @Override
- protected void beforeExecute(Thread t, Runnable r) {
- super.beforeExecute(t, r);
- lastThreadAssignmentTimeMillis = System.currentTimeMillis();
- startedCount.incrementAndGet();
- }
-
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
- super.afterExecute(r, t);
- completedCount.incrementAndGet();
- if (t != null) {
- metric.add(UNHANDLED_EXCEPTIONS_METRIC, 1L, metric.createContext(Map.of("exception", t.getClass().getSimpleName())));
- }
- }
-
- @Override
- public int getActiveCount() {
- return (int)(startedCount.get() - completedCount.get());
- }
+ threadpool.deconstruct();
}
}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/threadpool/ContainerThreadPool.java b/container-core/src/main/java/com/yahoo/container/handler/threadpool/ContainerThreadPool.java
new file mode 100644
index 00000000000..0f3be65f85f
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/handler/threadpool/ContainerThreadPool.java
@@ -0,0 +1,87 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.handler.threadpool;
+
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.container.handler.ThreadpoolConfig;
+import com.yahoo.container.protect.ProcessTerminator;
+import com.yahoo.jdisc.Metric;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A configurable thread pool. This provides the worker threads used for normal request processing.
+ *
+ * @author Steinar Knutsen
+ * @author baldersheim
+ * @author bratseth
+ * @author bjorncs
+ */
+public class ContainerThreadPool extends AbstractComponent implements AutoCloseable {
+
+ private final ExecutorServiceWrapper threadpool;
+
+ @Inject
+ public ContainerThreadPool(ThreadpoolConfig config, Metric metric) {
+ this(config, metric, new ProcessTerminator());
+ }
+
+ public ContainerThreadPool(ThreadpoolConfig threadpoolConfig, Metric metric, ProcessTerminator processTerminator) {
+ int maxNumThreads = computeThreadPoolSize(threadpoolConfig.maxthreads());
+ WorkerCompletionTimingThreadPoolExecutor executor =
+ new WorkerCompletionTimingThreadPoolExecutor(maxNumThreads, maxNumThreads,
+ 0L, TimeUnit.SECONDS,
+ createQ(threadpoolConfig.queueSize(), maxNumThreads),
+ ThreadFactoryFactory.getThreadFactory("threadpool"),
+ metric);
+ // Prestart needed, if not all threads will be created by the fist N tasks and hence they might also
+ // get the dreaded thread locals initialized even if they will never run.
+ // That counters what we we want to achieve with the Q that will prefer thread locality.
+ executor.prestartAllCoreThreads();
+ threadpool = new ExecutorServiceWrapper(executor, metric, processTerminator,
+ threadpoolConfig.maxThreadExecutionTimeSeconds() * 1000L);
+ }
+
+ public Executor executor() { return threadpool; }
+ @Override public void deconstruct() { closeInternal(); }
+ @Override public void close() { closeInternal(); }
+
+ /**
+ * Shutdown the thread pool, give a grace period of 1 second before forcibly
+ * shutting down all worker threads.
+ */
+ private void closeInternal() {
+ boolean terminated;
+
+ super.deconstruct();
+ threadpool.shutdown();
+ try {
+ terminated = threadpool.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ if (!terminated) {
+ threadpool.shutdownNow();
+ }
+ }
+
+ private static BlockingQueue<Runnable> createQ(int queueSize, int maxThreads) {
+ return (queueSize == 0)
+ ? new SynchronousQueue<>(false)
+ : (queueSize < 0)
+ ? new ArrayBlockingQueue<>(maxThreads*4)
+ : new ArrayBlockingQueue<>(queueSize);
+ }
+
+ private static int computeThreadPoolSize(int maxNumThreads) {
+ return (maxNumThreads <= 0)
+ ? Runtime.getRuntime().availableProcessors() * 4
+ : maxNumThreads;
+ }
+}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/threadpool/ExecutorServiceWrapper.java b/container-core/src/main/java/com/yahoo/container/handler/threadpool/ExecutorServiceWrapper.java
new file mode 100644
index 00000000000..f7b0a22120a
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/handler/threadpool/ExecutorServiceWrapper.java
@@ -0,0 +1,94 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.handler.threadpool;
+
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.yahoo.container.protect.ProcessTerminator;
+import com.yahoo.jdisc.Metric;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A service executor wrapper which emits metrics and
+ * shuts down the vm when no workers are available for too long to avoid containers lingering in a blocked state.
+ * Package private for testing
+ *
+ * @author Steinar Knutsen
+ * @author baldersheim
+ * @author bratseth
+ */
+class ExecutorServiceWrapper extends ForwardingExecutorService {
+
+ private final WorkerCompletionTimingThreadPoolExecutor wrapped;
+ private final Metric metric;
+ private final ProcessTerminator processTerminator;
+ private final long maxThreadExecutionTimeMillis;
+ private final Thread metricReporter;
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+
+ ExecutorServiceWrapper(
+ WorkerCompletionTimingThreadPoolExecutor wrapped,
+ Metric metric, ProcessTerminator processTerminator,
+ long maxThreadExecutionTimeMillis) {
+ this.wrapped = wrapped;
+ this.metric = metric;
+ this.processTerminator = processTerminator;
+ this.maxThreadExecutionTimeMillis = maxThreadExecutionTimeMillis;
+
+ metric.set(MetricNames.THREAD_POOL_SIZE, wrapped.getPoolSize(), null);
+ metric.set(MetricNames.ACTIVE_THREADS, wrapped.getActiveCount(), null);
+ metric.add(MetricNames.REJECTED_REQUEST, 0, null);
+ metricReporter = new Thread(this::reportMetrics);
+ metricReporter.setDaemon(true);
+ metricReporter.start();
+ }
+
+ private final void reportMetrics() {
+ try {
+ while (!closed.get()) {
+ metric.set(MetricNames.THREAD_POOL_SIZE, wrapped.getPoolSize(), null);
+ metric.set(MetricNames.ACTIVE_THREADS, wrapped.getActiveCount(), null);
+ Thread.sleep(100);
+ }
+ } catch (InterruptedException e) { }
+ }
+
+ @Override
+ public void shutdown() {
+ super.shutdown();
+ closed.set(true);
+ }
+
+ /**
+ * Tracks all instances of {@link RejectedExecutionException}.
+ * {@link ContainerThreadPool} returns an executor, so external uses will not
+ * have access to the methods declared by {@link ExecutorService}.
+ * ({@link Executor#execute(Runnable)} is declared by {@link Executor}.)
+ */
+ @Override
+ public void execute(Runnable command) {
+ try {
+ super.execute(command);
+ } catch (RejectedExecutionException e) {
+ metric.add(MetricNames.REJECTED_REQUEST, 1, null);
+ long timeSinceLastReturnedThreadMillis = System.currentTimeMillis() - wrapped.lastThreadAssignmentTimeMillis;
+ if (timeSinceLastReturnedThreadMillis > maxThreadExecutionTimeMillis)
+ processTerminator.logAndDie("No worker threads have been available for " +
+ timeSinceLastReturnedThreadMillis + " ms. Shutting down.", true);
+ throw e;
+ }
+ }
+
+ @Override
+ protected ExecutorService delegate() { return wrapped; }
+
+ private static final class MetricNames {
+ private static final String REJECTED_REQUEST = "serverRejectedRequests";
+ private static final String THREAD_POOL_SIZE = "serverThreadPoolSize";
+ private static final String ACTIVE_THREADS = "serverActiveThreads";
+ }
+
+}
+
diff --git a/container-core/src/main/java/com/yahoo/container/handler/threadpool/WorkerCompletionTimingThreadPoolExecutor.java b/container-core/src/main/java/com/yahoo/container/handler/threadpool/WorkerCompletionTimingThreadPoolExecutor.java
new file mode 100644
index 00000000000..9742e7ecfc3
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/handler/threadpool/WorkerCompletionTimingThreadPoolExecutor.java
@@ -0,0 +1,63 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.handler.threadpool;
+
+import com.yahoo.jdisc.Metric;
+
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A thread pool executor which maintains the last time a worker completed
+ * package private for testing
+ *
+ * @author Steinar Knutsen
+ * @author baldersheim
+ * @author bratseth
+ */
+class WorkerCompletionTimingThreadPoolExecutor extends ThreadPoolExecutor {
+
+ private static final String UNHANDLED_EXCEPTIONS_METRIC = "jdisc.thread_pool.unhandled_exceptions";
+
+ volatile long lastThreadAssignmentTimeMillis = System.currentTimeMillis();
+ private final AtomicLong startedCount = new AtomicLong(0);
+ private final AtomicLong completedCount = new AtomicLong(0);
+ private final Metric metric;
+
+ WorkerCompletionTimingThreadPoolExecutor(
+ int corePoolSize,
+ int maximumPoolSize,
+ long keepAliveTime,
+ TimeUnit unit,
+ BlockingQueue<Runnable> workQueue,
+ ThreadFactory threadFactory,
+ Metric metric) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+ this.metric = metric;
+ }
+
+ @Override
+ protected void beforeExecute(Thread t, Runnable r) {
+ super.beforeExecute(t, r);
+ lastThreadAssignmentTimeMillis = System.currentTimeMillis();
+ startedCount.incrementAndGet();
+ }
+
+ @Override
+ protected void afterExecute(Runnable r, Throwable t) {
+ super.afterExecute(r, t);
+ completedCount.incrementAndGet();
+ if (t != null) {
+ metric.add(UNHANDLED_EXCEPTIONS_METRIC, 1L, metric.createContext(Map.of("exception", t.getClass().getSimpleName())));
+ }
+ }
+
+ @Override
+ public int getActiveCount() {
+ return (int)(startedCount.get() - completedCount.get());
+ }
+}
+
diff --git a/container-core/src/main/java/com/yahoo/container/handler/threadpool/package-info.java b/container-core/src/main/java/com/yahoo/container/handler/threadpool/package-info.java
new file mode 100644
index 00000000000..6a94cea49da
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/handler/threadpool/package-info.java
@@ -0,0 +1,8 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.container.handler.threadpool;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/container-core/src/test/java/com/yahoo/container/handler/ThreadPoolProviderTestCase.java b/container-core/src/test/java/com/yahoo/container/handler/threadpool/ContainerThreadPoolTest.java
index 761ed40763c..7998bbc4872 100644
--- a/container-core/src/test/java/com/yahoo/container/handler/ThreadPoolProviderTestCase.java
+++ b/container-core/src/test/java/com/yahoo/container/handler/threadpool/ContainerThreadPoolTest.java
@@ -1,37 +1,33 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.handler;
-
-import static org.junit.Assert.fail;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ThreadPoolExecutor;
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.handler.threadpool;
+import com.yahoo.collections.Tuple2;
+import com.yahoo.concurrent.Receiver;
+import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.container.protect.ProcessTerminator;
+import com.yahoo.jdisc.Metric;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
-import com.yahoo.concurrent.Receiver;
-import com.yahoo.concurrent.Receiver.MessageState;
-import com.yahoo.collections.Tuple2;
-import com.yahoo.jdisc.Metric;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
/**
- * Check threadpool provider accepts tasks and shuts down properly.
- *
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @author Steinar Knutsen
+ * @author bjorncs
*/
-public class ThreadPoolProviderTestCase {
-
+public class ContainerThreadPoolTest {
@Test
- public final void testThreadPoolProvider() throws InterruptedException {
+ public final void testThreadPool() throws InterruptedException {
ThreadpoolConfig config = new ThreadpoolConfig(new ThreadpoolConfig.Builder().maxthreads(1));
- ThreadPoolProvider provider = new ThreadPoolProvider(config, Mockito.mock(Metric.class));
- Executor exec = provider.get();
- Tuple2<MessageState, Boolean> reply;
+ ContainerThreadPool threadPool = new ContainerThreadPool(config, Mockito.mock(Metric.class));
+ Executor exec = threadPool.executor();
+ Tuple2<Receiver.MessageState, Boolean> reply;
FlipIt command = new FlipIt();
for (boolean done = false; !done;) {
try {
@@ -42,13 +38,13 @@ public class ThreadPoolProviderTestCase {
}
}
reply = command.didItRun.get(5 * 60 * 1000);
- if (reply.first != MessageState.VALID) {
+ if (reply.first != Receiver.MessageState.VALID) {
fail("Executor task probably timed out, five minutes should be enough to flip a boolean.");
}
if (reply.second != Boolean.TRUE) {
fail("Executor task seemed to run, but did not get correct value.");
}
- provider.deconstruct();
+ threadPool.deconstruct();
command = new FlipIt();
try {
exec.execute(command);
@@ -61,9 +57,9 @@ public class ThreadPoolProviderTestCase {
private ThreadPoolExecutor createPool(int maxThreads, int queueSize) {
ThreadpoolConfig config = new ThreadpoolConfig(new ThreadpoolConfig.Builder().maxthreads(maxThreads).queueSize(queueSize));
- ThreadPoolProvider provider = new ThreadPoolProvider(config, Mockito.mock(Metric.class));
- ThreadPoolProvider.ExecutorServiceWrapper wrapper = (ThreadPoolProvider.ExecutorServiceWrapper) provider.get();
- ThreadPoolProvider.WorkerCompletionTimingThreadPoolExecutor executor = (ThreadPoolProvider.WorkerCompletionTimingThreadPoolExecutor)wrapper.delegate();
+ ContainerThreadPool threadPool = new ContainerThreadPool(config, Mockito.mock(Metric.class));
+ ExecutorServiceWrapper wrapper = (ExecutorServiceWrapper) threadPool.executor();
+ WorkerCompletionTimingThreadPoolExecutor executor = (WorkerCompletionTimingThreadPoolExecutor)wrapper.delegate();
return executor;
}
@@ -103,37 +99,37 @@ public class ThreadPoolProviderTestCase {
@Test
@Ignore // Ignored because it depends on the system time and so is unstable on factory
- public void testThreadPoolProviderTerminationOnBreakdown() throws InterruptedException {
+ public void testThreadPoolTerminationOnBreakdown() throws InterruptedException {
ThreadpoolConfig config = new ThreadpoolConfig(new ThreadpoolConfig.Builder().maxthreads(2)
- .maxThreadExecutionTimeSeconds(1));
+ .maxThreadExecutionTimeSeconds(1));
MockProcessTerminator terminator = new MockProcessTerminator();
- ThreadPoolProvider provider = new ThreadPoolProvider(config, Mockito.mock(Metric.class), terminator);
+ ContainerThreadPool threadPool = new ContainerThreadPool(config, Mockito.mock(Metric.class), terminator);
// No dying when threads hang shorter than max thread execution time
- provider.get().execute(new Hang(500));
- provider.get().execute(new Hang(500));
+ threadPool.executor().execute(new Hang(500));
+ threadPool.executor().execute(new Hang(500));
assertEquals(0, terminator.dieRequests);
- assertRejected(provider, new Hang(500)); // no more threads
+ assertRejected(threadPool, new Hang(500)); // no more threads
assertEquals(0, terminator.dieRequests); // ... but not for long enough yet
try { Thread.sleep(1500); } catch (InterruptedException e) {}
- provider.get().execute(new Hang(1));
+ threadPool.executor().execute(new Hang(1));
assertEquals(0, terminator.dieRequests);
try { Thread.sleep(50); } catch (InterruptedException e) {} // Make sure both threads are available
// Dying when hanging both thread pool threads for longer than max thread execution time
- provider.get().execute(new Hang(2000));
- provider.get().execute(new Hang(2000));
+ threadPool.executor().execute(new Hang(2000));
+ threadPool.executor().execute(new Hang(2000));
assertEquals(0, terminator.dieRequests);
- assertRejected(provider, new Hang(2000)); // no more threads
+ assertRejected(threadPool, new Hang(2000)); // no more threads
assertEquals(0, terminator.dieRequests); // ... but not for long enough yet
try { Thread.sleep(1500); } catch (InterruptedException e) {}
- assertRejected(provider, new Hang(2000)); // no more threads
+ assertRejected(threadPool, new Hang(2000)); // no more threads
assertEquals(1, terminator.dieRequests); // ... for longer than maxThreadExecutionTime
}
- private void assertRejected(ThreadPoolProvider provider, Runnable task) {
+ private void assertRejected(ContainerThreadPool threadPool, Runnable task) {
try {
- provider.get().execute(task);
+ threadPool.executor().execute(task);
fail("Expected execution rejected");
} catch (final RejectedExecutionException expected) {
}
@@ -165,4 +161,4 @@ public class ThreadPoolProviderTestCase {
}
-}
+} \ No newline at end of file
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
index 5cd14b3fac5..c8cce94d479 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
@@ -247,6 +247,10 @@ public class AthenzFacade implements AccessControl {
return hasAccess("callback", new AthenzResourceName(service.getDomain().getName(), "payment-notification-resource").toResourceNameString(), identity);
}
+ public boolean hasAccountingAccess(AthenzIdentity identity) {
+ return hasAccess("modify", new AthenzResourceName(service.getDomain().getName(), "hosted-accounting-resource").toResourceNameString(), identity);
+ }
+
/**
* Used when creating tenancies. As there are no tenancy policies at this point,
* we cannot use {@link #hasTenantAdminAccess(AthenzIdentity, AthenzDomain)}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java
index 25ee95e6d80..b9cf5ca4f4d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java
@@ -125,6 +125,11 @@ public class AthenzRoleFilter extends JsonSecurityRequestFilterBase {
roleMemberships.add(Role.paymentProcessor());
}));
+ futures.add(executor.submit(() -> {
+ if (athenz.hasAccountingAccess(identity))
+ roleMemberships.add(Role.hostedAccountant());
+ }));
+
// Run last request in handler thread to avoid creating extra thread.
if (athenz.hasSystemFlagsAccess(identity, /*dryrun*/true))
roleMemberships.add(Role.systemFlagsDryrunner());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java
index 5e50e80b7a7..3da662ee373 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java
@@ -70,13 +70,13 @@ public class AthenzRoleFilterTest {
public void testTranslations() throws Exception {
// Hosted operators are always members of the hostedOperator role.
- assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedSupporter()),
+ assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()),
filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH));
- assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedSupporter()),
+ assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()),
filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH));
- assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedSupporter()),
+ assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()),
filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH));
// Tenant admins are members of the athenzTenantAdmin role within their tenant subtree.
diff --git a/default_build_settings.cmake b/default_build_settings.cmake
index 64c918370aa..cc51bbde852 100644
--- a/default_build_settings.cmake
+++ b/default_build_settings.cmake
@@ -18,7 +18,7 @@ endfunction()
function(setup_vespa_default_build_settings_rhel_8)
message("-- Setting up default build settings for rhel 8")
set(DEFAULT_EXTRA_INCLUDE_DIRECTORY "${VESPA_DEPS}/include" "/usr/include/openblas" PARENT_SCOPE)
- set(DEFAULT_VESPA_LLVM_VERSION "8" PARENT_SCOPE)
+ set(DEFAULT_VESPA_LLVM_VERSION "9" PARENT_SCOPE)
endfunction()
function(setup_vespa_default_build_settings_centos_7)
diff --git a/dist/vespa.spec b/dist/vespa.spec
index a887fd0645b..690d4123de4 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -62,7 +62,11 @@ BuildRequires: vespa-icu-devel >= 65.1.0-1
%endif
%if 0%{?el8}
BuildRequires: cmake >= 3.11.4-3
+%if 0%{?centos}
BuildRequires: llvm-devel >= 8.0.1
+%else
+BuildRequires: llvm-devel >= 9.0.1
+%endif
BuildRequires: boost-devel >= 1.66
BuildRequires: openssl-devel
BuildRequires: vespa-gtest >= 1.8.1-1
@@ -162,10 +166,15 @@ Requires: vespa-telegraf >= 1.1.1-1
%define _extra_include_directory /usr/include/llvm7.0;%{_vespa_deps_prefix}/include;/usr/include/openblas
%endif
%if 0%{?el8}
+%if 0%{?centos}
Requires: llvm-libs >= 8.0.1
+%define _vespa_llvm_version 8
+%else
+Requires: llvm-libs >= 9.0.1
+%define _vespa_llvm_version 9
+%endif
Requires: vespa-protobuf >= 3.7.0-4
Requires: openssl-libs
-%define _vespa_llvm_version 8
%define _extra_link_directory %{_vespa_deps_prefix}/lib64
%define _extra_include_directory %{_vespa_deps_prefix}/include;/usr/include/openblas
%endif
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 838e55910e1..efe86bb6d55 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -246,13 +246,8 @@ public class Flags {
"Takes effect on next application redeploy",
APPLICATION_ID);
- public static final UnboundLongFlag CONFIGSERVER_SESSIONS_EXPIRY_INTERVAL_IN_DAYS = defineLongFlag(
- "configserver-sessions-expiry-interval-in-days", 1,
- "Expiry time for unused sessions in config server",
- "Takes effect on next run of config server maintainer SessionsMaintainer");
-
public static final UnboundLongFlag CONFIGSERVER_LOCAL_SESSIONS_EXPIRY_INTERVAL_IN_DAYS = defineLongFlag(
- "configserver-local-sessions-expiry-interval-in-days", 21,
+ "configserver-local-sessions-expiry-interval-in-days", 1,
"Expiry time for expired local sessions in config server",
"Takes effect on next run of config server maintainer SessionsMaintainer");
diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
index af134fac6cf..6e637c72d0f 100644
--- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
+++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/IntermediateOperation.java
@@ -59,7 +59,7 @@ public abstract class IntermediateOperation {
IntermediateOperation(String modelName, String name, List<IntermediateOperation> inputs) {
this.name = name;
- this.modelName = modelName;
+ this.modelName = ensureValidAsDimensionName(modelName);
this.inputs = new ArrayList<>(inputs);
this.inputs.forEach(i -> i.outputs.add(this));
}
@@ -351,6 +351,11 @@ public abstract class IntermediateOperation {
public abstract String operationName();
+ /** Required due to tensor dimension name restrictions */
+ private static String ensureValidAsDimensionName(String modelName) {
+ return modelName.replaceAll("[^\\w\\d\\$@_]", "_");
+ }
+
@Override
public String toString() {
return operationName() + "(" +
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
index 45ed8db3491..a9861497ca3 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
@@ -168,17 +168,25 @@ public final class Node {
public Optional<TenantName> reservedTo() { return reservedTo; }
/**
- * Returns a copy of this node with wantToRetire set to the given value and updated history.
- * If given wantToRetire is equal to the current, the method is no-op.
+ * Returns a copy of this node with wantToRetire and wantToDeprovision set to the given values and updated history.
+ *
+ * If both given wantToRetire and wantToDeprovision are equal to the current values, the method is no-op.
*/
- public Node withWantToRetire(boolean wantToRetire, Agent agent, Instant at) {
- if (wantToRetire == status.wantToRetire()) return this;
- Node node = this.with(status.withWantToRetire(wantToRetire));
+ public Node withWantToRetire(boolean wantToRetire, boolean wantToDeprovision, Agent agent, Instant at) {
+ if (!type.isDockerHost() && wantToDeprovision)
+ throw new IllegalArgumentException("wantToDeprovision can only be set for hosts");
+ if (wantToRetire == status.wantToRetire() &&
+ wantToDeprovision == status.wantToDeprovision()) return this;
+ Node node = this.with(status.withWantToRetire(wantToRetire, wantToDeprovision));
if (wantToRetire)
node = node.with(history.with(new History.Event(History.Event.Type.wantToRetire, agent, at)));
return node;
}
+ public Node withWantToRetire(boolean wantToRetire, Agent agent, Instant at) {
+ return withWantToRetire(wantToRetire, status.wantToDeprovision(), agent, at);
+ }
+
/**
* Returns a copy of this node which is retired.
* If the node was already retired it is returned as-is.
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
index cc485374340..b5e36abd076 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
@@ -434,7 +434,7 @@ public class NodeRepository extends AbstractComponent {
.map(node -> {
if (node.state() != State.provisioned && node.state() != State.dirty)
illegal("Can not set " + node + " ready. It is not provisioned or dirty.");
- return node.with(node.status().withWantToRetire(false).withWantToDeprovision(false));
+ return node.withWantToRetire(false, false, Agent.system, clock.instant());
})
.collect(Collectors.toList());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
index b2c57e984eb..3cb7cc218a7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
@@ -5,6 +5,7 @@ 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.History;
+import com.yahoo.vespa.hosted.provision.node.Status;
import java.time.Clock;
import java.time.Duration;
@@ -14,15 +15,19 @@ import java.util.List;
* Maintenance job which moves inactive nodes to dirty or parked after timeout.
*
* The timeout is in place for two reasons:
- * <ul>
- * <li>To ensure that the new application configuration has time to
- * propagate before the node is used for something else
- * <li>To provide a grace period in which nodes can be brought back to active
- * if they were deactivated in error. As inactive nodes retain their state
- * they can be brought back to active and correct state faster than a new node.
- * </ul>
*
- * Nodes with the retired flag should not be reused and will be moved to parked instead of dirty.
+ * - To ensure that the new application configuration has time to
+ * propagate before the node is used for something else.
+ *
+ * - To provide a grace period in which nodes can be brought back to active
+ * if they were deactivated in error. As inactive nodes retain their state
+ * they can be brought back to active and correct state faster than a new node.
+ *
+ * Nodes with following flags set are not reusable and will be moved to parked
+ * instead of dirty:
+ *
+ * - {@link Status#wantToRetire()} (when set by an operator)
+ * - {@link Status#wantToDeprovision()}
*
* @author bratseth
* @author mpolden
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Status.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Status.java
index 6b52bd68e73..c289edfc19e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Status.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Status.java
@@ -36,6 +36,11 @@ public class Status {
this.vespaVersion = Objects.requireNonNull(vespaVersion, "Vespa version must be non-null").filter(v -> !Version.emptyVersion.equals(v));
this.dockerImage = Objects.requireNonNull(dockerImage, "Docker image must be non-null").filter(d -> !DockerImage.EMPTY.equals(d));
this.failCount = failCount;
+ if (wantToDeprovision && !wantToRetire) {
+ // TODO(mpolden): Throw when persisted nodes have been rewritten
+ wantToRetire = true;
+ //throw new IllegalArgumentException("Node cannot be marked wantToDeprovision unless it's also marked wantToRetire");
+ }
this.wantToRetire = wantToRetire;
this.wantToDeprovision = wantToDeprovision;
this.osVersion = Objects.requireNonNull(osVersion, "OS version must be non-null");
@@ -69,8 +74,8 @@ public class Status {
/** Returns how many times this node has been moved to the failed state. */
public int failCount() { return failCount; }
- /** Returns a copy of this with the want to retire flag changed */
- public Status withWantToRetire(boolean wantToRetire) {
+ /** Returns a copy of this with the want to retire/deprovision flags changed */
+ public Status withWantToRetire(boolean wantToRetire, boolean wantToDeprovision) {
return new Status(reboot, vespaVersion, dockerImage, failCount, wantToRetire, wantToDeprovision, osVersion, firmwareVerifiedAt);
}
@@ -82,11 +87,6 @@ public class Status {
return wantToRetire;
}
- /** Returns a copy of this with the want to de-provision flag changed */
- public Status withWantToDeprovision(boolean wantToDeprovision) {
- return new Status(reboot, vespaVersion, dockerImage, failCount, wantToRetire, wantToDeprovision, osVersion, firmwareVerifiedAt);
- }
-
/**
* Returns whether this node should be de-provisioned when possible.
*/
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringUpgrader.java
index 6bd2545b153..b2b83b6d064 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringUpgrader.java
@@ -72,10 +72,10 @@ public class RetiringUpgrader implements Upgrader {
LOG.info("Retiring and deprovisioning " + host + ": On stale OS version " +
host.status().osVersion().current().map(Version::toFullString).orElse("<unset>") +
", want " + target);
- nodesToRetire.add(host.with(host.status()
- .withWantToDeprovision(true)
- .withOsVersion(host.status().osVersion().withWanted(Optional.of(target))))
- .withWantToRetire(true, Agent.RetiringUpgrader, now));
+
+ host = host.withWantToRetire(true, true, Agent.RetiringUpgrader, now);
+ host = host.with(host.status().withOsVersion(host.status().osVersion().withWanted(Optional.of(target))));
+ nodesToRetire.add(host);
nodeRepository.write(nodesToRetire, lock);
nodeRepository.osVersions().writeChange((change) -> change.withRetirementAt(now, nodeType));
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
index 8b5639cc514..897af634d49 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
@@ -6,13 +6,12 @@ import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
import com.yahoo.io.IOUtils;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.ObjectTraverser;
-import com.yahoo.slime.Type;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.slime.Type;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.Agent;
@@ -48,6 +47,7 @@ import static com.yahoo.config.provision.NodeResources.StorageType.remote;
public class NodePatcher {
private static final String WANT_TO_RETIRE = "wantToRetire";
+ private static final String WANT_TO_DEPROVISION = "wantToDeprovision";
private final NodeFlavors nodeFlavors;
private final Inspector inspector;
@@ -77,13 +77,13 @@ public class NodePatcher {
List<Node> patchedNodes = new ArrayList<>();
inspector.traverse((String name, Inspector value) -> {
try {
- node = applyField(node, name, value);
+ node = applyField(node, name, value, inspector);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not set field '" + name + "'", e);
}
try {
- patchedNodes.addAll(applyFieldRecursive(name, value));
+ patchedNodes.addAll(applyFieldRecursive(name, value, inspector));
} catch (IllegalArgumentException e) {
// Non recursive field, ignore
}
@@ -93,12 +93,12 @@ public class NodePatcher {
return patchedNodes;
}
- private List<Node> applyFieldRecursive(String name, Inspector value) {
+ private List<Node> applyFieldRecursive(String name, Inspector value, Inspector root) {
switch (name) {
case WANT_TO_RETIRE:
List<Node> childNodes = node.type().isDockerHost() ? nodes.get().childrenOf(node).asList() : List.of();
return childNodes.stream()
- .map(child -> applyField(child, name, value))
+ .map(child -> applyField(child, name, value, root))
.collect(Collectors.toList());
default :
@@ -106,7 +106,7 @@ public class NodePatcher {
}
}
- private Node applyField(Node node, String name, Inspector value) {
+ private Node applyField(Node node, String name, Inspector value, Inspector root) {
switch (name) {
case "currentRebootGeneration" :
return node.withCurrentRebootGeneration(asLong(value), clock.instant());
@@ -134,11 +134,10 @@ public class NodePatcher {
case "additionalIpAddresses" :
return IP.Config.verify(node.with(node.ipConfig().with(IP.Pool.of(asStringSet(value)))), nodes.get());
case WANT_TO_RETIRE :
- return node.withWantToRetire(asBoolean(value), Agent.operator, clock.instant());
- case "wantToDeprovision" :
- if (node.type() != NodeType.host && asBoolean(value))
- throw new IllegalArgumentException("wantToDeprovision can only be set for hosts");
- return node.with(node.status().withWantToDeprovision(asBoolean(value)));
+ case WANT_TO_DEPROVISION :
+ boolean wantToRetire = asOptionalBoolean(root.field(WANT_TO_RETIRE)).orElse(node.status().wantToRetire());
+ boolean wantToDeprovision = asOptionalBoolean(root.field(WANT_TO_DEPROVISION)).orElse(node.status().wantToDeprovision());
+ return node.withWantToRetire(wantToRetire, wantToDeprovision, Agent.operator, clock.instant());
case "reports" :
return nodeWithPatchedReports(node, value);
case "openStackId" :
@@ -202,7 +201,7 @@ public class NodePatcher {
if ((hasHardFailReports && node.state() == Node.State.failed) || node.state() == Node.State.parked)
return patchedNode;
- patchedNode = patchedNode.with(patchedNode.status().withWantToDeprovision(hasHardFailReports));
+ patchedNode = patchedNode.withWantToRetire(hasHardFailReports, hasHardFailReports, Agent.system, clock.instant());
}
return patchedNode;
@@ -252,19 +251,14 @@ public class NodePatcher {
return field.asString();
}
- private Optional<String> asOptionalString(Inspector field) {
- return field.type().equals(Type.NIX) ? Optional.empty() : Optional.of(asString(field));
- }
-
- // Allows us to clear optional flags by passing "null" as slime does not have an empty (but present) representation
- private Optional<String> removeQuotedNulls(Optional<String> value) {
- return value.filter(v -> !v.equals("null"));
- }
-
private boolean asBoolean(Inspector field) {
if ( ! field.type().equals(Type.BOOL))
throw new IllegalArgumentException("Expected a BOOL value, got a " + field.type());
return field.asBool();
}
+ private Optional<Boolean> asOptionalBoolean(Inspector field) {
+ return Optional.of(field).filter(Inspector::valid).map(this::asBoolean);
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index 94b97d91312..151bd80a7b7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -117,7 +117,7 @@ public class MockNodeRepository extends NodeRepository {
Node node55 = createNode("node55", "host55.yahoo.com", ipConfig(55), Optional.empty(),
new Flavor(new NodeResources(2, 8, 50, 1, fast, local)), Optional.empty(), NodeType.tenant);
- nodes.add(node55.with(node55.status().withWantToRetire(true).withWantToDeprovision(true)));
+ nodes.add(node55.with(node55.status().withWantToRetire(true, true)));
/* Setup docker hosts (two of these will be reserved for spares */
nodes.add(createNode("dockerhost1", "dockerhost1.yahoo.com", ipConfig(100, 1, 3), Optional.empty(),
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
index 7bb80fe2a21..97f1eda866a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.provision;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.History;
import com.yahoo.vespa.hosted.provision.node.IP;
@@ -13,7 +12,6 @@ import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
-import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
@@ -165,8 +163,7 @@ public class NodeRepositoryTest {
// Set host 1 properties and deprovision it
Node host1 = tester.nodeRepository().getNode("host1").get();
- host1 = host1.withWantToRetire(true, Agent.system, tester.nodeRepository().clock().instant());
- host1 = host1.with(host1.status().withWantToDeprovision(true));
+ host1 = host1.withWantToRetire(true, true, Agent.system, tester.nodeRepository().clock().instant());
host1 = host1.withFirmwareVerifiedAt(tester.clock().instant());
host1 = host1.with(host1.status().withIncreasedFailCount());
host1 = host1.with(host1.reports().withReport(Report.basicReport("id", Report.Type.HARD_FAIL, tester.clock().instant(), "Test report")));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
index e42e8e57b8c..89e43f80479 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
@@ -191,11 +191,11 @@ public class InactiveAndFailedExpirerTest {
@Test
public void nodes_marked_for_deprovisioning_move_to_parked() {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build();
- tester.makeReadyNodes(5, nodeResources);
+ tester.makeReadyHosts(2, nodeResources);
// Activate and deallocate
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test")).vespaVersion("6.42").build();
- List<HostSpec> preparedNodes = tester.prepare(applicationId, cluster, Capacity.from(new ClusterResources(2, 1, nodeResources)));
+ List<HostSpec> preparedNodes = tester.prepare(applicationId, cluster, Capacity.fromRequiredNodeType(NodeType.host));
tester.activate(applicationId, new HashSet<>(preparedNodes));
assertEquals(2, tester.getNodes(applicationId, Node.State.active).size());
tester.deactivate(applicationId);
@@ -204,7 +204,7 @@ public class InactiveAndFailedExpirerTest {
// Nodes marked for deprovisioning are moved to parked
tester.nodeRepository().write(inactiveNodes.stream()
- .map(node -> node.with(node.status().withWantToDeprovision(true)))
+ .map(node -> node.withWantToRetire(true, true, Agent.system, tester.clock().instant()))
.collect(Collectors.toList()), () -> {});
tester.advanceTime(Duration.ofMinutes(11));
new InactiveExpirer(tester.nodeRepository(), tester.clock(), Duration.ofMinutes(10)).run();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
index 16fba824300..b2ee298c19d 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
@@ -205,7 +205,7 @@ public class InPlaceResizeProvisionTest {
// ... same with setting a node to want to retire
Node nodeToWantoToRetire = listCluster(content1).not().retired().asList().get(0);
- tester.nodeRepository().write(nodeToWantoToRetire.with(nodeToWantoToRetire.status().withWantToRetire(true)),
+ tester.nodeRepository().write(nodeToWantoToRetire.withWantToRetire(true, Agent.system, tester.clock().instant()),
tester.nodeRepository().lock(nodeToWantoToRetire));
new PrepareHelper(tester, app).prepare(content1, 8, 1, halvedResources).activate();
assertTrue(listCluster(content1).retired().stream().anyMatch(n -> n.equals(nodeToWantoToRetire)));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
index 98133898cf6..11e7af512c3 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
@@ -113,7 +113,7 @@ public class NodeTypeProvisioningTest {
Node nodeToRetire = tester.nodeRepository().getNodes(NodeType.proxy, Node.State.active).get(5);
{ // Pick out a node and retire it
- tester.nodeRepository().write(nodeToRetire.with(nodeToRetire.status().withWantToRetire(true)), () -> {});
+ tester.nodeRepository().write(nodeToRetire.withWantToRetire(true, Agent.system, tester.clock().instant()), () -> {});
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals(11, hosts.size());
@@ -186,7 +186,7 @@ public class NodeTypeProvisioningTest {
String currentyRetiringHostname;
{
nodesToRetire.forEach(nodeToRetire ->
- tester.nodeRepository().write(nodeToRetire.with(nodeToRetire.status().withWantToRetire(true)), () -> {}));
+ tester.nodeRepository().write(nodeToRetire.withWantToRetire(true, Agent.system, tester.clock().instant()), () -> {}));
List<HostSpec> hosts = deployProxies(application, tester);
assertEquals(11, hosts.size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
index 7a7f8a7d891..607ca963cef 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
@@ -633,7 +633,7 @@ public class ProvisioningTest {
ApplicationId application = tester.makeApplicationId();
// Flag all nodes for retirement
List<Node> readyNodes = tester.makeReadyNodes(5, defaultResources);
- readyNodes.forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true))));
+ readyNodes.forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant())));
try {
prepare(application, 2, 0, 2, 0, defaultResources, tester);
@@ -661,7 +661,7 @@ public class ProvisioningTest {
assertEquals(0, NodeList.copyOf(tester.nodeRepository().getNodes(application, Node.State.active)).retired().size());
// Mark the nodes as want to retire
- tester.nodeRepository().getNodes(application, Node.State.active).forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true))));
+ tester.nodeRepository().getNodes(application, Node.State.active).forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant())));
// redeploy without allow failing
tester.activate(application, tester.prepare(application, cluster, capacityFORCED));
@@ -724,7 +724,7 @@ public class ProvisioningTest {
// Retire some nodes and redeploy
{
List<Node> nodesToRetire = tester.getNodes(application, Node.State.active).asList().subList(0, 2);
- nodesToRetire.forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true))));
+ nodesToRetire.forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant())));
SystemState state = prepare(application, 2, 0, 2, 0, defaultResources, tester);
tester.activate(application, state.allHosts);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
index 29dac58e10c..1e788e2c70e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
@@ -207,8 +207,17 @@ public class NodesV2ApiTest {
Utf8.toBytes("{\"modelName\": \"foo\"}"), Request.Method.PATCH),
"{\"message\":\"Updated dockerhost1.yahoo.com\"}");
assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
- Utf8.toBytes("{\"wantToDeprovision\": true}"), Request.Method.PATCH),
+ Utf8.toBytes("{\"wantToRetire\": true}"), Request.Method.PATCH),
"{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ Utf8.toBytes("{\"wantToDeprovision\": true}"), Request.Method.PATCH),
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ Utf8.toBytes("{\"wantToDeprovision\": false, \"wantToRetire\": false}"), Request.Method.PATCH),
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ Utf8.toBytes("{\"wantToDeprovision\": true, \"wantToRetire\": true}"), Request.Method.PATCH),
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
tester.assertResponseContains(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "\"modelName\":\"foo\"");
assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{\"modelName\": null}"), Request.Method.PATCH),
@@ -379,7 +388,7 @@ public class NodesV2ApiTest {
@Test
public void fails_to_ready_node_with_hard_fail() throws Exception {
assertResponse(new Request("http://localhost:8080/nodes/v2/node",
- ("[" + asNodeJson("host12.yahoo.com", "default") + "]").
+ ("[" + asHostJson("host12.yahoo.com", "default", Optional.empty()) + "]").
getBytes(StandardCharsets.UTF_8),
Request.Method.POST),
"{\"message\":\"Added 1 nodes to the provisioned state\"}");
@@ -563,7 +572,7 @@ public class NodesV2ApiTest {
@Test
public void test_reports_patching() throws IOException {
// Add report
- assertResponse(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com",
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{" +
" \"reports\": {" +
" \"actualCpuCores\": {" +
@@ -584,19 +593,19 @@ public class NodesV2ApiTest {
" }" +
"}"),
Request.Method.PATCH),
- "{\"message\":\"Updated host6.yahoo.com\"}");
- assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6-reports.json");
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertFile(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "docker-node1-reports.json");
// Patching with an empty reports is no-op
- tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com",
+ tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{\"reports\": {}}"),
Request.Method.PATCH),
200,
- "{\"message\":\"Updated host6.yahoo.com\"}");
- assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6-reports.json");
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertFile(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "docker-node1-reports.json");
// Patching existing report overwrites
- tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com",
+ tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{" +
" \"reports\": {" +
" \"actualCpuCores\": {" +
@@ -606,22 +615,22 @@ public class NodesV2ApiTest {
"}"),
Request.Method.PATCH),
200,
- "{\"message\":\"Updated host6.yahoo.com\"}");
- assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6-reports-2.json");
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertFile(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "docker-node1-reports-2.json");
// Clearing one report
- assertResponse(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com",
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{\"reports\": { \"diskSpace\": null } }"),
Request.Method.PATCH),
- "{\"message\":\"Updated host6.yahoo.com\"}");
- assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6-reports-3.json");
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertFile(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "docker-node1-reports-3.json");
// Clearing all reports
- assertResponse(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com",
+ assertResponse(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
Utf8.toBytes("{\"reports\": null }"),
Request.Method.PATCH),
- "{\"message\":\"Updated host6.yahoo.com\"}");
- assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6.json");
+ "{\"message\":\"Updated dockerhost1.yahoo.com\"}");
+ assertFile(new Request("http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com"), "docker-node1-reports-4.json");
}
@Test
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json
index a3d53798d7c..220fdbd8654 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-2.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json
@@ -1,35 +1,50 @@
{
- "url": "http://localhost:8080/nodes/v2/node/host6.yahoo.com",
- "id": "host6.yahoo.com",
+ "url": "http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ "id": "dockerhost1.yahoo.com",
"state": "active",
- "type": "tenant",
- "hostname": "host6.yahoo.com",
- "openStackId": "node6",
- "flavor": "[vcpu: 2.0, memory: 8.0 Gb, disk 50.0 Gb, bandwidth: 1.0 Gbps, storage type: local]",
- "resources":{"vcpu":2.0,"memoryGb":8.0,"diskGb":50.0,"bandwidthGbps":1.0,"diskSpeed":"fast","storageType":"local"},
- "environment": "DOCKER_CONTAINER",
+ "type": "host",
+ "hostname": "dockerhost1.yahoo.com",
+ "openStackId": "dockerhost1",
+ "flavor": "large",
+ "cpuCores": 4.0,
+ "resources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "environment": "BARE_METAL",
"owner": {
- "tenant": "tenant2",
- "application": "application2",
- "instance": "instance2"
+ "tenant": "zoneapp",
+ "application": "zoneapp",
+ "instance": "zoneapp"
},
"membership": {
- "clustertype": "content",
- "clusterid": "id2",
+ "clustertype": "container",
+ "clusterid": "node-admin",
"group": "0",
- "index": 1,
+ "index": 0,
"retired": false
},
"restartGeneration": 0,
"currentRestartGeneration": 0,
"wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0",
"wantedVespaVersion": "6.42.0",
- "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" },
+ "requestedResources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
"allowedToBeDown": false,
- "rebootGeneration": 1,
+ "rebootGeneration": 0,
"currentRebootGeneration": 0,
"failCount": 0,
- "wantToRetire": false,
+ "wantToRetire": true,
"wantToDeprovision": true,
"history": [
{
@@ -51,13 +66,22 @@
"event": "activated",
"at": 123,
"agent": "application"
+ },
+ {
+ "event": "wantToRetire",
+ "at": 123,
+ "agent": "system"
}
],
"ipAddresses": [
- "127.0.6.1",
- "::6:1"
+ "127.0.100.1",
+ "::100:1"
+ ],
+ "additionalIpAddresses": [
+ "::100:2",
+ "::100:3",
+ "::100:4"
],
- "additionalIpAddresses": [],
"reports": {
"actualCpuCores": {
"createdMillis": 3
@@ -65,7 +89,7 @@
"diskSpace": {
"createdMillis": 2,
"description": "Actual disk space (2TB) differs from spec (3TB)",
- "type":"HARD_FAIL",
+ "type": "HARD_FAIL",
"details": {
"inGib": 3,
"disks": [
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json
new file mode 100644
index 00000000000..d2474f21c55
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json
@@ -0,0 +1,90 @@
+{
+ "url": "http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ "id": "dockerhost1.yahoo.com",
+ "state": "active",
+ "type": "host",
+ "hostname": "dockerhost1.yahoo.com",
+ "openStackId": "dockerhost1",
+ "flavor": "large",
+ "cpuCores": 4.0,
+ "resources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "environment": "BARE_METAL",
+ "owner": {
+ "tenant": "zoneapp",
+ "application": "zoneapp",
+ "instance": "zoneapp"
+ },
+ "membership": {
+ "clustertype": "container",
+ "clusterid": "node-admin",
+ "group": "0",
+ "index": 0,
+ "retired": false
+ },
+ "restartGeneration": 0,
+ "currentRestartGeneration": 0,
+ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0",
+ "wantedVespaVersion": "6.42.0",
+ "requestedResources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "allowedToBeDown": false,
+ "rebootGeneration": 0,
+ "currentRebootGeneration": 0,
+ "failCount": 0,
+ "wantToRetire": false,
+ "wantToDeprovision": false,
+ "history": [
+ {
+ "event": "provisioned",
+ "at": 123,
+ "agent": "system"
+ },
+ {
+ "event": "readied",
+ "at": 123,
+ "agent": "system"
+ },
+ {
+ "event": "reserved",
+ "at": 123,
+ "agent": "application"
+ },
+ {
+ "event": "activated",
+ "at": 123,
+ "agent": "application"
+ },
+ {
+ "event": "wantToRetire",
+ "at": 123,
+ "agent": "system"
+ }
+ ],
+ "ipAddresses": [
+ "127.0.100.1",
+ "::100:1"
+ ],
+ "additionalIpAddresses": [
+ "::100:2",
+ "::100:3",
+ "::100:4"
+ ],
+ "reports": {
+ "actualCpuCores": {
+ "createdMillis": 3
+ }
+ }
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json
new file mode 100644
index 00000000000..cbf02795d73
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json
@@ -0,0 +1,85 @@
+{
+ "url": "http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ "id": "dockerhost1.yahoo.com",
+ "state": "active",
+ "type": "host",
+ "hostname": "dockerhost1.yahoo.com",
+ "openStackId": "dockerhost1",
+ "flavor": "large",
+ "cpuCores": 4.0,
+ "resources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "environment": "BARE_METAL",
+ "owner": {
+ "tenant": "zoneapp",
+ "application": "zoneapp",
+ "instance": "zoneapp"
+ },
+ "membership": {
+ "clustertype": "container",
+ "clusterid": "node-admin",
+ "group": "0",
+ "index": 0,
+ "retired": false
+ },
+ "restartGeneration": 0,
+ "currentRestartGeneration": 0,
+ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0",
+ "wantedVespaVersion": "6.42.0",
+ "requestedResources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "allowedToBeDown": false,
+ "rebootGeneration": 0,
+ "currentRebootGeneration": 0,
+ "failCount": 0,
+ "wantToRetire": false,
+ "wantToDeprovision": false,
+ "history": [
+ {
+ "event": "provisioned",
+ "at": 123,
+ "agent": "system"
+ },
+ {
+ "event": "readied",
+ "at": 123,
+ "agent": "system"
+ },
+ {
+ "event": "reserved",
+ "at": 123,
+ "agent": "application"
+ },
+ {
+ "event": "activated",
+ "at": 123,
+ "agent": "application"
+ },
+ {
+ "event": "wantToRetire",
+ "at": 123,
+ "agent": "system"
+ }
+ ],
+ "ipAddresses": [
+ "127.0.100.1",
+ "::100:1"
+ ],
+ "additionalIpAddresses": [
+ "::100:2",
+ "::100:3",
+ "::100:4"
+ ]
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json
index 67b8d67c7f1..c00c06634b5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json
@@ -1,35 +1,50 @@
{
- "url": "http://localhost:8080/nodes/v2/node/host6.yahoo.com",
- "id": "host6.yahoo.com",
+ "url": "http://localhost:8080/nodes/v2/node/dockerhost1.yahoo.com",
+ "id": "dockerhost1.yahoo.com",
"state": "active",
- "type": "tenant",
- "hostname": "host6.yahoo.com",
- "openStackId": "node6",
- "flavor": "[vcpu: 2.0, memory: 8.0 Gb, disk 50.0 Gb, bandwidth: 1.0 Gbps, storage type: local]",
- "resources":{"vcpu":2.0,"memoryGb":8.0,"diskGb":50.0,"bandwidthGbps":1.0,"diskSpeed":"fast","storageType":"local"},
- "environment": "DOCKER_CONTAINER",
+ "type": "host",
+ "hostname": "dockerhost1.yahoo.com",
+ "openStackId": "dockerhost1",
+ "flavor": "large",
+ "cpuCores": 4.0,
+ "resources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
+ "environment": "BARE_METAL",
"owner": {
- "tenant": "tenant2",
- "application": "application2",
- "instance": "instance2"
+ "tenant": "zoneapp",
+ "application": "zoneapp",
+ "instance": "zoneapp"
},
"membership": {
- "clustertype": "content",
- "clusterid": "id2",
+ "clustertype": "container",
+ "clusterid": "node-admin",
"group": "0",
- "index": 1,
+ "index": 0,
"retired": false
},
"restartGeneration": 0,
"currentRestartGeneration": 0,
"wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0",
"wantedVespaVersion": "6.42.0",
- "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" },
+ "requestedResources": {
+ "vcpu": 4.0,
+ "memoryGb": 32.0,
+ "diskGb": 1600.0,
+ "bandwidthGbps": 20.0,
+ "diskSpeed": "fast",
+ "storageType": "remote"
+ },
"allowedToBeDown": false,
- "rebootGeneration": 1,
+ "rebootGeneration": 0,
"currentRebootGeneration": 0,
"failCount": 0,
- "wantToRetire": false,
+ "wantToRetire": true,
"wantToDeprovision": true,
"history": [
{
@@ -51,24 +66,33 @@
"event": "activated",
"at": 123,
"agent": "application"
+ },
+ {
+ "event": "wantToRetire",
+ "at": 123,
+ "agent": "system"
}
],
"ipAddresses": [
- "127.0.6.1",
- "::6:1"
+ "127.0.100.1",
+ "::100:1"
+ ],
+ "additionalIpAddresses": [
+ "::100:2",
+ "::100:3",
+ "::100:4"
],
- "additionalIpAddresses": [],
"reports": {
"actualCpuCores": {
"createdMillis": 1,
"description": "Actual number of CPU cores (2) differs from spec (4)",
- "type":"HARD_FAIL",
+ "type": "HARD_FAIL",
"value": 2
},
"diskSpace": {
"createdMillis": 2,
"description": "Actual disk space (2TB) differs from spec (3TB)",
- "type":"HARD_FAIL",
+ "type": "HARD_FAIL",
"details": {
"inGib": 3,
"disks": [
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-3.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-3.json
deleted file mode 100644
index 7f0c3a5f706..00000000000
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6-reports-3.json
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- "url": "http://localhost:8080/nodes/v2/node/host6.yahoo.com",
- "id": "host6.yahoo.com",
- "state": "active",
- "type": "tenant",
- "hostname": "host6.yahoo.com",
- "openStackId": "node6",
- "flavor": "[vcpu: 2.0, memory: 8.0 Gb, disk 50.0 Gb, bandwidth: 1.0 Gbps, storage type: local]",
- "resources":{"vcpu":2.0,"memoryGb":8.0,"diskGb":50.0,"bandwidthGbps":1.0,"diskSpeed":"fast","storageType":"local"},
- "environment": "DOCKER_CONTAINER",
- "owner": {
- "tenant": "tenant2",
- "application": "application2",
- "instance": "instance2"
- },
- "membership": {
- "clustertype": "content",
- "clusterid": "id2",
- "group": "0",
- "index": 1,
- "retired": false
- },
- "restartGeneration": 0,
- "currentRestartGeneration": 0,
- "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0",
- "wantedVespaVersion": "6.42.0",
- "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" },
- "allowedToBeDown": false,
- "rebootGeneration": 1,
- "currentRebootGeneration": 0,
- "failCount": 0,
- "wantToRetire": false,
- "wantToDeprovision": false,
- "history": [
- {
- "event": "provisioned",
- "at": 123,
- "agent": "system"
- },
- {
- "event": "readied",
- "at": 123,
- "agent": "system"
- },
- {
- "event": "reserved",
- "at": 123,
- "agent": "application"
- },
- {
- "event": "activated",
- "at": 123,
- "agent": "application"
- }
- ],
- "ipAddresses": [
- "127.0.6.1",
- "::6:1"
- ],
- "additionalIpAddresses": [],
- "reports": {
- "actualCpuCores": {
- "createdMillis": 3
- }
- }
-}
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
index 6016d60b880..ea71cafb73a 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastore.cpp
@@ -788,7 +788,7 @@ DocumentMetaStore::getLidUsageStats() const
Blueprint::UP
DocumentMetaStore::createWhiteListBlueprint() const
{
- return _lidAlloc.createWhiteListBlueprint(getCommittedDocIdLimit());
+ return _lidAlloc.createWhiteListBlueprint();
}
AttributeVector::SearchContext::UP
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
index dec83b9bca7..31361e40c68 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
@@ -196,17 +196,15 @@ class WhiteListBlueprint : public SimpleLeafBlueprint, public WhiteListProvider
{
private:
const search::GrowableBitVector &_activeLids;
- const uint32_t _docIdLimit;
mutable std::mutex _lock;
mutable std::vector<search::fef::TermFieldMatchData *> _matchDataVector;
search::BitVector::UP get_white_list_filter() const override {
- return search::BitVector::create(_activeLids);
+ return search::BitVector::create(_activeLids, 0, get_docid_limit());
}
SearchIterator::UP
- createLeafSearch(const TermFieldMatchDataArray &tfmda,
- bool strict) const override
+ createLeafSearch(const TermFieldMatchDataArray &tfmda, bool strict) const override
{
assert(tfmda.size() == 0);
(void) tfmda;
@@ -219,14 +217,13 @@ private:
std::lock_guard<std::mutex> lock(_lock);
_matchDataVector.push_back(tfmd);
}
- return search::BitVectorIterator::create(&_activeLids, _docIdLimit, *tfmd, strict);
+ return search::BitVectorIterator::create(&_activeLids, get_docid_limit(), *tfmd, strict);
}
public:
- WhiteListBlueprint(const search::GrowableBitVector &activeLids, uint32_t docIdLimit)
+ WhiteListBlueprint(const search::GrowableBitVector &activeLids)
: SimpleLeafBlueprint(FieldSpecBaseList()),
_activeLids(activeLids),
- _docIdLimit(docIdLimit),
_matchDataVector()
{
setEstimate(HitEstimate(_activeLids.size(), false));
@@ -244,9 +241,9 @@ public:
}
Blueprint::UP
-LidAllocator::createWhiteListBlueprint(uint32_t docIdLimit) const
+LidAllocator::createWhiteListBlueprint() const
{
- return std::make_unique<WhiteListBlueprint>(_activeLids.getBitVector(), docIdLimit);
+ return std::make_unique<WhiteListBlueprint>(_activeLids.getBitVector());
}
void
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.h b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.h
index ad41c6a8224..ccf9ef4513e 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.h
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.h
@@ -35,8 +35,7 @@ public:
DocId getFreeLid(DocId lidLimit);
DocId peekFreeLid(DocId lidLimit);
- void ensureSpace(uint32_t newSize,
- uint32_t newCapacity);
+ void ensureSpace(uint32_t newSize, uint32_t newCapacity);
void registerLid(DocId lid) { _usedLids.setBit(lid); }
void unregisterLid(DocId lid);
size_t getUsedLidsSize() const;
@@ -48,7 +47,7 @@ public:
generation_t currentGeneration);
bool holdLidOK(DocId lid, DocId lidLimit) const;
void constructFreeList(DocId lidLimit);
- search::queryeval::Blueprint::UP createWhiteListBlueprint(uint32_t docIdLimit) const;
+ search::queryeval::Blueprint::UP createWhiteListBlueprint() const;
void updateActiveLids(DocId lid, bool active);
void clearDocs(DocId lidLow, DocId lidLimit);
void shrinkLidSpace(DocId committedDocIdLimit);
diff --git a/searchcore/src/vespa/searchcore/proton/server/matchview.cpp b/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
index 7ba9b971715..61b37a47d09 100644
--- a/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/matchview.cpp
@@ -58,7 +58,6 @@ MatchView::getMatcher(const vespalib::string & rankProfile) const
return retval;
}
-
MatchContext::UP
MatchView::createContext() const {
IAttributeContext::UP attrCtx = _attrMgr->createContext();
@@ -66,7 +65,6 @@ MatchView::createContext() const {
return std::make_unique<MatchContext>(std::move(attrCtx), std::move(searchCtx));
}
-
std::unique_ptr<SearchReply>
MatchView::match(std::shared_ptr<const ISearchHandler> searchHandler, const SearchRequest &req,
vespalib::ThreadBundle &threadBundle) const
@@ -74,13 +72,12 @@ MatchView::match(std::shared_ptr<const ISearchHandler> searchHandler, const Sear
Matcher::SP matcher = getMatcher(req.ranking);
SearchSession::OwnershipBundle owned_objects;
owned_objects.search_handler = std::move(searchHandler);
+ owned_objects.readGuard = _metaStore->getReadGuard();
owned_objects.context = createContext();
- owned_objects.readGuard = _metaStore->getReadGuard();;
MatchContext *ctx = owned_objects.context.get();
const search::IDocumentMetaStore & dms = owned_objects.readGuard->get();
return matcher->match(req, threadBundle, ctx->getSearchContext(), ctx->getAttributeContext(),
*_sessionMgr, dms, std::move(owned_objects));
}
-
} // namespace proton
diff --git a/searchlib/src/tests/nativerank/nativerank.cpp b/searchlib/src/tests/nativerank/nativerank.cpp
index e5482d95d02..b28e385b597 100644
--- a/searchlib/src/tests/nativerank/nativerank.cpp
+++ b/searchlib/src/tests/nativerank/nativerank.cpp
@@ -170,7 +170,8 @@ Test::testNativeFieldMatch()
f.firstOccTable = &t;
f.numOccTable = &t;
p.vector.push_back(f);
- NativeFieldMatchExecutor nfme(ft.getQueryEnv(), p);
+ NativeFieldMatchExecutorSharedState nfmess(ft.getQueryEnv(), p);
+ NativeFieldMatchExecutor nfme(nfmess);
EXPECT_EQUAL(p.minFieldLength, 6u);
EXPECT_EQUAL(nfme.getFirstOccBoost(0, 0, 4), 0);
EXPECT_EQUAL(nfme.getFirstOccBoost(0, 1, 4), 1);
diff --git a/searchlib/src/vespa/searchlib/common/allocatedbitvector.cpp b/searchlib/src/vespa/searchlib/common/allocatedbitvector.cpp
index 3add7d1a328..659e3718a13 100644
--- a/searchlib/src/vespa/searchlib/common/allocatedbitvector.cpp
+++ b/searchlib/src/vespa/searchlib/common/allocatedbitvector.cpp
@@ -17,6 +17,28 @@ size_t computeCapacity(size_t capacity, size_t allocatedBytes) {
return possibleCapacity;
}
+// This is to ensure that we only read size and capacity once during copy
+// to ensure that they do not change unexpectedly under our feet due to resizing in different thread.
+std::pair<BitVector::Index, BitVector::Index>
+extract_size_size(const BitVector & bv) {
+ BitVector::Index size = bv.size();
+ return std::pair<BitVector::Index, BitVector::Index>(size, size);
+}
+
+std::pair<BitVector::Index, BitVector::Index>
+extract_size_capacity(const AllocatedBitVector & bv) {
+ BitVector::Index size = bv.size();
+ BitVector::Index capacity = bv.capacity();
+ while (capacity < size) {
+ // Since size and capacity might be changed in another thread we need
+ // this fallback to avoid inconsistency during shrink.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ size = bv.size();
+ capacity = bv.capacity();
+ }
+ return std::pair<BitVector::Index, BitVector::Index>(size, capacity);
+}
+
}
AllocatedBitVector::AllocatedBitVector(Index numberOfElements) :
@@ -56,21 +78,21 @@ AllocatedBitVector::AllocatedBitVector(Index numberOfElements, Index capacityBit
}
AllocatedBitVector::AllocatedBitVector(const AllocatedBitVector & rhs) :
- AllocatedBitVector(rhs, rhs.capacity())
+ AllocatedBitVector(rhs, extract_size_capacity(rhs))
{ }
AllocatedBitVector::AllocatedBitVector(const BitVector & rhs) :
- AllocatedBitVector(rhs, rhs.size())
+ AllocatedBitVector(rhs, extract_size_size(rhs))
{ }
-AllocatedBitVector::AllocatedBitVector(const BitVector & rhs, Index capacity_) :
+AllocatedBitVector::AllocatedBitVector(const BitVector & rhs, std::pair<Index, Index> size_capacity) :
BitVector(),
- _capacityBits(capacity_),
- _alloc(allocatePaddedAndAligned(0, rhs.size(), capacity_))
+ _capacityBits(size_capacity.second),
+ _alloc(allocatePaddedAndAligned(0, size_capacity.first, size_capacity.second))
{
_capacityBits = computeCapacity(_capacityBits, _alloc.size());
- memcpy(_alloc.get(), rhs.getStart(), rhs.sizeBytes());
- init(_alloc.get(), 0, rhs.size());
+ memcpy(_alloc.get(), rhs.getStart(), numBytes(size_capacity.first - rhs.getStartIndex()));
+ init(_alloc.get(), 0, size_capacity.first);
setBit(size());
updateCount();
}
diff --git a/searchlib/src/vespa/searchlib/common/allocatedbitvector.h b/searchlib/src/vespa/searchlib/common/allocatedbitvector.h
index c52c52354a1..5a7d2e634ea 100644
--- a/searchlib/src/vespa/searchlib/common/allocatedbitvector.h
+++ b/searchlib/src/vespa/searchlib/common/allocatedbitvector.h
@@ -73,7 +73,7 @@ private:
BitVector::swap(rhs);
}
- AllocatedBitVector(const BitVector &other, Index capacity);
+ AllocatedBitVector(const BitVector &other, std::pair<Index, Index> size_capacity);
/**
* Prepare for potential reuse where new value might be filled in by
diff --git a/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.cpp b/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.cpp
index 64ae94ddc90..376ed8cd3d3 100644
--- a/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.cpp
@@ -7,6 +7,7 @@
#include <vespa/searchlib/fef/indexproperties.h>
#include <vespa/searchlib/fef/itablemanager.h>
#include <vespa/searchlib/fef/properties.h>
+#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/stash.h>
using namespace search::fef;
@@ -15,12 +16,44 @@ namespace search::features {
const uint32_t NativeFieldMatchParam::NOT_DEF_FIELD_LENGTH(std::numeric_limits<uint32_t>::max());
+NativeFieldMatchExecutorSharedState::NativeFieldMatchExecutorSharedState(const IQueryEnvironment& env,
+ const NativeFieldMatchParams& params)
+ : fef::Anything(),
+ _params(params),
+ _query_terms(),
+ _divisor(0)
+{
+ QueryTermHelper queryTerms(env);
+ for (const QueryTerm & qtTmp : queryTerms.terms()) {
+ if (qtTmp.termData()->getWeight().percent() != 0) // only consider query terms with contribution
+ {
+ MyQueryTerm qt(qtTmp);
+ typedef search::fef::ITermFieldRangeAdapter FRA;
+ uint32_t totalFieldWeight = 0;
+ for (FRA iter(*qt.termData()); iter.valid(); iter.next()) {
+ const ITermFieldData& tfd = iter.get();
+ uint32_t fieldId = tfd.getFieldId();
+ if (_params.considerField(fieldId)) { // only consider fields with contribution
+ totalFieldWeight += _params.vector[fieldId].fieldWeight;
+ qt.handles().emplace_back(tfd.getHandle(), &tfd);
+ }
+ }
+ if (!qt.handles().empty()) {
+ _query_terms.push_back(qt);
+ _divisor += (qt.significance() * qt.termData()->getWeight().percent() * totalFieldWeight);
+ }
+ }
+ }
+}
+
+NativeFieldMatchExecutorSharedState::~NativeFieldMatchExecutorSharedState() = default;
+
feature_t
NativeFieldMatchExecutor::calculateScore(const MyQueryTerm &qt, uint32_t docId)
{
feature_t termScore = 0;
for (size_t i = 0; i < qt.handles().size(); ++i) {
- TermFieldHandle tfh = qt.handles()[i];
+ TermFieldHandle tfh = qt.handles()[i].first;
const TermFieldMatchData *tfmd = _md->resolveTermField(tfh);
const NativeFieldMatchParam & param = _params.vector[tfmd->getFieldId()];
if (tfmd->getDocId() == docId) { // do we have a hit
@@ -38,33 +71,17 @@ NativeFieldMatchExecutor::calculateScore(const MyQueryTerm &qt, uint32_t docId)
return termScore;
}
-NativeFieldMatchExecutor::NativeFieldMatchExecutor(const IQueryEnvironment & env,
- const NativeFieldMatchParams & params) :
- FeatureExecutor(),
- _params(params),
- _queryTerms(),
- _divisor(0),
- _md(nullptr)
+NativeFieldMatchExecutor::NativeFieldMatchExecutor(const NativeFieldMatchExecutorSharedState& shared_state)
+ : FeatureExecutor(),
+ _params(shared_state.get_params()),
+ _queryTerms(shared_state.get_query_terms()),
+ _divisor(shared_state.get_divisor()),
+ _md(nullptr)
{
- QueryTermHelper queryTerms(env);
- for (const QueryTerm & qtTmp : queryTerms.terms()) {
- if (qtTmp.termData()->getWeight().percent() != 0) // only consider query terms with contribution
- {
- MyQueryTerm qt(qtTmp);
- typedef search::fef::ITermFieldRangeAdapter FRA;
- uint32_t totalFieldWeight = 0;
- for (FRA iter(*qt.termData()); iter.valid(); iter.next()) {
- const ITermFieldData& tfd = iter.get();
- uint32_t fieldId = tfd.getFieldId();
- if (_params.considerField(fieldId)) { // only consider fields with contribution
- totalFieldWeight += _params.vector[fieldId].fieldWeight;
- qt.handles().push_back(tfd.getHandle());
- }
- }
- if (!qt.handles().empty()) {
- _queryTerms.push_back(qt);
- _divisor += (qt.significance() * qt.termData()->getWeight().percent() * totalFieldWeight);
- }
+ for (const auto& qt : _queryTerms) {
+ for (const auto& handle : qt.handles()) {
+ // Record that we need normal term field match data
+ (void) handle.second->getHandle(MatchDataDetails::Normal);
}
}
}
@@ -92,7 +109,8 @@ NativeFieldMatchBlueprint::NativeFieldMatchBlueprint() :
Blueprint("nativeFieldMatch"),
_params(),
_defaultFirstOcc("expdecay(8000,12.50)"),
- _defaultNumOcc("loggrowth(1500,4000,19)")
+ _defaultNumOcc("loggrowth(1500,4000,19)"),
+ _shared_state_key()
{
}
@@ -116,9 +134,12 @@ bool
NativeFieldMatchBlueprint::setup(const IIndexEnvironment & env,
const ParameterList & params)
{
+ vespalib::asciistream shared_state_key_builder;
_params.resize(env.getNumFields());
FieldWrapper fields(env, params, FieldType::INDEX);
vespalib::string defaultFirstOccImportance = env.getProperties().lookup(getBaseName(), "firstOccurrenceImportance").get("0.5");
+ shared_state_key_builder << "fef.nativeFieldMatch[";
+ bool first_field = true;
for (uint32_t i = 0; i < fields.getNumFields(); ++i) {
const FieldInfo * info = fields.getField(i);
uint32_t fieldId = info->id();
@@ -160,8 +181,16 @@ NativeFieldMatchBlueprint::setup(const IIndexEnvironment & env,
}
if (param.field) {
env.hintFieldAccess(fieldId);
+ if (first_field) {
+ first_field = false;
+ } else {
+ shared_state_key_builder << ",";
+ }
+ shared_state_key_builder << info->name();
}
}
+ shared_state_key_builder << "]";
+ _shared_state_key = shared_state_key_builder.str();
_params.minFieldLength = util::strToNum<uint32_t>(env.getProperties().lookup
(getBaseName(), "minFieldLength").get("6"));
@@ -172,17 +201,23 @@ NativeFieldMatchBlueprint::setup(const IIndexEnvironment & env,
FeatureExecutor &
NativeFieldMatchBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash &stash) const
{
- NativeFieldMatchExecutor &native = stash.create<NativeFieldMatchExecutor>(env, _params);
- if (native.empty()) {
+ auto *shared_state = dynamic_cast<const NativeFieldMatchExecutorSharedState *>(env.getObjectStore().get(_shared_state_key));
+ if (shared_state == nullptr) {
+ shared_state = &stash.create<NativeFieldMatchExecutorSharedState>(env, _params);
+ }
+ if (shared_state->empty()) {
return stash.create<SingleZeroValueExecutor>();
} else {
- return native;
+ return stash.create<NativeFieldMatchExecutor>(*shared_state);
}
}
void
NativeFieldMatchBlueprint::prepareSharedState(const IQueryEnvironment &queryEnv, IObjectStore &objectStore) const {
QueryTermHelper::lookupAndStoreQueryTerms(queryEnv, objectStore);
+ if (objectStore.get(_shared_state_key) == nullptr) {
+ objectStore.add(_shared_state_key, std::make_unique<NativeFieldMatchExecutorSharedState>(queryEnv, _params));
+ }
}
}
diff --git a/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.h b/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.h
index 9b132561cd3..5e8d865e159 100644
--- a/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.h
+++ b/searchlib/src/vespa/searchlib/features/nativefieldmatchfeature.h
@@ -29,13 +29,12 @@ public:
};
/**
- * Implements the executor for calculating the native field match score.
- **/
-class NativeFieldMatchExecutor : public fef::FeatureExecutor
-{
-private:
- typedef std::vector<fef::TermFieldHandle> HandleVector;
-
+ * Class containing shared state for native field match executor.
+ */
+class NativeFieldMatchExecutorSharedState : public fef::Anything {
+public:
+ using WrappedHandle = std::pair<fef::TermFieldHandle, const fef::ITermFieldData*>;
+ using HandleVector = std::vector<WrappedHandle>;
class MyQueryTerm : public QueryTerm
{
private:
@@ -45,8 +44,28 @@ private:
HandleVector &handles() { return _handles; }
const HandleVector &handles() const { return _handles; }
};
+private:
+ const NativeFieldMatchParams& _params;
+ std::vector<MyQueryTerm> _query_terms;
+ feature_t _divisor;
+public:
+ NativeFieldMatchExecutorSharedState(const fef::IQueryEnvironment& env, const NativeFieldMatchParams& params);
+ ~NativeFieldMatchExecutorSharedState();
+ const NativeFieldMatchParams& get_params() const { return _params; }
+ const std::vector<MyQueryTerm>& get_query_terms() const { return _query_terms; }
+ feature_t get_divisor() const { return _divisor; }
+ bool empty() const { return _query_terms.empty(); }
+};
+
+/**
+ * Implements the executor for calculating the native field match score.
+ **/
+class NativeFieldMatchExecutor : public fef::FeatureExecutor
+{
+private:
+ using MyQueryTerm = NativeFieldMatchExecutorSharedState::MyQueryTerm;
const NativeFieldMatchParams & _params;
- std::vector<MyQueryTerm> _queryTerms;
+ vespalib::ConstArrayRef<MyQueryTerm> _queryTerms;
feature_t _divisor;
const fef::MatchData *_md;
@@ -74,8 +93,7 @@ private:
virtual void handle_bind_match_data(const fef::MatchData &md) override;
public:
- NativeFieldMatchExecutor(const fef::IQueryEnvironment & env,
- const NativeFieldMatchParams & params);
+ NativeFieldMatchExecutor(const NativeFieldMatchExecutorSharedState& shared_state);
void execute(uint32_t docId) override;
feature_t getFirstOccBoost(uint32_t field, uint32_t position, uint32_t fieldLength) const {
@@ -85,7 +103,6 @@ public:
feature_t getNumOccBoost(uint32_t field, uint32_t occs, uint32_t fieldLength) const {
return getNumOccBoost(_params.vector[field], occs, fieldLength);
}
- bool empty() const { return _queryTerms.empty(); }
};
@@ -97,6 +114,7 @@ private:
NativeFieldMatchParams _params;
vespalib::string _defaultFirstOcc;
vespalib::string _defaultNumOcc;
+ vespalib::string _shared_state_key;
public:
NativeFieldMatchBlueprint();
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
index 3284530392f..8f0e70f554c 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
@@ -72,7 +72,7 @@ public class OperationHandlerImpl implements OperationHandler {
public static final int VISIT_TIMEOUT_MS = 120000;
public static final int WANTED_DOCUMENT_COUNT_UPPER_BOUND = 1000; // Approximates the max default size of a bucket
- public static final int CONCURRENCY_UPPER_BOUND = 200;
+ public static final int CONCURRENCY_UPPER_BOUND = 100;
private final DocumentAccess documentAccess;
private final DocumentApiMetrics metricsHelper;
private final ClusterEnumerator clusterEnumerator;