Implement StalenessChecker for projects
This commit implements a staleness checker for projects in the same fashion that we have it for groups and accounts. It uses the newly added ref_state field. Change-Id: Icbcf0bd1c700df789ef26501e4218956d718ea4c
This commit is contained in:
@@ -68,6 +68,8 @@ import com.google.gerrit.extensions.common.EditInfo;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.index.project.ProjectIndex;
|
||||
import com.google.gerrit.index.project.ProjectIndexCollection;
|
||||
import com.google.gerrit.mail.Address;
|
||||
import com.google.gerrit.mail.EmailHeader;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
@@ -274,6 +276,7 @@ public abstract class AbstractDaemonTest {
|
||||
|
||||
@Inject private ChangeIndexCollection changeIndexes;
|
||||
@Inject private AccountIndexCollection accountIndexes;
|
||||
@Inject private ProjectIndexCollection projectIndexes;
|
||||
@Inject private EventRecorder.Factory eventRecorderFactory;
|
||||
@Inject private InProcessProtocol inProcessProtocol;
|
||||
@Inject private Provider<AnonymousUser> anonymousUser;
|
||||
@@ -900,6 +903,41 @@ public abstract class AbstractDaemonTest {
|
||||
};
|
||||
}
|
||||
|
||||
protected AutoCloseable disableProjectIndex() {
|
||||
disableProjectIndexWrites();
|
||||
ProjectIndex searchIndex = projectIndexes.getSearchIndex();
|
||||
if (!(searchIndex instanceof DisabledProjectIndex)) {
|
||||
projectIndexes.setSearchIndex(new DisabledProjectIndex(searchIndex), false);
|
||||
}
|
||||
|
||||
return new AutoCloseable() {
|
||||
@Override
|
||||
public void close() {
|
||||
enableProjectIndexWrites();
|
||||
ProjectIndex searchIndex = projectIndexes.getSearchIndex();
|
||||
if (searchIndex instanceof DisabledProjectIndex) {
|
||||
projectIndexes.setSearchIndex(((DisabledProjectIndex) searchIndex).unwrap(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void disableProjectIndexWrites() {
|
||||
for (ProjectIndex i : projectIndexes.getWriteIndexes()) {
|
||||
if (!(i instanceof DisabledProjectIndex)) {
|
||||
projectIndexes.addWriteIndex(new DisabledProjectIndex(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void enableProjectIndexWrites() {
|
||||
for (ProjectIndex i : projectIndexes.getWriteIndexes()) {
|
||||
if (i instanceof DisabledProjectIndex) {
|
||||
projectIndexes.addWriteIndex(((DisabledProjectIndex) i).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static Gson newGson() {
|
||||
return OutputFormat.JSON_COMPACT.newGson();
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ java_library2(
|
||||
"//java/com/google/gerrit/extensions:api",
|
||||
"//java/com/google/gerrit/httpd",
|
||||
"//java/com/google/gerrit/index",
|
||||
"//java/com/google/gerrit/index/project",
|
||||
"//java/com/google/gerrit/lucene",
|
||||
"//java/com/google/gerrit/mail",
|
||||
"//java/com/google/gerrit/metrics",
|
||||
|
||||
76
java/com/google/gerrit/acceptance/DisabledProjectIndex.java
Normal file
76
java/com/google/gerrit/acceptance/DisabledProjectIndex.java
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2018 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.acceptance;
|
||||
|
||||
import com.google.gerrit.index.QueryOptions;
|
||||
import com.google.gerrit.index.Schema;
|
||||
import com.google.gerrit.index.project.ProjectData;
|
||||
import com.google.gerrit.index.project.ProjectIndex;
|
||||
import com.google.gerrit.index.query.DataSource;
|
||||
import com.google.gerrit.index.query.Predicate;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
||||
/**
|
||||
* This class wraps an index and assumes the search index can't handle any queries. However, it does
|
||||
* return the current schema as the assumption is that we need a search index for starting Gerrit in
|
||||
* the first place and only later lose the index connection (making it so that we can't send
|
||||
* requests there anymore).
|
||||
*/
|
||||
public class DisabledProjectIndex implements ProjectIndex {
|
||||
private final ProjectIndex index;
|
||||
|
||||
public DisabledProjectIndex(ProjectIndex index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public ProjectIndex unwrap() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schema<ProjectData> getSchema() {
|
||||
return index.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
index.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(ProjectData obj) {
|
||||
throw new UnsupportedOperationException("ProjectIndex is disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Project.NameKey key) {
|
||||
throw new UnsupportedOperationException("ProjectIndex is disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
throw new UnsupportedOperationException("ProjectIndex is disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts) {
|
||||
throw new UnsupportedOperationException("ProjectIndex is disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markReady(boolean ready) {
|
||||
throw new UnsupportedOperationException("ProjectIndex is disabled");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2018 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.index.project;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.gerrit.index.IndexConfig;
|
||||
import com.google.gerrit.index.QueryOptions;
|
||||
import com.google.gerrit.index.RefState;
|
||||
import com.google.gerrit.index.project.ProjectData;
|
||||
import com.google.gerrit.index.project.ProjectField;
|
||||
import com.google.gerrit.index.project.ProjectIndex;
|
||||
import com.google.gerrit.index.project.ProjectIndexCollection;
|
||||
import com.google.gerrit.index.query.FieldBundle;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class StalenessChecker {
|
||||
private static final ImmutableSet<String> FIELDS =
|
||||
ImmutableSet.of(ProjectField.NAME.getName(), ProjectField.REF_STATE.getName());
|
||||
|
||||
private final ProjectCache projectCache;
|
||||
private final ProjectIndexCollection indexes;
|
||||
private final IndexConfig indexConfig;
|
||||
|
||||
@Inject
|
||||
StalenessChecker(
|
||||
ProjectCache projectCache, ProjectIndexCollection indexes, IndexConfig indexConfig) {
|
||||
this.projectCache = projectCache;
|
||||
this.indexes = indexes;
|
||||
this.indexConfig = indexConfig;
|
||||
}
|
||||
|
||||
public boolean isStale(Project.NameKey project) throws IOException {
|
||||
ProjectData projectData = projectCache.get(project).toProjectData();
|
||||
ProjectIndex i = indexes.getSearchIndex();
|
||||
if (i == null) {
|
||||
return false; // No index; caller couldn't do anything if it is stale.
|
||||
}
|
||||
|
||||
Optional<FieldBundle> result =
|
||||
i.getRaw(project, QueryOptions.create(indexConfig, 0, 1, FIELDS));
|
||||
if (!result.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SetMultimap<Project.NameKey, RefState> indexedRefStates =
|
||||
RefState.parseStates(result.get().getValue(ProjectField.REF_STATE));
|
||||
|
||||
SetMultimap<Project.NameKey, RefState> currentRefStates =
|
||||
MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
projectData
|
||||
.tree()
|
||||
.stream()
|
||||
.filter(p -> p.getProject().getConfigRefState() != null)
|
||||
.forEach(
|
||||
p ->
|
||||
currentRefStates.put(
|
||||
p.getProject().getNameKey(),
|
||||
RefState.create(RefNames.REFS_CONFIG, p.getProject().getConfigRefState())));
|
||||
|
||||
return !currentRefStates.equals(indexedRefStates);
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,13 @@ import com.google.gerrit.index.project.ProjectIndexCollection;
|
||||
import com.google.gerrit.index.project.ProjectIndexer;
|
||||
import com.google.gerrit.index.query.FieldBundle;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.index.project.StalenessChecker;
|
||||
import com.google.gerrit.server.project.ProjectConfig;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
@@ -41,6 +44,7 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
|
||||
@Inject private ProjectIndexer projectIndexer;
|
||||
@Inject private ProjectIndexCollection indexes;
|
||||
@Inject private IndexConfig indexConfig;
|
||||
@Inject private StalenessChecker stalenessChecker;
|
||||
|
||||
private static final ImmutableSet<String> FIELDS =
|
||||
ImmutableSet.of(ProjectField.NAME.getName(), ProjectField.REF_STATE.getName());
|
||||
@@ -72,4 +76,54 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
|
||||
allProjects,
|
||||
ImmutableSet.of(RefState.of(allProjectConfigRef)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stalenessChecker_currentProject_notStale() throws Exception {
|
||||
assertThat(stalenessChecker.isStale(project)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stalenessChecker_currentProjectUpdates_isStale() throws Exception {
|
||||
updateProjectConfigWithoutIndexUpdate(project);
|
||||
assertThat(stalenessChecker.isStale(project)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stalenessChecker_parentProjectUpdates_isStale() throws Exception {
|
||||
updateProjectConfigWithoutIndexUpdate(allProjects);
|
||||
assertThat(stalenessChecker.isStale(project)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stalenessChecker_hierarchyChange_isStale() throws Exception {
|
||||
Project.NameKey p1 = createProject("p1", allProjects);
|
||||
Project.NameKey p2 = createProject("p2", allProjects);
|
||||
try (ProjectConfigUpdate u = updateProject(project)) {
|
||||
u.getConfig().getProject().setParentName(p1);
|
||||
u.save();
|
||||
}
|
||||
assertThat(stalenessChecker.isStale(project)).isFalse();
|
||||
|
||||
updateProjectConfigWithoutIndexUpdate(p1, c -> c.getProject().setParentName(p2));
|
||||
assertThat(stalenessChecker.isStale(project)).isTrue();
|
||||
}
|
||||
|
||||
private void updateProjectConfigWithoutIndexUpdate(Project.NameKey project) throws Exception {
|
||||
updateProjectConfigWithoutIndexUpdate(
|
||||
project, c -> c.getProject().setDescription("making it stale"));
|
||||
}
|
||||
|
||||
private void updateProjectConfigWithoutIndexUpdate(
|
||||
Project.NameKey project, Consumer<ProjectConfig> update) throws Exception {
|
||||
try (AutoCloseable ignored = disableProjectIndex()) {
|
||||
try (ProjectConfigUpdate u = updateProject(project)) {
|
||||
update.accept(u.getConfig());
|
||||
u.save();
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Drop, as we just wanted to drop the index update
|
||||
return;
|
||||
}
|
||||
fail("should have a UnsupportedOperationException");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user