Disallow change index task duplication
Do not allow change index and reindex tasks to be queued multiple times for a given change. Make sure to remove the index or reindex task from the corresponding queue when it gets executed. Change-Id: Ia68c1e541a3ed36873d3aa3b13548e93393b78ac
This commit is contained in:
committed by
Marco Miller
parent
da6d10f7d7
commit
3ea4b005a2
@@ -18,6 +18,7 @@ import static com.google.gerrit.server.extensions.events.EventUtil.logEventListe
|
|||||||
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.util.concurrent.Atomics;
|
import com.google.common.util.concurrent.Atomics;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
@@ -50,7 +51,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
@@ -111,6 +114,11 @@ public class ChangeIndexer {
|
|||||||
private final StalenessChecker stalenessChecker;
|
private final StalenessChecker stalenessChecker;
|
||||||
private final boolean autoReindexIfStale;
|
private final boolean autoReindexIfStale;
|
||||||
|
|
||||||
|
private final Set<IndexTask> queuedIndexTasks =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
private final Set<ReindexIfStaleTask> queuedReindexIfStaleTasks =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
ChangeIndexer(
|
ChangeIndexer(
|
||||||
@GerritServerConfig Config cfg,
|
@GerritServerConfig Config cfg,
|
||||||
@@ -178,7 +186,11 @@ public class ChangeIndexer {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
|
public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
|
||||||
Project.NameKey project, Change.Id id) {
|
Project.NameKey project, Change.Id id) {
|
||||||
return submit(new IndexTask(project, id));
|
IndexTask task = new IndexTask(project, id);
|
||||||
|
if (queuedIndexTasks.add(task)) {
|
||||||
|
return submit(task);
|
||||||
|
}
|
||||||
|
return Futures.immediateCheckedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -309,7 +321,11 @@ public class ChangeIndexer {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public com.google.common.util.concurrent.CheckedFuture<Boolean, IOException> reindexIfStale(
|
public com.google.common.util.concurrent.CheckedFuture<Boolean, IOException> reindexIfStale(
|
||||||
Project.NameKey project, Change.Id id) {
|
Project.NameKey project, Change.Id id) {
|
||||||
return submit(new ReindexIfStaleTask(project, id), batchExecutor);
|
ReindexIfStaleTask task = new ReindexIfStaleTask(project, id);
|
||||||
|
if (queuedReindexIfStaleTasks.add(task)) {
|
||||||
|
return submit(task, batchExecutor);
|
||||||
|
}
|
||||||
|
return Futures.immediateCheckedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoReindexIfStale(ChangeData cd) {
|
private void autoReindexIfStale(ChangeData cd) {
|
||||||
@@ -351,6 +367,8 @@ public class ChangeIndexer {
|
|||||||
|
|
||||||
protected abstract T callImpl(Provider<ReviewDb> db) throws Exception;
|
protected abstract T callImpl(Provider<ReviewDb> db) throws Exception;
|
||||||
|
|
||||||
|
protected abstract void remove();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
||||||
@@ -405,15 +423,35 @@ public class ChangeIndexer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void callImpl(Provider<ReviewDb> db) throws Exception {
|
public Void callImpl(Provider<ReviewDb> db) throws Exception {
|
||||||
|
remove();
|
||||||
ChangeData cd = newChangeData(db.get(), project, id);
|
ChangeData cd = newChangeData(db.get(), project, id);
|
||||||
index(cd);
|
index(cd);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(IndexTask.class, id.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof IndexTask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IndexTask other = (IndexTask) obj;
|
||||||
|
return id.get() == other.id.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "index-change-" + id;
|
return "index-change-" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void remove() {
|
||||||
|
queuedIndexTasks.remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not AbstractIndexTask as it doesn't need ReviewDb.
|
// Not AbstractIndexTask as it doesn't need ReviewDb.
|
||||||
@@ -445,6 +483,7 @@ public class ChangeIndexer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean callImpl(Provider<ReviewDb> db) throws Exception {
|
public Boolean callImpl(Provider<ReviewDb> db) throws Exception {
|
||||||
|
remove();
|
||||||
try {
|
try {
|
||||||
if (stalenessChecker.isStale(id)) {
|
if (stalenessChecker.isStale(id)) {
|
||||||
index(newChangeData(db.get(), project, id));
|
index(newChangeData(db.get(), project, id));
|
||||||
@@ -464,10 +503,29 @@ public class ChangeIndexer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(ReindexIfStaleTask.class, id.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof ReindexIfStaleTask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ReindexIfStaleTask other = (ReindexIfStaleTask) obj;
|
||||||
|
return id.get() == other.id.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "reindex-if-stale-change-" + id;
|
return "reindex-if-stale-change-" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void remove() {
|
||||||
|
queuedReindexIfStaleTasks.remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCausedByRepositoryNotFoundException(Throwable throwable) {
|
private boolean isCausedByRepositoryNotFoundException(Throwable throwable) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.index.change;
|
|||||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||||
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
|
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
@@ -42,8 +43,11 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -62,6 +66,8 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
private final ListeningExecutorService executor;
|
private final ListeningExecutorService executor;
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
|
|
||||||
|
private final Set<Index> queuedIndexTasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ReindexAfterRefUpdate(
|
ReindexAfterRefUpdate(
|
||||||
@GerritServerConfig Config cfg,
|
@GerritServerConfig Config cfg,
|
||||||
@@ -109,9 +115,12 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Change> changes) {
|
public void onSuccess(List<Change> changes) {
|
||||||
for (Change c : changes) {
|
for (Change c : changes) {
|
||||||
|
Index task = new Index(event, c.getId());
|
||||||
|
if (queuedIndexTasks.add(task)) {
|
||||||
// Don't retry indefinitely; if this fails changes may be stale.
|
// Don't retry indefinitely; if this fails changes may be stale.
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
Future<?> possiblyIgnoredError = executor.submit(new Index(event, c.getId()));
|
Future<?> possiblyIgnoredError = executor.submit(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +150,8 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract V impl(RequestContext ctx) throws Exception;
|
protected abstract V impl(RequestContext ctx) throws Exception;
|
||||||
|
|
||||||
|
protected abstract void remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GetChanges extends Task<List<Change>> {
|
private class GetChanges extends Task<List<Change>> {
|
||||||
@@ -165,6 +176,9 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
+ " update of project "
|
+ " update of project "
|
||||||
+ event.getProjectName();
|
+ event.getProjectName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void remove() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Index extends Task<Void> {
|
private class Index extends Task<Void> {
|
||||||
@@ -179,6 +193,7 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
protected Void impl(RequestContext ctx) throws OrmException, IOException {
|
protected Void impl(RequestContext ctx) throws OrmException, IOException {
|
||||||
// Reload change, as some time may have passed since GetChanges.
|
// Reload change, as some time may have passed since GetChanges.
|
||||||
ReviewDb db = ctx.getReviewDbProvider().get();
|
ReviewDb db = ctx.getReviewDbProvider().get();
|
||||||
|
remove();
|
||||||
try {
|
try {
|
||||||
Change c =
|
Change c =
|
||||||
notesFactory
|
notesFactory
|
||||||
@@ -191,9 +206,28 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(Index.class, id.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Index other = (Index) obj;
|
||||||
|
return id.get() == other.id.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Index change " + id.get() + " of project " + event.getProjectName();
|
return "Index change " + id.get() + " of project " + event.getProjectName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void remove() {
|
||||||
|
queuedIndexTasks.remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user