ChangeFinder: Dedup results when subindexes are used

Some secondary index implementations store changes in nonoverlapping
subindexes. Due to the non-atomic nature of these subindexes, we might
fleetingly observe a change as present twice. This is not an error that
the user can do anything about, so relax ChangeFinder a bit in this
specific case.

Change-Id: Ie434937ad6e05c063bae04fc0affe3eaef6a51e0
This commit is contained in:
Dave Borowitz
2017-04-05 12:45:42 -04:00
parent a86bdfd872
commit 32dc96c2fc
4 changed files with 36 additions and 7 deletions

View File

@@ -68,6 +68,6 @@ public class ElasticIndexModule extends LifecycleModule {
@Provides
@Singleton
IndexConfig getIndexConfig(@GerritServerConfig Config cfg) {
return IndexConfig.fromConfig(cfg);
return IndexConfig.fromConfig(cfg).separateChangeSubIndexes(true).build();
}
}

View File

@@ -84,7 +84,7 @@ public class LuceneIndexModule extends LifecycleModule {
IndexConfig getIndexConfig(@GerritServerConfig Config cfg) {
BooleanQuery.setMaxClauseCount(
cfg.getInt("index", "maxTerms", BooleanQuery.getMaxClauseCount()));
return IndexConfig.fromConfig(cfg);
return IndexConfig.fromConfig(cfg).separateChangeSubIndexes(true).build();
}
private static class MultiVersionModule extends LifecycleModule {

View File

@@ -14,9 +14,11 @@
package com.google.gerrit.server;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.index.IndexConfig;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
@@ -29,13 +31,16 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Singleton
public class ChangeFinder {
private final IndexConfig indexConfig;
private final Provider<InternalChangeQuery> queryProvider;
@Inject
ChangeFinder(Provider<InternalChangeQuery> queryProvider) {
ChangeFinder(IndexConfig indexConfig, Provider<InternalChangeQuery> queryProvider) {
this.indexConfig = indexConfig;
this.queryProvider = queryProvider;
}
@@ -93,8 +98,24 @@ public class ChangeFinder {
private List<ChangeControl> asChangeControls(List<ChangeData> cds, CurrentUser user)
throws OrmException {
List<ChangeControl> ctls = new ArrayList<>(cds.size());
if (!indexConfig.separateChangeSubIndexes()) {
for (ChangeData cd : cds) {
ctls.add(cd.changeControl(user));
}
return ctls;
}
// If an index implementation uses separate non-atomic subindexes, it's possible to temporarily
// observe a change as present in both subindexes, if this search is concurrent with a write.
// Dedup to avoid confusing the caller. We can choose an arbitrary ChangeData instance because
// the index results have no stored fields, so the data is already reloaded. (It's also possible
// that a change might appear in zero subindexes, but there's nothing we can do here to help
// this case.)
Set<Change.Id> seen = Sets.newHashSetWithExpectedSize(cds.size());
for (ChangeData cd : cds) {
ctls.add(cd.changeControl(user));
if (seen.add(cd.getId())) {
ctls.add(cd.changeControl(user));
}
}
return ctls;
}

View File

@@ -34,12 +34,12 @@ public abstract class IndexConfig {
return builder().build();
}
public static IndexConfig fromConfig(Config cfg) {
public static Builder fromConfig(Config cfg) {
Builder b = builder();
setIfPresent(cfg, "maxLimit", b::maxLimit);
setIfPresent(cfg, "maxPages", b::maxPages);
setIfPresent(cfg, "maxTerms", b::maxTerms);
return b.build();
return b;
}
private static void setIfPresent(Config cfg, String name, IntConsumer setter) {
@@ -53,7 +53,8 @@ public abstract class IndexConfig {
return new AutoValue_IndexConfig.Builder()
.maxLimit(Integer.MAX_VALUE)
.maxPages(Integer.MAX_VALUE)
.maxTerms(DEFAULT_MAX_TERMS);
.maxTerms(DEFAULT_MAX_TERMS)
.separateChangeSubIndexes(false);
}
@AutoValue.Builder
@@ -70,6 +71,8 @@ public abstract class IndexConfig {
abstract int maxTerms();
public abstract Builder separateChangeSubIndexes(boolean separate);
abstract IndexConfig autoBuild();
public IndexConfig build() {
@@ -101,4 +104,9 @@ public abstract class IndexConfig {
* for performance reasons.
*/
public abstract int maxTerms();
/**
* @return whether different subsets of changes may be stored in different physical sub-indexes.
*/
public abstract boolean separateChangeSubIndexes();
}