summaryrefslogtreecommitdiffstats
path: root/configserver/src
diff options
context:
space:
mode:
authorHarald Musum <musum@yahooinc.com>2022-08-22 14:12:59 +0200
committerHarald Musum <musum@yahooinc.com>2022-08-22 14:12:59 +0200
commitcf1afa8d727c6a8013b97fb39a97605feacb08a9 (patch)
treeb69ef1dec52db3880f21be3524114c7245493f4c /configserver/src
parent675463f4e421d074e84abedd91117818adf40d51 (diff)
parent5a9875b516d43bfa367b5f3083f9bc58e7d8f795 (diff)
Merge branch 'master' into hmusum/cleanup-15
Diffstat (limited to 'configserver/src')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java80
-rwxr-xr-xconfigserver/src/main/sh/start-configserver10
-rw-r--r--configserver/src/test/apps/illegalApp2/hosts.xml7
-rw-r--r--configserver/src/test/apps/illegalApp2/schemas/music.sd50
-rw-r--r--configserver/src/test/apps/illegalApp2/services.xml39
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java49
9 files changed, 185 insertions, 59 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
index 1aa70ff4b5b..59a48ad3c7e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
@@ -25,7 +25,6 @@ import com.yahoo.vespa.config.server.application.ConfigNotConvergedException;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
import com.yahoo.vespa.config.server.configchange.ReindexActions;
import com.yahoo.vespa.config.server.configchange.RestartActions;
-import com.yahoo.vespa.config.server.session.LocalSession;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.session.Session;
import com.yahoo.vespa.config.server.session.SessionRepository;
@@ -161,9 +160,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
}
private void deleteSession() {
- SessionRepository sessionRepository = sessionRepository();
- LocalSession localSession = sessionRepository.getLocalSession(session.getSessionId());
- sessionRepository.deleteLocalSession(localSession);
+ sessionRepository().deleteLocalSession(session.getSessionId());
}
private SessionRepository sessionRepository() {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 068323f7784..7c7a12bbf36 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -166,6 +166,7 @@ public class ModelContextImpl implements ModelContext {
public static class FeatureFlags implements ModelContext.FeatureFlags {
+ private final String queryDispatchPolicy;
private final double defaultTermwiseLimit;
private final boolean useThreePhaseUpdates;
private final String feedSequencer;
@@ -276,8 +277,10 @@ public class ModelContextImpl implements ModelContext {
this.mbus_cpp_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_CPP_EVENTS_BEFORE_WAKEUP);
this.rpc_num_targets = flagValue(source, appId, version, Flags.RPC_NUM_TARGETS);
this.rpc_events_before_wakeup = flagValue(source, appId, version, Flags.RPC_EVENTS_BEFORE_WAKEUP);
+ this.queryDispatchPolicy = flagValue(source, appId, version, Flags.QUERY_DISPATCH_POLICY);
}
+ @Override public String queryDispatchPolicy() { return queryDispatchPolicy;}
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@Override public boolean useThreePhaseUpdates() { return useThreePhaseUpdates; }
@Override public String feedSequencerType() { return feedSequencer; }
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index f2fbff84907..fc33830e707 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -39,7 +39,6 @@ import com.yahoo.vespa.config.server.http.v2.response.ReindexingResponse;
import com.yahoo.vespa.config.server.tenant.Tenant;
import java.io.IOException;
-import java.io.OutputStream;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index a6bbd6c20a2..2e9c28bdc3b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -221,15 +221,31 @@ public class SessionRepository {
Set<LocalSession> sessionIds = new HashSet<>();
for (File session : sessions) {
long sessionId = Long.parseLong(session.getName());
- SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionId);
- File sessionDir = getAndValidateExistingSessionAppDir(sessionId);
- ApplicationPackage applicationPackage = FilesApplicationPackage.fromFile(sessionDir);
- LocalSession localSession = new LocalSession(tenantName, sessionId, applicationPackage, sessionZKClient);
+ LocalSession localSession = getSessionFromFile(sessionId);
sessionIds.add(localSession);
}
return sessionIds;
}
+ private LocalSession getSessionFromFile(long sessionId) {
+ SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionId);
+ File sessionDir = getAndValidateExistingSessionAppDir(sessionId);
+ ApplicationPackage applicationPackage = FilesApplicationPackage.fromFile(sessionDir);
+ return new LocalSession(tenantName, sessionId, applicationPackage, sessionZKClient);
+ }
+
+ public Set<Long> getLocalSessionsIdsFromFileSystem() {
+ File[] sessions = tenantFileSystemDirs.sessionsPath().listFiles(sessionApplicationsFilter);
+ if (sessions == null) return Set.of();
+
+ Set<Long> sessionIds = new HashSet<>();
+ for (File session : sessions) {
+ long sessionId = Long.parseLong(session.getName());
+ sessionIds.add(sessionId);
+ }
+ return sessionIds;
+ }
+
public ConfigChangeActions prepareLocalSession(Session session, DeployLogger logger, PrepareParams params, Instant now) {
params.vespaVersion().ifPresent(version -> {
if ( ! params.isBootstrap() && ! modelFactoryRegistry.allVersions().contains(version))
@@ -310,8 +326,7 @@ public class SessionRepository {
}
// Will delete session data in ZooKeeper and file system
- public void deleteLocalSession(LocalSession session) {
- long sessionId = session.getSessionId();
+ public void deleteLocalSession(long sessionId) {
log.log(Level.FINE, () -> "Deleting local session " + sessionId);
SessionStateWatcher watcher = sessionStateWatchers.remove(sessionId);
if (watcher != null) watcher.close();
@@ -323,7 +338,7 @@ public class SessionRepository {
private void deleteAllSessions() {
for (LocalSession session : getLocalSessions()) {
- deleteLocalSession(session);
+ deleteLocalSession(session.getSessionId());
}
}
@@ -586,35 +601,48 @@ public class SessionRepository {
public void deleteExpiredSessions(Map<ApplicationId, Long> activeSessions) {
log.log(Level.FINE, () -> "Deleting expired local sessions for tenant '" + tenantName + "'");
- Set<LocalSession> toDelete = new HashSet<>();
+ Set<Long> sessionIdsToDelete = new HashSet<>();
Set<Long> newSessions = findNewSessionsInFileSystem();
try {
- for (LocalSession candidate : getLocalSessionsFromFileSystem()) {
+ for (long sessionId : getLocalSessionsIdsFromFileSystem()) {
// Skip sessions newly added (we might have a session in the file system, but not in ZooKeeper,
// we don't want to touch any of them)
- if (newSessions.contains(candidate.getSessionId()))
+ if (newSessions.contains(sessionId))
continue;
- Instant createTime = candidate.getCreateTime();
- log.log(Level.FINE, () -> "Candidate local session for deletion: " + candidate.getSessionId() +
- ", created: " + createTime + ", state " + candidate.getStatus() + ", can be deleted: " + canBeDeleted(candidate));
+ var sessionZooKeeperClient = createSessionZooKeeperClient(sessionId);
+ Instant createTime = sessionZooKeeperClient.readCreateTime();
+ Session.Status status = sessionZooKeeperClient.readStatus();
- if (hasExpired(createTime) && canBeDeleted(candidate)) {
- toDelete.add(candidate);
+ log.log(Level.FINE, () -> "Candidate local session for deletion: " + sessionId +
+ ", created: " + createTime + ", status " + status + ", can be deleted: " + canBeDeleted(sessionId, status) +
+ ", hasExpired: " + hasExpired(createTime));
+
+ if (hasExpired(createTime) && canBeDeleted(sessionId, status)) {
+ log.log(Level.FINE, () -> "expired: " + hasExpired(createTime) + ", can be deleted: " + canBeDeleted(sessionId, status));
+ sessionIdsToDelete.add(sessionId);
} else if (createTime.plus(Duration.ofDays(1)).isBefore(clock.instant())) {
- Optional<ApplicationId> applicationId = candidate.getOptionalApplicationId();
+ LocalSession session;
+ log.log(Level.FINE, () -> "not expired, but more than 1 day old: " + sessionId);
+ try {
+ session = getSessionFromFile(sessionId);
+ } catch (Exception e) {
+ log.log(Level.FINE, () -> "could not get session from file: " + sessionId + ": " + e.getMessage());
+ continue;
+ }
+ Optional<ApplicationId> applicationId = session.getOptionalApplicationId();
if (applicationId.isEmpty()) continue;
Long activeSession = activeSessions.get(applicationId.get());
- if (activeSession == null || activeSession != candidate.getSessionId()) {
- toDelete.add(candidate);
- log.log(Level.FINE, () -> "Will delete inactive session " + candidate.getSessionId() + " created " +
+ if (activeSession == null || activeSession != sessionId) {
+ sessionIdsToDelete.add(sessionId);
+ log.log(Level.FINE, () -> "Will delete inactive session " + sessionId + " created " +
createTime + " for '" + applicationId + "'");
}
}
}
- toDelete.forEach(this::deleteLocalSession);
+ sessionIdsToDelete.forEach(this::deleteLocalSession);
// Make sure to catch here, to avoid executor just dying in case of issues ...
} catch (Throwable e) {
@@ -628,16 +656,16 @@ public class SessionRepository {
}
// Sessions with state other than UNKNOWN or ACTIVATE or old sessions in UNKNOWN state
- private boolean canBeDeleted(LocalSession candidate) {
- return ( ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(candidate.getStatus()))
- || oldSessionDirWithUnknownStatus(candidate);
+ private boolean canBeDeleted(long sessionId, Session.Status status) {
+ return ( ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(status))
+ || oldSessionDirWithUnknownStatus(sessionId, status);
}
- private boolean oldSessionDirWithUnknownStatus(LocalSession session) {
+ private boolean oldSessionDirWithUnknownStatus(long sessionId, Session.Status status) {
Duration expiryTime = Duration.ofHours(configserverConfig.keepSessionsWithUnknownStatusHours());
- File sessionDir = tenantFileSystemDirs.getUserApplicationDir(session.getSessionId());
+ File sessionDir = tenantFileSystemDirs.getUserApplicationDir(sessionId);
return sessionDir.exists()
- && session.getStatus() == Session.Status.UNKNOWN
+ && status == Session.Status.UNKNOWN
&& created(sessionDir).plus(expiryTime).isBefore(clock.instant());
}
diff --git a/configserver/src/main/sh/start-configserver b/configserver/src/main/sh/start-configserver
index 8127b0bfafc..f223c0a8fb9 100755
--- a/configserver/src/main/sh/start-configserver
+++ b/configserver/src/main/sh/start-configserver
@@ -151,9 +151,7 @@ export standalone_jdisc_container__deployment_profile=configserver
# class path
CP="${VESPA_HOME}/lib/jars/jdisc_core-jar-with-dependencies.jar"
-baseuserargs="$VESPA_CONFIGSERVER_JVMARGS"
-serveruserargs="$cloudconfig_server__jvmargs"
-jvmargs="$baseuserargs $serveruserargs"
+jvmoptions="$VESPA_CONFIGSERVER_JVMARGS"
export LD_PRELOAD=${VESPA_HOME}/lib64/vespa/malloc/libvespamalloc.so
@@ -161,8 +159,8 @@ rm -f $cfpfile
vespa-run-as-vespa-user sh -c "printenv > $cfpfile"
fixddir $bundlecachedir
-heap_min=$(get_min_heap_mb "${jvmargs}" 128)
-heap_max=$(get_max_heap_mb "${jvmargs}" 2048)
+heap_min=$(get_min_heap_mb "${jvmoptions}" 128)
+heap_max=$(get_max_heap_mb "${jvmoptions}" 2048)
vespa-run-as-vespa-user vespa-runserver -s ${VESPA_SERVICE_NAME} -r 30 -p $pidfile -- \
java \
-Xms${heap_min}m -Xmx${heap_max}m \
@@ -174,7 +172,7 @@ vespa-run-as-vespa-user vespa-runserver -s ${VESPA_SERVICE_NAME} -r 30 -p $pidfi
-XX:+ExitOnOutOfMemoryError \
-XX:-OmitStackTraceInFastThrow \
-XX:MaxJavaStackTraceDepth=1000000 \
- $jvmargs \
+ $jvmoptions \
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.net=ALL-UNNAMED \
diff --git a/configserver/src/test/apps/illegalApp2/hosts.xml b/configserver/src/test/apps/illegalApp2/hosts.xml
new file mode 100644
index 00000000000..a515a4e97da
--- /dev/null
+++ b/configserver/src/test/apps/illegalApp2/hosts.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<hosts>
+ <host name="mytesthost">
+ <alias>node1</alias>
+ </host>
+</hosts>
diff --git a/configserver/src/test/apps/illegalApp2/schemas/music.sd b/configserver/src/test/apps/illegalApp2/schemas/music.sd
new file mode 100644
index 00000000000..f4b11d1e8e4
--- /dev/null
+++ b/configserver/src/test/apps/illegalApp2/schemas/music.sd
@@ -0,0 +1,50 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+# A basic search definition - called music, should be saved to music.sd
+search music {
+
+ # It contains one document type only - called music as well
+ document music {
+
+ field title type string {
+ indexing: summary | index # How this field should be indexed
+ # index-to: title, default # Create two indexes
+ weight: 75 # Ranking importancy of this field, used by the built in nativeRank feature
+ }
+
+ field artist type string {
+ indexing: summary | attribute | index
+ # index-to: artist, default
+
+ weight: 25
+ }
+
+ field year type int {
+ indexing: summary | attribute
+ }
+
+ # Increase query
+ field popularity type int {
+ indexing: summary | attribute
+ }
+
+ field url type uri {
+ indexing: summary | index
+ }
+
+ }
+
+ rank-profile default inherits default {
+ first-phase {
+ expression: nativeRank(title,artist) + attribute(popularity)
+ }
+
+ }
+
+ rank-profile textmatch inherits default {
+ first-phase {
+ expression: nativeRank(title,artist)
+ }
+
+ }
+
+}
diff --git a/configserver/src/test/apps/illegalApp2/services.xml b/configserver/src/test/apps/illegalApp2/services.xml
new file mode 100644
index 00000000000..3e957a0e228
--- /dev/null
+++ b/configserver/src/test/apps/illegalApp2/services.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<services version="1.0">
+
+ <admin version="2.0">
+ <adminserver hostalias="node1"/>
+ <logserver hostalias="node1" />
+ </admin>
+
+ <content version="1.0">
+ <redundancy>2</redundancy>
+ <documents>
+ <document type="music" mode="index"/>
+ </documents>
+ <nodes>
+ <node hostalias="node1" distribution-key="0"/>
+ </nodes>
+
+ </content>
+
+ <container version="1.0">
+ <include dir='file:///etc/passwd'/>
+ <document-processing compressdocuments="true">
+ <chain id="ContainerWrapperTest">
+ <documentprocessor id="com.yahoo.vespa.config.AppleDocProc"/>
+ </chain>
+ </document-processing>
+
+ <config name="project.specific">
+ <value>someval</value>
+ </config>
+
+ <nodes>
+ <node hostalias="node1" />
+ </nodes>
+
+ </container>
+
+</services>
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index d1d8c165124..a8439a9061c 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -59,9 +59,7 @@ import org.junit.rules.TemporaryFolder;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.io.UncheckedIOException;
import java.nio.file.Files;
-import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
@@ -94,6 +92,7 @@ public class ApplicationRepositoryTest {
private final static File testAppLogServerWithContainer = new File("src/test/apps/app-logserver-with-container");
private final static File app1 = new File("src/test/apps/cs1");
private final static File app2 = new File("src/test/apps/cs2");
+ private final static File illegalApp2 = new File("src/test/apps/illegalapp2");
private final static TenantName tenant1 = TenantName.from("test1");
private final static TenantName tenant2 = TenantName.from("test2");
@@ -456,7 +455,7 @@ public class ApplicationRepositoryTest {
deployment4.get().prepare(); // session 5 (not activated)
assertEquals(2, sessionRepository.getLocalSessions().size());
- sessionRepository.deleteLocalSession(localSession);
+ sessionRepository.deleteLocalSession(localSession.getSessionId());
assertEquals(1, sessionRepository.getLocalSessions().size());
// Create a local session without any data in zookeeper (corner case seen in production occasionally)
@@ -464,28 +463,29 @@ public class ApplicationRepositoryTest {
int sessionId = 6;
TenantName tenantName = tester.tenant().getName();
Instant session6CreateTime = clock.instant();
- Files.createDirectory(new TenantFileSystemDirs(serverdb, tenantName).getUserApplicationDir(sessionId).toPath());
- LocalSession localSession2 = new LocalSession(tenant1,
+ TenantFileSystemDirs tenantFileSystemDirs = new TenantFileSystemDirs(serverdb, tenantName);
+ Files.createDirectory(tenantFileSystemDirs.getUserApplicationDir(sessionId).toPath());
+ String hostName = ConfigUtils.getCanonicalHostName();
+ LocalSession localSession2 = new LocalSession(tenantName,
sessionId,
FilesApplicationPackage.fromFile(testApp),
- new SessionZooKeeperClient(curator,
- tenantName,
- sessionId,
- ConfigUtils.getCanonicalHostName()));
+ new SessionZooKeeperClient(curator, tenantName, sessionId, hostName));
sessionRepository.addLocalSession(localSession2);
assertEquals(2, sessionRepository.getLocalSessions().size());
// Create a session, set status to UNKNOWN, we don't want to expire those (creation time is then EPOCH,
// so will be candidate for expiry)
- Session session = sessionRepository.createRemoteSession(7);
- sessionRepository.createSetStatusTransaction(session, Session.Status.UNKNOWN);
+ sessionId = 7;
+ Session session = sessionRepository.createRemoteSession(sessionId);
+ sessionRepository.createSessionZooKeeperClient(sessionId).createNewSession(clock.instant());
+ sessionRepository.createSetStatusTransaction(session, Session.Status.UNKNOWN).commit();
assertEquals(2, sessionRepository.getLocalSessions().size()); // Still 2, no new local session
// Check that trying to expire local session when there exists a local session without any data in zookeeper
// should not delete session if this is a new file ...
deleteExpiredLocalSessionsAndAssertNumberOfSessions(2, tester, sessionRepository);
- // ... but it should be deleted if some time has passed
+ // ... but it should be deleted when some time has passed
clock.advance(Duration.ofSeconds(60));
deleteExpiredLocalSessionsAndAssertNumberOfSessions(1, tester, sessionRepository);
@@ -495,6 +495,21 @@ public class ApplicationRepositoryTest {
// Advance time, session SHOULD be deleted
clock.advance(Duration.ofHours(configserverConfig.keepSessionsWithUnknownStatusHours()).plus(Duration.ofMinutes(1)));
deleteExpiredLocalSessionsAndAssertNumberOfSessions(0, tester, sessionRepository);
+
+ // Create a local session with invalid application package and check that expiring local sessions still works
+ sessionId = 8;
+ java.nio.file.Path applicationPath = tenantFileSystemDirs.getUserApplicationDir(sessionId).toPath();
+ session = sessionRepository.createRemoteSession(sessionId);
+ sessionRepository.createSessionZooKeeperClient(sessionId).createNewSession(clock.instant());
+ sessionRepository.createSetStatusTransaction(session, Session.Status.PREPARE).commit();
+ Files.createDirectory(applicationPath);
+ Files.writeString(Files.createFile(applicationPath.resolve("services.xml")), "non-legal xml");
+ assertEquals(0, sessionRepository.getLocalSessions().size()); // Will not show up in local sessions
+
+ // Advance time, session SHOULD be deleted
+ clock.advance(Duration.ofHours(configserverConfig.keepSessionsWithUnknownStatusHours()).plus(Duration.ofMinutes(1)));
+ deleteExpiredLocalSessionsAndAssertNumberOfSessions(0, tester, sessionRepository);
+ assertFalse(applicationPath.toFile().exists()); // App has been deleted
}
@Test
@@ -737,16 +752,6 @@ public class ApplicationRepositoryTest {
return applicationRepository.getMetadataFromLocalSession(tenant, sessionId);
}
- private void setCreatedTime(java.nio.file.Path file, Instant createdTime) {
- try {
- BasicFileAttributeView attributes = Files.getFileAttributeView(file, BasicFileAttributeView.class);
- FileTime time = FileTime.fromMillis(createdTime.toEpochMilli());
- attributes.setTimes(time, time, time);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
/** Stores all added or set values for each metric and context. */
static class MockMetric implements Metric {