Merge branch 'stable-2.16' into stable-3.0
* stable-2.16:
Add batchThreadPoolSize to high-availability.config
Use dedicated Executor Service for batch indexing
Change-Id: Iac5a645e367684245a0cb406b33d5b6d39efe494
diff --git a/.bazelversion b/.bazelversion
index fd2a018..7c69a55 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.1.0
+3.7.0
diff --git a/BUILD b/BUILD
index 577b49d..c3f5a11 100644
--- a/BUILD
+++ b/BUILD
@@ -41,7 +41,6 @@
visibility = ["//visibility:public"],
exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
":high-availability__plugin",
- "@mockito//jar",
"@wiremock//jar",
],
)
diff --git a/WORKSPACE b/WORKSPACE
index 456e679..666572c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,26 +3,15 @@
load("//:bazlets.bzl", "load_bazlets")
load_bazlets(
- commit = "6c39deb06f58bb62162ccb6865964f531739f512",
+ commit = "a029d8e41d6211c8b23052aa0a0c2c7649577e85",
#local_path = "/home/<user>/projects/bazlets",
)
-# Snapshot Plugin API
-#load(
-# "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-# "gerrit_api_maven_local",
-#)
-
-# Load snapshot Plugin API
-#gerrit_api_maven_local()
-
-# Release Plugin API
load(
"@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
"gerrit_api",
)
-# Load release Plugin API
gerrit_api()
load("//:external_plugin_deps.bzl", "external_plugin_deps")
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 12bf919..0f8b43b 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -3,39 +3,8 @@
def external_plugin_deps():
maven_jar(
name = "wiremock",
- artifact = "com.github.tomakehurst:wiremock-standalone:2.26.3",
- sha1 = "245c6efae2cbcb4e4f3457caf3d1c030cbaf2eb5",
- )
-
- maven_jar(
- name = "mockito",
- artifact = "org.mockito:mockito-core:2.28.2",
- sha1 = "91110215a8cb9b77a46e045ee758f77d79167cc0",
- deps = [
- "@byte-buddy//jar",
- "@byte-buddy-agent//jar",
- "@objenesis//jar",
- ],
- )
-
- BYTE_BUDDY_VERSION = "1.9.10"
-
- maven_jar(
- name = "byte-buddy",
- artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
- sha1 = "211a2b4d3df1eeef2a6cacf78d74a1f725e7a840",
- )
-
- maven_jar(
- name = "byte-buddy-agent",
- artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
- sha1 = "9674aba5ee793e54b864952b001166848da0f26b",
- )
-
- maven_jar(
- name = "objenesis",
- artifact = "org.objenesis:objenesis:2.6",
- sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
+ artifact = "com.github.tomakehurst:wiremock-standalone:2.27.2",
+ sha1 = "327647a19b2319af2526b9c33a5733a2241723e0",
)
maven_jar(
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
index 99cc106..093628e 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
@@ -45,6 +45,9 @@
public class Configuration {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
+ public static final int DEFAULT_NUM_STRIPED_LOCKS = 10;
+ public static final int DEFAULT_TIMEOUT_MS = 5000;
+
// common parameter to peerInfo section
static final String PEER_INFO_SECTION = "peerInfo";
@@ -55,7 +58,6 @@
static final int DEFAULT_INDEX_RETRY_INTERVAL = 30000;
static final int DEFAULT_THREAD_POOL_SIZE = 4;
static final String NUM_STRIPED_LOCKS = "numStripedLocks";
- static final int DEFAULT_NUM_STRIPED_LOCKS = 10;
private final Main main;
private final AutoReindex autoReindex;
@@ -334,6 +336,9 @@
}
public static class Http {
+ public static final int DEFAULT_MAX_TRIES = 360;
+ public static final int DEFAULT_RETRY_INTERVAL = 10000;
+
static final String HTTP_SECTION = "http";
static final String USER_KEY = "user";
static final String PASSWORD_KEY = "password";
@@ -342,10 +347,6 @@
static final String MAX_TRIES_KEY = "maxTries";
static final String RETRY_INTERVAL_KEY = "retryInterval";
- static final int DEFAULT_TIMEOUT_MS = 5000;
- static final int DEFAULT_MAX_TRIES = 360;
- static final int DEFAULT_RETRY_INTERVAL = 10000;
-
private final String user;
private final String password;
private final int connectionTimeout;
@@ -447,6 +448,7 @@
public static class Index extends Forwarding {
static final String INDEX_SECTION = "index";
static final String MAX_TRIES_KEY = "maxTries";
+ static final String WAIT_TIMEOUT_KEY = "waitTimeout";
static final String RETRY_INTERVAL_KEY = "retryInterval";
static final String SYNCHRONIZE_FORCED_KEY = "synchronizeForced";
static final boolean DEFAULT_SYNCHRONIZE_FORCED = true;
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
index 6635b80..8a267d6 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
@@ -24,6 +24,7 @@
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Cache.CACHE_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Cache.PATTERN_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_THREAD_POOL_SIZE;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_TIMEOUT_MS;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Event.EVENT_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Forwarding.DEFAULT_SYNCHRONIZE;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Forwarding.SYNCHRONIZE_KEY;
@@ -33,7 +34,6 @@
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.CONNECTION_TIMEOUT_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_MAX_TRIES;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_RETRY_INTERVAL;
-import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_TIMEOUT_MS;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.HTTP_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.MAX_TRIES_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.PASSWORD_KEY;
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
index de156a2..eb65106 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
@@ -19,11 +19,9 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.AbstractIndexRestApiServlet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.sql.Timestamp;
@@ -48,12 +46,12 @@
}
@Override
- protected Iterable<AccountState> fetchItems(ReviewDb db) throws Exception {
+ protected Iterable<AccountState> fetchItems() throws Exception {
return accounts.all();
}
@Override
- protected Optional<Timestamp> indexIfNeeded(ReviewDb db, AccountState as, Timestamp sinceTs) {
+ protected Optional<Timestamp> indexIfNeeded(AccountState as, Timestamp sinceTs) {
try {
Account a = as.getAccount();
Timestamp accountTs = a.getRegisteredOn();
@@ -63,7 +61,7 @@
accountIdx.index(a.getId(), Operation.INDEX, Optional.empty());
return Optional.of(accountTs);
}
- } catch (IOException | OrmException e) {
+ } catch (IOException e) {
log.atSevere().withCause(e).log("Reindex failed");
}
return Optional.empty();
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
index 67c8325..dd323b4 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
@@ -21,13 +21,11 @@
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.sql.Timestamp;
@@ -77,14 +75,14 @@
}
@Override
- protected Iterable<Change> fetchItems(ReviewDb db) throws Exception {
+ protected Iterable<Change> fetchItems() throws Exception {
Stream<Change> allChangesStream = Stream.empty();
Iterable<Project.NameKey> projects = projectCache.all();
for (Project.NameKey projectName : projects) {
try (Repository repo = repoManager.openRepository(projectName)) {
Stream<Change> projectChangesStream =
notesFactory
- .scan(repo, db, projectName)
+ .scan(repo, projectName)
.map((ChangeNotesResult changeNotes) -> changeNotes.notes().getChange());
allChangesStream = Streams.concat(allChangesStream, projectChangesStream);
}
@@ -93,7 +91,7 @@
}
@Override
- protected Optional<Timestamp> indexIfNeeded(ReviewDb db, Change c, Timestamp sinceTs) {
+ protected Optional<Timestamp> indexIfNeeded(Change c, Timestamp sinceTs) {
try {
Timestamp changeTs = c.getLastUpdatedOn();
if (changeTs.after(sinceTs)) {
@@ -102,7 +100,7 @@
changeIdx.index(c.getProject() + "~" + c.getId(), Operation.INDEX, Optional.empty());
return Optional.of(changeTs);
}
- } catch (OrmException | IOException e) {
+ } catch (IOException e) {
log.atSevere().withCause(e).log("Reindex failed");
}
return Optional.empty();
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
index 71a0280..e37234c 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
@@ -16,7 +16,6 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.AbstractIndexRestApiServlet;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
@@ -34,12 +33,12 @@
}
@Override
- protected Iterable<GroupReference> fetchItems(ReviewDb db) throws Exception {
+ protected Iterable<GroupReference> fetchItems() throws Exception {
return groups.getAllGroupReferences()::iterator;
}
@Override
- protected Optional<Timestamp> indexIfNeeded(ReviewDb db, GroupReference g, Timestamp sinceTs) {
+ protected Optional<Timestamp> indexIfNeeded(GroupReference g, Timestamp sinceTs) {
return Optional.empty();
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
index 8abdb59..561bb19 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
@@ -23,11 +23,9 @@
import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.gerrit.extensions.events.ProjectIndexedListener;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -54,7 +52,6 @@
private final FlusherRunner accountFlusher;
private final FlusherRunner groupFlusher;
private final FlusherRunner projectFlusher;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ChangeFinder changeFinder;
private final CurrentRequestContext currCtx;
@@ -104,7 +101,6 @@
public IndexTs(
@PluginData Path dataDir,
WorkQueue queue,
- SchemaFactory<ReviewDb> schemaFactory,
ChangeFinder changeFinder,
CurrentRequestContext currCtx) {
this.dataDir = dataDir;
@@ -113,7 +109,6 @@
this.accountFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.ACCOUNT);
this.groupFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.GROUP);
this.projectFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.PROJECT);
- this.schemaFactory = schemaFactory;
this.changeFinder = changeFinder;
this.currCtx = currCtx;
}
@@ -137,7 +132,7 @@
public void onChangeIndexed(String projectName, int id) {
currCtx.onlyWithContext(
(ctx) -> {
- try (ReviewDb db = schemaFactory.open()) {
+ try {
ChangeNotes changeNotes = changeFinder.findOne(projectName + "~" + id);
update(
IndexName.CHANGE,
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ProjectReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ProjectReindexRunnable.java
index 582227d..ff5a965 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ProjectReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ProjectReindexRunnable.java
@@ -16,7 +16,6 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.AbstractIndexRestApiServlet;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
@@ -35,12 +34,12 @@
}
@Override
- protected Iterable<Project.NameKey> fetchItems(ReviewDb db) {
+ protected Iterable<Project.NameKey> fetchItems() {
return projectCache.all();
}
@Override
- protected Optional<Timestamp> indexIfNeeded(ReviewDb db, Project.NameKey g, Timestamp sinceTs) {
+ protected Optional<Timestamp> indexIfNeeded(Project.NameKey g, Timestamp sinceTs) {
return Optional.empty();
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
index 7a5669e..3cb0ed0 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
@@ -17,7 +17,6 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.AbstractIndexRestApiServlet;
import com.google.common.base.Stopwatch;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
@@ -27,6 +26,7 @@
import java.util.concurrent.TimeUnit;
abstract class ReindexRunnable<T> implements Runnable {
+
private static final FluentLogger log = FluentLogger.forEnclosingClass();
private final AbstractIndexRestApiServlet.IndexName itemName;
@@ -49,14 +49,13 @@
if (maybeIndexTs.isPresent()) {
newLastIndexTs = maxTimestamp(newLastIndexTs, Timestamp.valueOf(maybeIndexTs.get()));
log.atFine().log("Scanning for all the %ss after %s", itemNameString, newLastIndexTs);
- try (ManualRequestContext mctx = ctx.open();
- ReviewDb db = mctx.getReviewDbProvider().get()) {
+ try (ManualRequestContext mctx = ctx.open()) {
int count = 0;
int errors = 0;
Stopwatch stopwatch = Stopwatch.createStarted();
- for (T c : fetchItems(db)) {
+ for (T c : fetchItems()) {
try {
- Optional<Timestamp> itemTs = indexIfNeeded(db, c, newLastIndexTs);
+ Optional<Timestamp> itemTs = indexIfNeeded(c, newLastIndexTs);
if (itemTs.isPresent()) {
count++;
newLastIndexTs = maxTimestamp(newLastIndexTs, itemTs.get());
@@ -102,7 +101,7 @@
return ts2;
}
- protected abstract Iterable<T> fetchItems(ReviewDb db) throws Exception;
+ protected abstract Iterable<T> fetchItems() throws Exception;
- protected abstract Optional<Timestamp> indexIfNeeded(ReviewDb db, T item, Timestamp sinceTs);
+ protected abstract Optional<Timestamp> indexIfNeeded(T item, Timestamp sinceTs);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcher.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcher.java
index 27cb5c2..0f43eaf 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcher.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcher.java
@@ -26,14 +26,18 @@
@Singleton
class CachePatternMatcher {
private static final List<String> DEFAULT_PATTERNS =
- ImmutableList.of(
- "accounts", "^groups.*", "ldap_usernames", "projects", "sshkeys", "web_sessions");
+ ImmutableList.of("accounts", "^groups.*", "ldap_usernames", "projects", "sshkeys");
private final Pattern pattern;
@Inject
CachePatternMatcher(Configuration cfg) {
List<String> patterns = new ArrayList<>(DEFAULT_PATTERNS);
+
+ if (cfg.websession().synchronize()) {
+ patterns.add("web_sessions");
+ }
+
patterns.addAll(cfg.cache().patterns());
this.pattern = Pattern.compile(Joiner.on("|").join(patterns));
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ProjectNameKeyAdapter.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ProjectNameKeyAdapter.java
new file mode 100644
index 0000000..8c00282
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ProjectNameKeyAdapter.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 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.ericsson.gerrit.plugins.highavailability.event;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+
+public class ProjectNameKeyAdapter
+ implements JsonSerializer<Project.NameKey>, JsonDeserializer<Project.NameKey> {
+ @Override
+ public JsonElement serialize(
+ Project.NameKey project, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(project.get());
+ }
+
+ @Override
+ public Project.NameKey deserialize(
+ JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ if (json.isJsonObject()) {
+ return Project.nameKey(json.getAsJsonObject().get("name").getAsJsonPrimitive().getAsString());
+ }
+ return Project.nameKey(json.getAsString());
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
index 3d9fd60..f156da4 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
@@ -14,7 +14,6 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventBroker;
import com.google.gerrit.server.events.EventListener;
@@ -24,7 +23,6 @@
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
-import com.google.inject.Provider;
class ForwardedAwareEventBroker extends EventBroker {
@@ -34,15 +32,8 @@
PluginSetContext<EventListener> unrestrictedListeners,
PermissionBackend permissionBackend,
ProjectCache projectCache,
- Factory notesFactory,
- Provider<ReviewDb> dbProvider) {
- super(
- listeners,
- unrestrictedListeners,
- permissionBackend,
- projectCache,
- notesFactory,
- dbProvider);
+ Factory notesFactory) {
+ super(listeners, unrestrictedListeners, permissionBackend, projectCache, notesFactory);
}
@Override
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandler.java
index 51db006..414e795 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandler.java
@@ -18,7 +18,6 @@
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventDispatcher;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -42,9 +41,8 @@
* Dispatch an event in the local node, event will not be forwarded to the other node.
*
* @param event The event to dispatch
- * @throws OrmException If an error occur while retrieving the change the event belongs to.
*/
- public void dispatch(Event event) throws OrmException, PermissionBackendException {
+ public void dispatch(Event event) throws PermissionBackendException {
try {
Context.setForwardedEvent(true);
log.atFine().log("dispatching event %s", event.getType());
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandler.java
index ba60830..f31331d 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandler.java
@@ -14,12 +14,10 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.Optional;
/**
@@ -33,13 +31,12 @@
private final AccountIndexer indexer;
@Inject
- ForwardedIndexAccountHandler(AccountIndexer indexer, Configuration config) {
- super(config.index());
+ ForwardedIndexAccountHandler(AccountIndexer indexer) {
this.indexer = indexer;
}
@Override
- protected void doIndex(Account.Id id, Optional<IndexEvent> indexEvent) throws IOException {
+ protected void doIndex(Account.Id id, Optional<IndexEvent> indexEvent) {
indexer.index(id);
log.atFine().log("Account %s successfully indexed", id);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexBatchChangeHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexBatchChangeHandler.java
index a6cbb7f..dee8876 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexBatchChangeHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexBatchChangeHandler.java
@@ -16,7 +16,6 @@
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.index.ChangeCheckerImpl.Factory;
-import com.ericsson.gerrit.plugins.highavailability.index.ChangeDb;
import com.ericsson.gerrit.plugins.highavailability.index.ForwardedBatchIndexExecutor;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.util.OneOffRequestContext;
@@ -30,11 +29,10 @@
@Inject
ForwardedIndexBatchChangeHandler(
ChangeIndexer indexer,
- ChangeDb changeDb,
Configuration config,
@ForwardedBatchIndexExecutor ScheduledExecutorService indexExecutor,
OneOffRequestContext oneOffCtx,
Factory changeCheckerFactory) {
- super(indexer, changeDb, config, indexExecutor, oneOffCtx, changeCheckerFactory);
+ super(indexer, config, indexExecutor, oneOffCtx, changeCheckerFactory);
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
index d7c7e88..add93d7 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
@@ -18,17 +18,14 @@
import com.ericsson.gerrit.plugins.highavailability.Configuration.Index;
import com.ericsson.gerrit.plugins.highavailability.index.ChangeChecker;
import com.ericsson.gerrit.plugins.highavailability.index.ChangeCheckerImpl;
-import com.ericsson.gerrit.plugins.highavailability.index.ChangeDb;
import com.ericsson.gerrit.plugins.highavailability.index.ForwardedIndexExecutor;
import com.google.common.base.Splitter;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -45,7 +42,6 @@
@Singleton
public class ForwardedIndexChangeHandler extends ForwardedIndexingHandler<String> {
private final ChangeIndexer indexer;
- private final ChangeDb changeDb;
private final ScheduledExecutorService indexExecutor;
private final OneOffRequestContext oneOffCtx;
private final int retryInterval;
@@ -55,14 +51,11 @@
@Inject
ForwardedIndexChangeHandler(
ChangeIndexer indexer,
- ChangeDb changeDb,
Configuration config,
@ForwardedIndexExecutor ScheduledExecutorService indexExecutor,
OneOffRequestContext oneOffCtx,
ChangeCheckerImpl.Factory changeCheckerFactory) {
- super(config.index());
this.indexer = indexer;
- this.changeDb = changeDb;
this.indexExecutor = indexExecutor;
this.oneOffCtx = oneOffCtx;
this.changeCheckerFactory = changeCheckerFactory;
@@ -73,13 +66,12 @@
}
@Override
- protected void doIndex(String id, Optional<IndexEvent> indexEvent)
- throws IOException, OrmException {
+ protected void doIndex(String id, Optional<IndexEvent> indexEvent) throws IOException {
doIndex(id, indexEvent, 0);
}
private void doIndex(String id, Optional<IndexEvent> indexEvent, int retryCount)
- throws IOException, OrmException {
+ throws IOException {
try {
ChangeChecker checker = changeCheckerFactory.create(id);
Optional<ChangeNotes> changeNotes = checker.getChangeNotes();
@@ -121,11 +113,9 @@
}
}
- private void reindex(ChangeNotes notes) throws IOException, OrmException {
- try (ReviewDb db = changeDb.open()) {
- notes.reload();
- indexer.index(db, notes.getChange());
- }
+ private void reindex(ChangeNotes notes) {
+ notes.reload();
+ indexer.index(notes.getChange());
}
private boolean rescheduleIndex(String id, Optional<IndexEvent> indexEvent, int retryCount) {
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandler.java
index fb4d2f2..127af36 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandler.java
@@ -14,12 +14,10 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.Optional;
/**
@@ -33,14 +31,12 @@
private final GroupIndexer indexer;
@Inject
- ForwardedIndexGroupHandler(GroupIndexer indexer, Configuration config) {
- super(config.index());
+ ForwardedIndexGroupHandler(GroupIndexer indexer) {
this.indexer = indexer;
}
@Override
- protected void doIndex(AccountGroup.UUID uuid, Optional<IndexEvent> indexEvent)
- throws IOException {
+ protected void doIndex(AccountGroup.UUID uuid, Optional<IndexEvent> indexEvent) {
indexer.index(uuid);
log.atFine().log("Group %s successfully indexed", uuid);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandler.java
index 5ae4cc0..b6daf41 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandler.java
@@ -14,12 +14,10 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.Optional;
/**
@@ -33,14 +31,12 @@
private final ProjectIndexer indexer;
@Inject
- ForwardedIndexProjectHandler(ProjectIndexer indexer, Configuration config) {
- super(config.index());
+ ForwardedIndexProjectHandler(ProjectIndexer indexer) {
this.indexer = indexer;
}
@Override
- protected void doIndex(Project.NameKey projectName, Optional<IndexEvent> indexEvent)
- throws IOException {
+ protected void doIndex(Project.NameKey projectName, Optional<IndexEvent> indexEvent) {
indexer.index(projectName);
log.atFine().log("Project %s successfully indexed", projectName);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexingHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexingHandler.java
index 78e103d..44e35a8 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexingHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexingHandler.java
@@ -14,13 +14,12 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.common.flogger.FluentLogger;
-import com.google.common.util.concurrent.Striped;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
+import java.util.Collections;
import java.util.Optional;
-import java.util.concurrent.locks.Lock;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Base class to handle forwarded indexing. This class is meant to be extended by classes used on
@@ -30,6 +29,7 @@
*/
public abstract class ForwardedIndexingHandler<T> {
protected static final FluentLogger log = FluentLogger.forEnclosingClass();
+ private final Set<T> inFlightIndexing = Collections.newSetFromMap(new ConcurrentHashMap<>());
public enum Operation {
INDEX,
@@ -41,17 +41,10 @@
}
}
- private final Striped<Lock> idLocks;
-
- protected abstract void doIndex(T id, Optional<IndexEvent> indexEvent)
- throws IOException, OrmException;
+ protected abstract void doIndex(T id, Optional<IndexEvent> indexEvent) throws IOException;
protected abstract void doDelete(T id, Optional<IndexEvent> indexEvent) throws IOException;
- protected ForwardedIndexingHandler(Configuration.Index indexConfig) {
- idLocks = Striped.lock(indexConfig.numStripedLocks());
- }
-
/**
* Index an item in the local node, indexing will not be forwarded to the other node.
*
@@ -59,16 +52,12 @@
* @param operation The operation to do; index or delete
* @param indexEvent The index event details.
* @throws IOException If an error occur while indexing.
- * @throws OrmException If an error occur while retrieving a change related to the item to index
*/
- public void index(T id, Operation operation, Optional<IndexEvent> indexEvent)
- throws IOException, OrmException {
+ public void index(T id, Operation operation, Optional<IndexEvent> indexEvent) throws IOException {
log.atFine().log("%s %s %s", operation, id, indexEvent);
- try {
- Context.setForwardedEvent(true);
- Lock idLock = idLocks.get(id);
- idLock.lock();
+ if (inFlightIndexing.add(id)) {
try {
+ Context.setForwardedEvent(true);
switch (operation) {
case INDEX:
doIndex(id, indexEvent);
@@ -81,10 +70,12 @@
break;
}
} finally {
- idLock.unlock();
+ Context.unsetForwardedEvent();
+ inFlightIndexing.remove(id);
}
- } finally {
- Context.unsetForwardedEvent();
+ } else {
+ throw new InFlightIndexedException(
+ String.format("Indexing for %s %s %s already in flight", operation, id, indexEvent));
}
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
index 851d7e8..b73b676 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
@@ -15,6 +15,7 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
import com.google.gerrit.server.events.Event;
+import java.util.concurrent.CompletableFuture;
/** Forward indexing, stream events and cache evictions to the other master */
public interface Forwarder {
@@ -24,9 +25,10 @@
*
* @param accountId the account to index.
* @param indexEvent the details of the index event.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean indexAccount(int accountId, IndexEvent indexEvent);
+ CompletableFuture<Boolean> indexAccount(int accountId, IndexEvent indexEvent);
/**
* Forward a change indexing event to the other master.
@@ -34,9 +36,10 @@
* @param projectName the project of the change to index.
* @param changeId the change to index.
* @param indexEvent the details of the index event.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean indexChange(String projectName, int changeId, IndexEvent indexEvent);
+ CompletableFuture<Boolean> indexChange(String projectName, int changeId, IndexEvent indexEvent);
/**
* Forward a change indexing event to the other master using batch index endpoint.
@@ -44,67 +47,76 @@
* @param projectName the project of the change to index.
* @param changeId the change to index.
* @param indexEvent the details of the index event.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean batchIndexChange(String projectName, int changeId, IndexEvent indexEvent);
+ CompletableFuture<Boolean> batchIndexChange(
+ String projectName, int changeId, IndexEvent indexEvent);
/**
* Forward a delete change from index event to the other master.
*
* @param changeId the change to remove from the index.
* @param indexEvent the details of the index event.
- * @return rue if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean deleteChangeFromIndex(int changeId, IndexEvent indexEvent);
+ CompletableFuture<Boolean> deleteChangeFromIndex(int changeId, IndexEvent indexEvent);
/**
* Forward a group indexing event to the other master.
*
* @param uuid the group to index.
* @param indexEvent the details of the index event.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean indexGroup(String uuid, IndexEvent indexEvent);
+ CompletableFuture<Boolean> indexGroup(String uuid, IndexEvent indexEvent);
/**
* Forward a project indexing event to the other master.
*
* @param projectName the project to index.
* @param indexEvent the details of the index event.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean indexProject(String projectName, IndexEvent indexEvent);
+ CompletableFuture<Boolean> indexProject(String projectName, IndexEvent indexEvent);
/**
* Forward a stream event to the other master.
*
* @param event the event to forward.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean send(Event event);
+ CompletableFuture<Boolean> send(Event event);
/**
* Forward a cache eviction event to the other master.
*
* @param cacheName the name of the cache to evict an entry from.
* @param key the key identifying the entry to evict from the cache.
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean evict(String cacheName, Object key);
+ CompletableFuture<Boolean> evict(String cacheName, Object key);
/**
* Forward an addition to the project list cache to the other master.
*
* @param projectName the name of the project to add to the project list cache
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean addToProjectList(String projectName);
+ CompletableFuture<Boolean> addToProjectList(String projectName);
/**
* Forward a removal from the project list cache to the other master.
*
* @param projectName the name of the project to remove from the project list cache
- * @return true if successful, otherwise false.
+ * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+ * false.
*/
- boolean removeFromProjectList(String projectName);
+ CompletableFuture<Boolean> removeFromProjectList(String projectName);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/InFlightIndexedException.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/InFlightIndexedException.java
new file mode 100644
index 0000000..87d28b3
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/InFlightIndexedException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 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.ericsson.gerrit.plugins.highavailability.forwarder;
+
+import java.io.IOException;
+
+public class InFlightIndexedException extends IOException {
+
+ private static final long serialVersionUID = 1L;
+
+ public InFlightIndexedException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java
index 410aa23..5603fc2 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java
@@ -16,7 +16,6 @@
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
-import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler;
@@ -25,7 +24,6 @@
import com.google.common.base.Charsets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Optional;
@@ -93,10 +91,6 @@
} catch (IOException e) {
sendError(rsp, SC_CONFLICT, e.getMessage());
log.atSevere().withCause(e).log("Unable to update %s index", indexName);
- } catch (OrmException e) {
- String msg = String.format("Error trying to find %s", indexName);
- sendError(rsp, SC_NOT_FOUND, msg);
- log.atFine().withCause(e).log(msg);
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
index 61b714c..3d2d25e 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
@@ -16,7 +16,6 @@
import static com.google.common.net.MediaType.JSON_UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
@@ -25,7 +24,6 @@
import com.google.common.net.MediaType;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -55,9 +53,6 @@
}
forwardedEventHandler.dispatch(getEventFromRequest(req));
rsp.setStatus(SC_NO_CONTENT);
- } catch (OrmException e) {
- log.atFine().withCause(e).log("Error trying to find a change");
- sendError(rsp, SC_NOT_FOUND, "Change not found\n");
} catch (IOException | PermissionBackendException e) {
log.atSevere().withCause(e).log("Unable to re-trigger event");
sendError(rsp, SC_BAD_REQUEST, e.getMessage());
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java
index e62cec2..c600019 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java
@@ -14,10 +14,13 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
+import com.ericsson.gerrit.plugins.highavailability.event.ProjectNameKeyAdapter;
import com.google.common.base.Supplier;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventDeserializer;
import com.google.gerrit.server.events.SupplierDeserializer;
+import com.google.gerrit.server.events.SupplierSerializer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.Provider;
@@ -28,6 +31,8 @@
public Gson get() {
return new GsonBuilder()
.registerTypeAdapter(Event.class, new EventDeserializer())
+ .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeyAdapter())
+ .registerTypeAdapter(Supplier.class, new SupplierSerializer())
.registerTypeAdapter(Supplier.class, new SupplierDeserializer())
.create();
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
index f2ac080..cf98aae 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
@@ -15,11 +15,9 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.HttpResponseHandler.HttpResult;
-import com.google.common.base.Supplier;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.MediaType;
-import com.google.gerrit.server.events.SupplierSerializer;
import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import java.io.IOException;
import java.net.URI;
@@ -32,12 +30,12 @@
class HttpSession {
private final CloseableHttpClient httpClient;
- private final Gson gson =
- new GsonBuilder().registerTypeAdapter(Supplier.class, new SupplierSerializer()).create();
+ private final Gson gson;
@Inject
- HttpSession(CloseableHttpClient httpClient) {
+ HttpSession(CloseableHttpClient httpClient, GsonProvider gsonProvider) {
this.httpClient = httpClient;
+ this.gson = gsonProvider.get();
}
HttpResult post(String uri) throws IOException {
@@ -67,7 +65,8 @@
}
}
- private String jsonEncode(Object content) {
+ @VisibleForTesting
+ String jsonEncode(Object content) {
if (content instanceof String) {
return (String) content;
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
index 52065ed..6426b5a 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
@@ -28,10 +28,8 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
import javax.net.ssl.SSLException;
import org.apache.http.HttpException;
import org.apache.http.client.ClientProtocolException;
@@ -49,6 +47,7 @@
private final Configuration cfg;
private final Provider<Set<PeerInfo>> peerInfoProvider;
private final GsonProvider gson;
+ private final RestForwarderScheduler scheduler;
@Inject
RestForwarder(
@@ -56,21 +55,24 @@
@PluginName String pluginName,
Configuration cfg,
Provider<Set<PeerInfo>> peerInfoProvider,
- GsonProvider gson) {
+ GsonProvider gson,
+ RestForwarderScheduler scheduler) {
this.httpSession = httpClient;
this.pluginRelativePath = Joiner.on("/").join("plugins", pluginName);
this.cfg = cfg;
this.peerInfoProvider = peerInfoProvider;
this.gson = gson;
+ this.scheduler = scheduler;
}
@Override
- public boolean indexAccount(final int accountId, IndexEvent event) {
+ public CompletableFuture<Boolean> indexAccount(final int accountId, IndexEvent event) {
return execute(RequestMethod.POST, "index account", "index/account", accountId, event);
}
@Override
- public boolean indexChange(String projectName, int changeId, IndexEvent event) {
+ public CompletableFuture<Boolean> indexChange(
+ String projectName, int changeId, IndexEvent event) {
return execute(
RequestMethod.POST,
"index change",
@@ -79,8 +81,8 @@
event);
}
- @Override
- public boolean batchIndexChange(String projectName, int changeId, IndexEvent event) {
+ public CompletableFuture<Boolean> batchIndexChange(
+ String projectName, int changeId, IndexEvent event) {
return execute(
RequestMethod.POST,
"index change",
@@ -90,13 +92,13 @@
}
@Override
- public boolean deleteChangeFromIndex(final int changeId, IndexEvent event) {
+ public CompletableFuture<Boolean> deleteChangeFromIndex(final int changeId, IndexEvent event) {
return execute(
RequestMethod.DELETE, "delete change", "index/change", buildIndexEndpoint(changeId), event);
}
@Override
- public boolean indexGroup(final String uuid, IndexEvent event) {
+ public CompletableFuture<Boolean> indexGroup(final String uuid, IndexEvent event) {
return execute(RequestMethod.POST, "index group", "index/group", uuid, event);
}
@@ -110,24 +112,24 @@
}
@Override
- public boolean indexProject(String projectName, IndexEvent event) {
+ public CompletableFuture<Boolean> indexProject(String projectName, IndexEvent event) {
return execute(
RequestMethod.POST, "index project", "index/project", Url.encode(projectName), event);
}
@Override
- public boolean send(final Event event) {
+ public CompletableFuture<Boolean> send(final Event event) {
return execute(RequestMethod.POST, "send event", "event", event.type, event);
}
@Override
- public boolean evict(final String cacheName, final Object key) {
+ public CompletableFuture<Boolean> evict(final String cacheName, final Object key) {
String json = gson.get().toJson(key);
return execute(RequestMethod.POST, "invalidate cache " + cacheName, "cache", cacheName, json);
}
@Override
- public boolean addToProjectList(String projectName) {
+ public CompletableFuture<Boolean> addToProjectList(String projectName) {
return execute(
RequestMethod.POST,
"Update project_list, add ",
@@ -136,7 +138,7 @@
}
@Override
- public boolean removeFromProjectList(String projectName) {
+ public CompletableFuture<Boolean> removeFromProjectList(String projectName) {
return execute(
RequestMethod.DELETE,
"Update project_list, remove ",
@@ -148,19 +150,19 @@
return Joiner.on("/").join("cache", Constants.PROJECT_LIST);
}
- private boolean execute(RequestMethod method, String action, String endpoint, Object id) {
+ private CompletableFuture<Boolean> execute(
+ RequestMethod method, String action, String endpoint, Object id) {
return execute(method, action, endpoint, id, null);
}
- private boolean execute(
+ private CompletableFuture<Boolean> execute(
RequestMethod method, String action, String endpoint, Object id, Object payload) {
- List<CompletableFuture<Boolean>> futures =
- peerInfoProvider.get().stream()
- .map(peer -> createRequest(method, peer, action, endpoint, id, payload))
- .map(request -> CompletableFuture.supplyAsync(request::execute))
- .collect(Collectors.toList());
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
- return futures.stream().allMatch(CompletableFuture::join);
+ return peerInfoProvider.get().stream()
+ .map(peer -> createRequest(method, peer, action, endpoint, id, payload))
+ .map(scheduler::execute)
+ .reduce(
+ CompletableFuture.completedFuture(true),
+ (a, b) -> a.thenCombine(b, (left, right) -> left && right));
}
private Request createRequest(
@@ -186,7 +188,7 @@
};
}
- private abstract class Request {
+ protected abstract class Request {
private final String action;
private final Object key;
private final String destination;
@@ -199,42 +201,36 @@
this.destination = destination;
}
- boolean execute() {
- log.atFine().log("Executing %s %s towards %s", action, key, destination);
- for (; ; ) {
- try {
- execCnt++;
- tryOnce();
- log.atFine().log("%s %s towards %s OK", action, key, destination);
- return true;
- } catch (ForwardingException e) {
- int maxTries = cfg.http().maxTries();
- log.atFine().withCause(e).log(
- "Failed to %s %s on %s [%d/%d]", action, key, destination, execCnt, maxTries);
- if (!e.isRecoverable()) {
- log.atSevere().withCause(e).log(
- "%s %s towards %s failed with unrecoverable error; giving up",
- action, key, destination);
- return false;
- }
- if (execCnt >= maxTries) {
- log.atSevere().log(
- "Failed to %s %s on %s after %d tries; giving up",
- action, key, destination, maxTries);
- return false;
- }
+ @Override
+ public String toString() {
+ return String.format("%s:%s => %s (try #%d)", action, key, destination, execCnt);
+ }
- log.atFine().log("Retrying to %s %s on %s", action, key, destination);
- try {
- Thread.sleep(cfg.http().retryInterval());
- } catch (InterruptedException ie) {
- log.atSevere().withCause(ie).log(
- "%s %s towards %s was interrupted; giving up", action, key, destination);
- Thread.currentThread().interrupt();
- return false;
- }
+ boolean execute() throws ForwardingException {
+ log.atFine().log("Executing %s %s towards %s", action, key, destination);
+ try {
+ execCnt++;
+ tryOnce();
+ log.atFine().log("%s %s towards %s OK", action, key, destination);
+ return true;
+ } catch (ForwardingException e) {
+ int maxTries = cfg.http().maxTries();
+ log.atFine().withCause(e).log(
+ "Failed to %s %s on %s [%d/%d]", action, key, destination, execCnt, maxTries);
+ if (!e.isRecoverable()) {
+ log.atSevere().withCause(e).log(
+ "%s %s towards %s failed with unrecoverable error; giving up",
+ action, key, destination);
+ throw e;
+ }
+ if (execCnt >= maxTries) {
+ log.atSevere().log(
+ "Failed to %s %s on %s after %d tries; giving up",
+ action, key, destination, maxTries);
+ throw e;
}
}
+ return false;
}
void tryOnce() throws ForwardingException {
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderScheduler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderScheduler.java
new file mode 100644
index 0000000..ebbd940
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderScheduler.java
@@ -0,0 +1,146 @@
+// Copyright (C) 2020 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.ericsson.gerrit.plugins.highavailability.forwarder.rest;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.server.git.WorkQueue;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+@Singleton
+public class RestForwarderScheduler {
+ private static final FluentLogger log = FluentLogger.forEnclosingClass();
+ private final ScheduledExecutorService executor;
+ private final long retryIntervalMs;
+
+ public class CompletablePromise<V> extends CompletableFuture<V> {
+ private Future<V> future;
+
+ public CompletablePromise(Future<V> future) {
+ this.future = future;
+ executor.execute(this::tryToComplete);
+ }
+
+ private void tryToComplete() {
+ if (future.isDone()) {
+ try {
+ complete(future.get());
+ } catch (InterruptedException e) {
+ completeExceptionally(e);
+ } catch (ExecutionException e) {
+ completeExceptionally(e.getCause());
+ }
+ return;
+ }
+
+ if (future.isCancelled()) {
+ cancel(true);
+ return;
+ }
+
+ executor.execute(this::tryToComplete);
+ }
+ }
+
+ @Inject
+ public RestForwarderScheduler(
+ WorkQueue workQueue, Configuration cfg, Provider<Set<PeerInfo>> peerInfoProvider) {
+ int executorSize = peerInfoProvider.get().size() * cfg.index().threadPoolSize();
+ retryIntervalMs = cfg.index().retryInterval();
+ this.executor = workQueue.createQueue(executorSize, "RestForwarderScheduler");
+ }
+
+ @VisibleForTesting
+ public RestForwarderScheduler(ScheduledExecutorService executor) {
+ this.executor = executor;
+ retryIntervalMs = 0;
+ }
+
+ public CompletableFuture<Boolean> execute(RestForwarder.Request request) {
+ return execute(request, 0);
+ }
+
+ public CompletableFuture<Boolean> execute(RestForwarder.Request request, long delayMs) {
+ return supplyAsync(
+ request.toString(),
+ () -> {
+ try {
+ if (!request.execute()) {
+ log.atWarning().log(
+ "Rescheduling %s for retry after %d msec", request, retryIntervalMs);
+ return execute(request, retryIntervalMs);
+ }
+ return CompletableFuture.completedFuture(true);
+ } catch (ForwardingException e) {
+ log.atSevere().withCause(e).log("Forwarding of %s has failed", request);
+ return CompletableFuture.completedFuture(false);
+ }
+ },
+ executor,
+ delayMs);
+ }
+
+ private CompletableFuture<Boolean> supplyAsync(
+ String taskName,
+ Supplier<CompletableFuture<Boolean>> fn,
+ ScheduledExecutorService executor,
+ long delayMs) {
+ BooleanAsyncSupplier asyncSupplier = new BooleanAsyncSupplier(taskName, fn);
+ executor.schedule(asyncSupplier, delayMs, TimeUnit.MILLISECONDS);
+ return asyncSupplier.future();
+ }
+
+ static class BooleanAsyncSupplier implements Runnable {
+ private CompletableFuture<CompletableFuture<Boolean>> dep;
+ private Supplier<CompletableFuture<Boolean>> fn;
+ private String taskName;
+
+ BooleanAsyncSupplier(String taskName, Supplier<CompletableFuture<Boolean>> fn) {
+ this.taskName = taskName;
+ this.dep = new CompletableFuture<>();
+ this.fn = fn;
+ }
+
+ public CompletableFuture<Boolean> future() {
+ return dep.thenCompose(Function.identity());
+ }
+
+ @Override
+ public void run() {
+ try {
+ dep.complete(fn.get());
+ } catch (Throwable ex) {
+ dep.completeExceptionally(ex);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return taskName;
+ }
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeChecker.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeChecker.java
index ce04589..ef7f942 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeChecker.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeChecker.java
@@ -16,7 +16,6 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.Optional;
@@ -27,18 +26,16 @@
* Return the Change notes read from ReviewDb or NoteDb.
*
* @return notes of the Change
- * @throws OrmException if ReviewDb or NoteDb cannot be opened
*/
- Optional<ChangeNotes> getChangeNotes() throws OrmException;
+ Optional<ChangeNotes> getChangeNotes();
/**
* Create a new index event POJO associated with the current Change.
*
* @return new IndexEvent
* @throws IOException if the current Change cannot read
- * @throws OrmException if ReviewDb cannot be opened
*/
- Optional<IndexEvent> newIndexEvent() throws IOException, OrmException;
+ Optional<IndexEvent> newIndexEvent() throws IOException;
/**
* Check if the local Change is aligned with the indexEvent received.
@@ -46,9 +43,8 @@
* @param indexEvent indexing event
* @return true if the local Change is up-to-date, false otherwise.
* @throws IOException if an I/O error occurred while reading the local Change
- * @throws OrmException if the local ReviewDb cannot be opened
*/
- boolean isChangeUpToDate(Optional<IndexEvent> indexEvent) throws IOException, OrmException;
+ boolean isChangeUpToDate(Optional<IndexEvent> indexEvent) throws IOException;
/**
* Return the last computed up-to-date Change time-stamp.
@@ -57,7 +53,6 @@
*
* @return the Change timestamp epoch in seconds
* @throws IOException if an I/O error occurred while reading the local Change
- * @throws OrmException if the local ReviewDb cannot be opened
*/
- Optional<Long> getComputedChangeTs() throws IOException, OrmException;
+ Optional<Long> getComputedChangeTs() throws IOException;
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
index a1b797f..1656eb9 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
@@ -19,15 +19,12 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -41,7 +38,6 @@
private static final FluentLogger log = FluentLogger.forEnclosingClass();
private final GitRepositoryManager gitRepoMgr;
private final CommentsUtil commentsUtil;
- private final ChangeDb changeDb;
private final OneOffRequestContext oneOffReqCtx;
private final String changeId;
private final ChangeFinder changeFinder;
@@ -56,20 +52,18 @@
public ChangeCheckerImpl(
GitRepositoryManager gitRepoMgr,
CommentsUtil commentsUtil,
- ChangeDb changeDb,
ChangeFinder changeFinder,
OneOffRequestContext oneOffReqCtx,
@Assisted String changeId) {
this.changeFinder = changeFinder;
this.gitRepoMgr = gitRepoMgr;
this.commentsUtil = commentsUtil;
- this.changeDb = changeDb;
this.oneOffReqCtx = oneOffReqCtx;
this.changeId = changeId;
}
@Override
- public Optional<IndexEvent> newIndexEvent() throws IOException, OrmException {
+ public Optional<IndexEvent> newIndexEvent() throws IOException {
Optional<Long> changeTs = getComputedChangeTs();
if (!changeTs.isPresent()) {
return Optional.empty();
@@ -91,7 +85,7 @@
}
@Override
- public Optional<ChangeNotes> getChangeNotes() throws OrmException {
+ public Optional<ChangeNotes> getChangeNotes() {
try (ManualRequestContext ctx = oneOffReqCtx.open()) {
changeNotes = Optional.ofNullable(changeFinder.findOne(changeId));
return changeNotes;
@@ -99,8 +93,7 @@
}
@Override
- public boolean isChangeUpToDate(Optional<IndexEvent> indexEventOption)
- throws IOException, OrmException {
+ public boolean isChangeUpToDate(Optional<IndexEvent> indexEventOption) throws IOException {
getComputedChangeTs();
log.atFine().log("Checking change %s against index event %s", this, indexEventOption);
if (!computedChangeTs.isPresent()) {
@@ -128,7 +121,7 @@
}
@Override
- public Optional<Long> getComputedChangeTs() throws IOException, OrmException {
+ public Optional<Long> getComputedChangeTs() {
if (!computedChangeTs.isPresent()) {
computedChangeTs = computeLastChangeTs();
}
@@ -146,7 +139,7 @@
+ getBranchTargetSha(repo)
+ "/meta:"
+ getMetaSha(repo);
- } catch (IOException | OrmException e) {
+ } catch (IOException e) {
log.atSevere().withCause(e).log("Unable to render change %s", changeId);
return "change-id=" + changeId;
}
@@ -168,36 +161,26 @@
}
}
+ private Optional<Long> computeLastChangeTs() {
+ return getChangeNotes().map(this::getTsFromChangeAndDraftComments);
+ }
+
private String getMetaSha(Repository repo) throws IOException {
- if (PrimaryStorage.NOTE_DB.equals(PrimaryStorage.of(changeNotes.get().getChange()))) {
- String refName = RefNames.changeMetaRef(changeNotes.get().getChange().getId());
- Ref ref = repo.exactRef(refName);
- if (ref == null) {
- throw new IOException(
- String.format("Unable to find meta ref %s for change %s", refName, changeId));
- }
- return ref.getTarget().getObjectId().getName();
+ String refName = RefNames.changeMetaRef(changeNotes.get().getChange().getId());
+ Ref ref = repo.exactRef(refName);
+ if (ref == null) {
+ throw new IOException(
+ String.format("Unable to find meta ref %s for change %s", refName, changeId));
}
-
- return null;
+ return ref.getTarget().getObjectId().getName();
}
- private Optional<Long> computeLastChangeTs() throws OrmException {
- try (ReviewDb db = changeDb.open()) {
- return getChangeNotes().map(notes -> getTsFromChangeAndDraftComments(db, notes));
- }
- }
-
- private long getTsFromChangeAndDraftComments(ReviewDb db, ChangeNotes notes) {
+ private long getTsFromChangeAndDraftComments(ChangeNotes notes) {
Change change = notes.getChange();
Timestamp changeTs = change.getLastUpdatedOn();
- try {
- for (Comment comment : commentsUtil.draftByChange(db, changeNotes.get())) {
- Timestamp commentTs = comment.writtenOn;
- changeTs = commentTs.after(changeTs) ? commentTs : changeTs;
- }
- } catch (OrmException e) {
- log.atWarning().withCause(e).log("Unable to access draft comments for change %s", change);
+ for (Comment comment : commentsUtil.draftByChange(changeNotes.get())) {
+ Timestamp commentTs = comment.writtenOn;
+ changeTs = commentTs.after(changeTs) ? commentTs : changeTs;
}
return changeTs.getTime() / 1000;
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeDb.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeDb.java
deleted file mode 100644
index bef5363..0000000
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeDb.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.ericsson.gerrit.plugins.highavailability.index;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-
-public class ChangeDb {
- private static final DisabledReviewDb disabledReviewDb = new DisabledReviewDb();
-
- private final NotesMigration migration;
- private final SchemaFactory<ReviewDb> schemaFactory;
-
- @Inject
- public ChangeDb(NotesMigration migration, SchemaFactory<ReviewDb> schemaFactory) {
- this.migration = migration;
- this.schemaFactory = schemaFactory;
- }
-
- public ReviewDb open() throws OrmException {
- return migration.readChanges() ? disabledReviewDb : schemaFactory.open();
- }
-}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/CurrentRequestContext.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/CurrentRequestContext.java
index f8c6e31..46aadbb 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/CurrentRequestContext.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/CurrentRequestContext.java
@@ -20,7 +20,6 @@
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.function.Consumer;
@@ -51,8 +50,6 @@
if (ctx == null) {
try (ManualRequestContext manualCtx = oneOffCtx.open()) {
body.accept(manualCtx);
- } catch (OrmException e) {
- logger.atSevere().withCause(e).log("Unable to open request context");
}
} else {
body.accept(ctx);
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/DisabledReviewDb.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/DisabledReviewDb.java
deleted file mode 100644
index 48c6ecb..0000000
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/DisabledReviewDb.java
+++ /dev/null
@@ -1,201 +0,0 @@
-// 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.ericsson.gerrit.plugins.highavailability.index;
-
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ChangeAccess;
-import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
-import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
-import com.google.gerrit.reviewdb.server.PatchSetAccess;
-import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import com.google.gwtorm.server.StatementExecutor;
-import java.util.Map;
-
-/** ReviewDb that is disabled. */
-@SuppressWarnings("deprecation")
-public class DisabledReviewDb implements ReviewDb {
- public static class Disabled extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- private Disabled() {
- super("ReviewDb is disabled for changes");
- }
- }
-
- public static class DisabledChangeAccess implements ChangeAccess {
-
- @Override
- public String getRelationName() {
- throw new Disabled();
- }
-
- @Override
- public int getRelationID() {
- throw new Disabled();
- }
-
- @Override
- public ResultSet<Change> iterateAllEntities() throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public Change.Id primaryKey(Change entity) {
- throw new Disabled();
- }
-
- @Override
- public Map<Change.Id, Change> toMap(Iterable<Change> c) {
- throw new Disabled();
- }
-
- @Override
- public CheckedFuture<Change, OrmException> getAsync(Change.Id key) {
- throw new Disabled();
- }
-
- @Override
- public ResultSet<Change> get(Iterable<Change.Id> keys) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void insert(Iterable<Change> instances) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void update(Iterable<Change> instances) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void upsert(Iterable<Change> instances) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void deleteKeys(Iterable<Change.Id> keys) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void delete(Iterable<Change> instances) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public void beginTransaction(Change.Id key) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public Change atomicUpdate(Change.Id key, AtomicUpdate<Change> update) throws OrmException {
- throw new Disabled();
- }
-
- @Override
- public Change get(Change.Id id) throws OrmException {
- return null;
- }
-
- @Override
- public ResultSet<Change> all() throws OrmException {
- return null;
- }
- }
-
- @Override
- public void close() {
- // Do nothing.
- }
-
- @Override
- public void commit() {
- throw new Disabled();
- }
-
- @Override
- public void rollback() {
- throw new Disabled();
- }
-
- @Override
- public void updateSchema(StatementExecutor e) {
- throw new Disabled();
- }
-
- @Override
- public void pruneSchema(StatementExecutor e) {
- throw new Disabled();
- }
-
- @Override
- public Access<?, ?>[] allRelations() {
- throw new Disabled();
- }
-
- @Override
- public SchemaVersionAccess schemaVersion() {
- throw new Disabled();
- }
-
- @Override
- public ChangeAccess changes() {
- return new DisabledChangeAccess();
- }
-
- @Override
- public PatchSetApprovalAccess patchSetApprovals() {
- throw new Disabled();
- }
-
- @Override
- public ChangeMessageAccess changeMessages() {
- throw new Disabled();
- }
-
- @Override
- public PatchSetAccess patchSets() {
- throw new Disabled();
- }
-
- @Override
- public PatchLineCommentAccess patchComments() {
- throw new Disabled();
- }
-
- @Override
- public int nextAccountId() {
- throw new Disabled();
- }
-
- @Override
- public int nextAccountGroupId() {
- throw new Disabled();
- }
-
- @Override
- public int nextChangeId() {
- throw new Disabled();
- }
-}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
index 068ef0d..73e842f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
@@ -14,6 +14,7 @@
package com.ericsson.gerrit.plugins.highavailability.index;
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
@@ -27,8 +28,10 @@
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
class IndexEventHandler
implements ChangeIndexedListener,
@@ -36,28 +39,37 @@
GroupIndexedListener,
ProjectIndexedListener {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
- private final Executor executor;
- private final Executor batchExecutor;
+ private final ScheduledExecutorService executor;
+ private final ScheduledExecutorService batchExecutor;
private final Forwarder forwarder;
private final String pluginName;
private final Set<IndexTask> queuedTasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final ChangeCheckerImpl.Factory changeChecker;
private final CurrentRequestContext currCtx;
+ private final IndexEventLocks locks;
+
+ private final int retryInterval;
+ private final int maxTries;
@Inject
IndexEventHandler(
- @IndexExecutor Executor executor,
- @BatchIndexExecutor Executor batchExecutor,
+ @IndexExecutor ScheduledExecutorService executor,
+ @BatchIndexExecutor ScheduledExecutorService batchExecutor,
@PluginName String pluginName,
Forwarder forwarder,
ChangeCheckerImpl.Factory changeChecker,
- CurrentRequestContext currCtx) {
+ CurrentRequestContext currCtx,
+ Configuration cfg,
+ IndexEventLocks locks) {
this.forwarder = forwarder;
this.executor = executor;
this.batchExecutor = batchExecutor;
this.pluginName = pluginName;
this.changeChecker = changeChecker;
this.currCtx = currCtx;
+ this.locks = locks;
+ this.retryInterval = cfg.http().retryInterval();
+ this.maxTries = cfg.http().maxTries();
}
@Override
@@ -141,6 +153,7 @@
abstract class IndexTask implements Runnable {
protected final IndexEvent indexEvent;
+ private int retryCount = 0;
IndexTask() {
indexEvent = new IndexEvent();
@@ -152,11 +165,27 @@
@Override
public void run() {
- queuedTasks.remove(this);
- execute();
+ locks.withLock(
+ this,
+ () -> {
+ queuedTasks.remove(this);
+ return execute();
+ },
+ this::reschedule);
}
- abstract void execute();
+ private void reschedule() {
+ if (++retryCount <= maxTries) {
+ log.atFine().log("Retrying %d times to %s", retryCount, this);
+ executor.schedule(this, retryInterval, TimeUnit.MILLISECONDS);
+ } else {
+ log.atSevere().log("Failed to %s after %d tries; giving up", this, maxTries);
+ }
+ }
+
+ abstract CompletableFuture<Boolean> execute();
+
+ abstract String indexId();
}
class IndexChangeTask extends IndexTask {
@@ -170,8 +199,8 @@
}
@Override
- public void execute() {
- forwarder.indexChange(projectName, changeId, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.indexChange(projectName, changeId, indexEvent);
}
@Override
@@ -192,6 +221,11 @@
public String toString() {
return String.format("[%s] Index change %s in target instance", pluginName, changeId);
}
+
+ @Override
+ String indexId() {
+ return "change/" + changeId;
+ }
}
class BatchIndexChangeTask extends IndexTask {
@@ -205,8 +239,13 @@
}
@Override
- public void execute() {
- forwarder.batchIndexChange(projectName, changeId, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.batchIndexChange(projectName, changeId, indexEvent);
+ }
+
+ @Override
+ String indexId() {
+ return "change/" + changeId;
}
@Override
@@ -238,8 +277,8 @@
}
@Override
- public void execute() {
- forwarder.deleteChangeFromIndex(changeId, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.deleteChangeFromIndex(changeId, indexEvent);
}
@Override
@@ -260,6 +299,11 @@
public String toString() {
return String.format("[%s] Delete change %s in target instance", pluginName, changeId);
}
+
+ @Override
+ String indexId() {
+ return "change/" + changeId;
+ }
}
class IndexAccountTask extends IndexTask {
@@ -270,8 +314,8 @@
}
@Override
- public void execute() {
- forwarder.indexAccount(accountId, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.indexAccount(accountId, indexEvent);
}
@Override
@@ -292,6 +336,11 @@
public String toString() {
return String.format("[%s] Index account %s in target instance", pluginName, accountId);
}
+
+ @Override
+ String indexId() {
+ return "account/" + accountId;
+ }
}
class IndexGroupTask extends IndexTask {
@@ -302,8 +351,8 @@
}
@Override
- public void execute() {
- forwarder.indexGroup(groupUUID, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.indexGroup(groupUUID, indexEvent);
}
@Override
@@ -324,6 +373,11 @@
public String toString() {
return String.format("[%s] Index group %s in target instance", pluginName, groupUUID);
}
+
+ @Override
+ String indexId() {
+ return "group/" + groupUUID;
+ }
}
class IndexProjectTask extends IndexTask {
@@ -334,8 +388,8 @@
}
@Override
- public void execute() {
- forwarder.indexProject(projectName, indexEvent);
+ public CompletableFuture<Boolean> execute() {
+ return forwarder.indexProject(projectName, indexEvent);
}
@Override
@@ -356,5 +410,10 @@
public String toString() {
return String.format("[%s] Index project %s in target instance", pluginName, projectName);
}
+
+ @Override
+ String indexId() {
+ return "project/" + projectName;
+ }
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventLocks.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventLocks.java
new file mode 100644
index 0000000..3fd26db
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventLocks.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 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.ericsson.gerrit.plugins.highavailability.index;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexTask;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.flogger.FluentLogger;
+import com.google.common.util.concurrent.Striped;
+import com.google.inject.Inject;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class IndexEventLocks {
+ private static final FluentLogger log = FluentLogger.forEnclosingClass();
+
+ private static final int NUMBER_OF_INDEX_TASK_TYPES = 4;
+ private static final int WAIT_TIMEOUT_MS = 5;
+
+ private final Striped<Semaphore> semaphores;
+
+ @Inject
+ public IndexEventLocks(Configuration cfg) {
+ this.semaphores =
+ Striped.semaphore(NUMBER_OF_INDEX_TASK_TYPES * cfg.index().numStripedLocks(), 1);
+ }
+
+ public CompletableFuture<?> withLock(
+ IndexTask id, IndexCallFunction function, VoidFunction lockAcquireTimeoutCallback) {
+ String indexId = id.indexId();
+ Semaphore idSemaphore = getSemaphore(indexId);
+ try {
+ log.atFine().log("Trying to acquire %s", id);
+ if (idSemaphore.tryAcquire(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ log.atFine().log("Acquired %s", id);
+ return function
+ .invoke()
+ .whenComplete(
+ (result, error) -> {
+ try {
+ log.atFine().log("Trying to release %s", id);
+ idSemaphore.release();
+ log.atFine().log("Released %s", id);
+ } catch (Throwable t) {
+ log.atSevere().withCause(t).log("Unable to release %s", id);
+ throw t;
+ }
+ });
+ }
+
+ String timeoutMessage =
+ String.format(
+ "Acquisition of the locking of %s timed out after %d msec: consider increasing the number of shards",
+ indexId, WAIT_TIMEOUT_MS);
+ log.atWarning().log(timeoutMessage);
+ lockAcquireTimeoutCallback.invoke();
+ CompletableFuture<?> failureFuture = new CompletableFuture<>();
+ failureFuture.completeExceptionally(new InterruptedException(timeoutMessage));
+ return failureFuture;
+ } catch (InterruptedException e) {
+ CompletableFuture<?> failureFuture = new CompletableFuture<>();
+ failureFuture.completeExceptionally(e);
+ log.atSevere().withCause(e).log("Locking of %s was interrupted; giving up", indexId);
+ return failureFuture;
+ }
+ }
+
+ @VisibleForTesting
+ protected Semaphore getSemaphore(String indexId) {
+ return semaphores.get(indexId);
+ }
+
+ @FunctionalInterface
+ public interface VoidFunction {
+ void invoke();
+ }
+
+ @FunctionalInterface
+ public interface IndexCallFunction {
+ CompletableFuture<?> invoke();
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
index 4258dce..3bcc34f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
@@ -20,26 +20,31 @@
import com.google.gerrit.extensions.events.ProjectIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.inject.Scopes;
import com.google.inject.assistedinject.FactoryModuleBuilder;
-import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
public class IndexModule extends LifecycleModule {
@Override
protected void configure() {
- bind(Executor.class).annotatedWith(IndexExecutor.class).toProvider(IndexExecutorProvider.class);
- bind(Executor.class)
- .annotatedWith(BatchIndexExecutor.class)
- .toProvider(BatchIndexExecutorProvider.class);
+ bind(ScheduledExecutorService.class)
+ .annotatedWith(IndexExecutor.class)
+ .toProvider(IndexExecutorProvider.class);
bind(ScheduledExecutorService.class)
.annotatedWith(ForwardedIndexExecutor.class)
.toProvider(ForwardedIndexExecutorProvider.class);
+ bind(IndexEventLocks.class).in(Scopes.SINGLETON);
+ bind(ScheduledExecutorService.class)
+ .annotatedWith(BatchIndexExecutor.class)
+ .toProvider(BatchIndexExecutorProvider.class);
bind(ScheduledExecutorService.class)
.annotatedWith(ForwardedBatchIndexExecutor.class)
.toProvider(ForwardedBatchIndexExecutorProvider.class);
listener().to(IndexExecutorProvider.class);
- DynamicSet.bind(binder(), ChangeIndexedListener.class).to(IndexEventHandler.class);
+ DynamicSet.bind(binder(), ChangeIndexedListener.class)
+ .to(IndexEventHandler.class)
+ .in(Scopes.SINGLETON);
DynamicSet.bind(binder(), AccountIndexedListener.class).to(IndexEventHandler.class);
DynamicSet.bind(binder(), GroupIndexedListener.class).to(IndexEventHandler.class);
DynamicSet.bind(binder(), ProjectIndexedListener.class).to(IndexEventHandler.class);
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index c7196c6..fd0b56e 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,20 +1,19 @@
This plugin allows making Gerrit highly available by having redundant Gerrit
-masters.
+active/passive instances.
-The masters must be:
+The Gerrit instances must be:
-* connecting to the same database
* sharing the git repositories using a shared file system (e.g. NFS)
* behind a load balancer (e.g. HAProxy)
-Currently, the mode supported is one primary (active) master and multiple backup
-(passive) masters but eventually the plan is to support `n` active masters. In
-the active/passive mode, the active master is handling all traffic while the
-passives are kept updated to be always ready to take over.
+Currently, the mode supported is one active instance and multiple backup
+(passive) instances but eventually the plan is to support `n` active instances.
+In the active/passive mode, the active instance is handling all traffic while
+the passives are kept updated to be always ready to take over.
-Even if database and git repositories are shared by the masters, there are a few
-areas of concern in order to be able to switch traffic between masters in a
+Even if git repositories are shared by the instances, there are a few areas
+of concern in order to be able to switch traffic between instances in a
transparent manner from the user's perspective. The 4 areas of concern are
things that Gerrit stores either in memory or locally in the review site:
@@ -23,68 +22,67 @@
* stream-events
* web sessions
-They need either to be shared or kept local to each master but synchronized.
-This plugin needs to be installed in all the masters and it will take care of sharing
-or synchronizing them.
+They need either to be shared or kept local to each instances but synchronized.
+This plugin needs to be installed in all the instances and it will take care of
+sharing or synchronizing them.
#### Caches
-Every time a cache eviction occurs in one of the masters, the eviction will be
-forwarded the other masters so their caches do not contain stale entries.
+Every time a cache eviction occurs in one of the instances, the eviction will be
+forwarded the other nodes so their caches do not contain stale entries.
#### Secondary indexes
-Every time the secondary index is modified in one of the masters, e.g., a change
-is added, updated or removed from the index, the others master's index are
-updated accordingly. This way, both indexes are kept synchronized.
+Every time the secondary index is modified in one of the instances, e.g., a change
+is added, updated or removed from the index, the others instances index are updated
+accordingly. This way, both indexes are kept synchronized.
#### Stream events
-Every time a stream event occurs in one of the masters (see [more events info]
-(https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events)),
-the event is forwarded to the other masters which re-plays it. This way, the
-output of the stream-events command is the same, no matter which master a client
-is connected to.
+Every time a stream event occurs in one of the instances
+(see [more events info](https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events)),
+the event is forwarded to the other instances which re-plays it. This way, the output
+of the stream-events command is the same, no matter which instance a client is
+connected to.
#### Web session
The built-in Gerrit H2 based web session cache is replaced with a file based
-implementation that is shared amongst the masters.
+implementation that is shared amongst the instances.
## Setup
Prerequisites:
-* Unique database server must be accessible from all the masters
* Git repositories must be located on a shared file system
* A directory on a shared file system must be available for @PLUGIN@ to use
-For the masters:
+For the instances:
-* Configure database section in gerrit.config to use the shared database
* Configure gerrit.basePath in gerrit.config to the shared repositories location
-* Install and configure @PLUGIN@ plugin
+* Configure gerrit.serverId in gerrit.config based on [config](config.md)'s introduction
+* Install and configure this @PLUGIN@ plugin [further](config.md) or based on below.
Here is an example of the minimal @PLUGIN@.config:
-Primary master
+Active instance
```
[main]
- sharedDirectory = /directory/accessible/from/both/masters
+ sharedDirectory = /directory/accessible/from/both/nodes
[peerInfo "static"]
- url = http://backupMasterHost1:8081/
+ url = http://backupNodeHost1:8081/
[http]
user = username
password = password
```
-Backup master
+Backup instance
```
[main]
- sharedDirectory = /directory/accessible/from/both/masters
+ sharedDirectory = /directory/accessible/from/both/nodes
[peerInfo "static"]
- url = http://primaryMasterHost:8080/
+ url = http://primaryNodeHost:8080/
[http]
user = username
@@ -93,10 +91,10 @@
### HA replica site
-It is possible to create a copy of the master site and configure both sites to run
-in HA mode as peers. This is possible when the directory where the copy will be
-created is accessible from this machine. Such a replica site can be created by
-means of a gerrit [site init](../../../Documentation/pgm-init.html) step,
+It is possible to create a copy of the instance site and configure both
+sites to run in HA mode as peers. This is possible when the directory where
+the copy will be created is accessible from this machine. Such a replica site
+can be created by means of a gerrit [site init](../../../Documentation/pgm-init.html) step,
contributed by the plugin under its init section.
This init step is optional but defaults to creating the replica. If you want to
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index ea20e89..360218d 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -2,8 +2,14 @@
@PLUGIN@ Configuration
=========================
-The @PLUGIN@ plugin must be installed on all the instances and the following fields
-should be specified in `$site_path/etc/@PLUGIN@.config` file:
+The @PLUGIN@ plugin must be installed on all the instances. Each instance should
+be configured with the same [gerrit.serverId](https://gerrit-documentation.storage.googleapis.com/Documentation/3.0.0/config-gerrit.html#gerrit.serverId).
+If there are existing changes in [NoteDb](https://gerrit-documentation.storage.googleapis.com/Documentation/3.0.0/note-db.html)
+made with another `serverId`, then this plugin might not be able to access them.
+Likewise, if the HA gerrit.serverIds differ, then changes conveyed by one
+instance will not be accessible by the other.
+
+The following fields should be specified in `$site_path/etc/@PLUGIN@.config` files:
File '@PLUGIN@.config'
--------------------
@@ -49,7 +55,7 @@
```
```main.sharedDirectory```
-: Path to a directory accessible from both master instances.
+: Path to a directory accessible from both instances.
When given as a relative path, then it is resolved against the $SITE_PATH
or Gerrit server. For example, if $SITE_PATH is "/gerrit/root" and
sharedDirectory is given as "shared/dir" then the real path of the shared
@@ -75,13 +81,6 @@
Delay is expressed in Gerrit time values as in [websession.cleanupInterval](#websessioncleanupInterval).
When not specified, polling of conditional reindexing is disabled.
-```autoReindex.interval```
-: Enable the tracking of the latest change indexed under data/high-availability
- for each of the indexes. At startup scans all the changes, accounts and groups
- and reindex the ones that have been updated by other nodes while the server was down.
- When not specified, the default is "false", that means no automatic tracking
- and indexing at start.
-
```peerInfo.strategy```
: Strategy to find other peers. Supported strategies are `static` or `jgroups`.
Defaults to `jgroups`.
@@ -161,9 +160,6 @@
: The interval of time in milliseconds between the subsequent auto-retries.
When not specified, the default value is set to 10000ms.
-NOTE: the default settings for `http.timeout` and `http.maxTries` ensure that
-the plugin will keep retrying to forward a message for one hour.
-
```cache.synchronize```
: Whether to synchronize cache evictions.
Defaults to true.
@@ -216,6 +212,9 @@
: The interval of time in milliseconds between the subsequent auto-retries.
Defaults to 30000 (30 seconds).
+NOTE: the default settings for `http.socketTimeout` and `http.maxTries` ensure
+that the plugin will keep retrying to forward a message for one hour.
+
```websession.synchronize```
: Whether to synchronize web sessions.
Defaults to true.
diff --git a/src/test/docker/docker-compose.yaml b/src/test/docker/docker-compose.yaml
index 93971b3..647aa6d 100644
--- a/src/test/docker/docker-compose.yaml
+++ b/src/test/docker/docker-compose.yaml
@@ -2,17 +2,6 @@
services:
- postgres:
- image: postgres:9.5.4
- environment:
- - POSTGRES_USER=gerrit
- - POSTGRES_PASSWORD=secret
- - POSTGRES_DB=reviewdb
- networks:
- - gerrit-net
- volumes:
- - ./pgdata:/var/lib/postgresql/data
-
gerrit-01:
build: gerrit
ports:
@@ -20,8 +9,6 @@
- "29411:29418"
networks:
- gerrit-net
- depends_on:
- - postgres
volumes:
- /dev/urandom:/dev/random
- ./gitvolume:/var/gerrit/git
@@ -39,7 +26,6 @@
networks:
- gerrit-net
depends_on:
- - postgres
- gerrit-01
volumes:
- /dev/urandom:/dev/random
diff --git a/src/test/docker/etc/gerrit.config b/src/test/docker/etc/gerrit.config
index c110835..90a4057 100644
--- a/src/test/docker/etc/gerrit.config
+++ b/src/test/docker/etc/gerrit.config
@@ -1,12 +1,7 @@
[gerrit]
basePath = git
canonicalWebUrl = http://gerrit:8080/
-[database]
- type = postgresql
- hostname = postgres
- database = reviewdb
- username = gerrit
- password = secret
+ serverId = f7696647-8efd-41b1-bd60-d321bc071ea9
[index]
type = LUCENE
[auth]
diff --git a/src/test/docker/gerrit/Dockerfile b/src/test/docker/gerrit/Dockerfile
index 537cb3c..707e5ae 100644
--- a/src/test/docker/gerrit/Dockerfile
+++ b/src/test/docker/gerrit/Dockerfile
@@ -1,19 +1,17 @@
-FROM gerritcodereview/gerrit:2.16.22
+FROM gerritcodereview/gerrit:3.0.12
-ENV GERRIT_BRANCH=stable-2.16
+ENV GERRIT_BRANCH=stable-3.0
ENV GERRIT_CI_URL=https://gerrit-ci.gerritforge.com/job
USER root
-RUN yum install -y iputils-ping netcat postgresql curl lsof gettext moreutils net-tools netcat inetutils-ping sudo
+RUN yum install -y iputils-ping netcat curl lsof gettext moreutils net-tools netcat inetutils-ping sudo
USER gerrit
ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-javamelody-bazel-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/javamelody/javamelody.jar /var/gerrit/plugins/javamelody.jar
-ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-javamelody-bazel-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/javamelody/javamelody-deps_deploy.jar /var/gerrit/lib/javamelody-deps_deploy.jar
ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-high-availability-bazel-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/high-availability/high-availability.jar /var/gerrit/plugins/high-availability.jar
-ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-delete-project-bazel-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/delete-project/delete-project.jar /var/gerrit/plugins/delete-project.jar
USER root
@@ -24,4 +22,6 @@
ARG GERRIT_UID=1000
RUN usermod -u ${GERRIT_UID} gerrit &> /dev/null
+
+ENTRYPOINT ["/usr/bin/env"]
CMD /bin/start.sh
diff --git a/src/test/docker/gerrit/start.sh b/src/test/docker/gerrit/start.sh
index 7aa3550..0dae4c3 100755
--- a/src/test/docker/gerrit/start.sh
+++ b/src/test/docker/gerrit/start.sh
@@ -1,18 +1,14 @@
#!/bin/bash -e
-wait-for-it.sh postgres:5432 -t 600 -- echo "Postgres is up"
-
if [[ ! -z "$WAIT_FOR" ]]
then
wait-for-it.sh $WAIT_FOR -t 600 -- echo "$WAIT_FOR is up"
fi
-chown -R gerrit: /var/gerrit
-
sudo -u gerrit cp /var/gerrit/etc/gerrit.config.orig /var/gerrit/etc/gerrit.config
sudo -u gerrit cp /var/gerrit/etc/high-availability.config.orig /var/gerrit/etc/high-availability.config
-if [[ ! -f /var/gerrit/git/All-Projects.git/config ]]
+if [[ ! -f /var/gerrit/etc/ssh_host_ed25519_key ]]
then
echo "Initializing Gerrit site ..."
sudo -u gerrit java -jar /var/gerrit/bin/gerrit.war init -d /var/gerrit --batch
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
index 5bca2cb..3444625 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
@@ -19,6 +19,7 @@
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Cache.PATTERN_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_NUM_STRIPED_LOCKS;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_THREAD_POOL_SIZE;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_TIMEOUT_MS;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Event.EVENT_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Forwarding.DEFAULT_SYNCHRONIZE;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Forwarding.SYNCHRONIZE_KEY;
@@ -28,7 +29,6 @@
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.CONNECTION_TIMEOUT_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_MAX_TRIES;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_RETRY_INTERVAL;
-import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.DEFAULT_TIMEOUT_MS;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.HTTP_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.MAX_TRIES_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Http.PASSWORD_KEY;
@@ -115,15 +115,16 @@
@Test
public void testGetPeerInfoStrategy() {
- assertThat(getConfiguration().peerInfo().strategy()).isSameAs(DEFAULT_PEER_INFO_STRATEGY);
+ assertThat(getConfiguration().peerInfo().strategy())
+ .isSameInstanceAs(DEFAULT_PEER_INFO_STRATEGY);
globalPluginConfig.setString(
PEER_INFO_SECTION, null, STRATEGY_KEY, PeerInfoStrategy.STATIC.name());
- assertThat(getConfiguration().peerInfo().strategy()).isSameAs(PeerInfoStrategy.STATIC);
+ assertThat(getConfiguration().peerInfo().strategy()).isSameInstanceAs(PeerInfoStrategy.STATIC);
globalPluginConfig.setString(
PEER_INFO_SECTION, null, STRATEGY_KEY, PeerInfoStrategy.JGROUPS.name());
- assertThat(getConfiguration().peerInfo().strategy()).isSameAs(PeerInfoStrategy.JGROUPS);
+ assertThat(getConfiguration().peerInfo().strategy()).isSameInstanceAs(PeerInfoStrategy.JGROUPS);
}
@Test
@@ -132,7 +133,7 @@
globalPluginConfig.setStringList(PEER_INFO_SECTION, STATIC_SUBSECTION, URL_KEY, URLS);
assertThat(getConfiguration().peerInfoStatic().urls())
- .containsAllIn(ImmutableList.of(URL, "http://anotherUrl"));
+ .containsAtLeastElementsIn(ImmutableList.of(URL, "http://anotherUrl"));
}
@Test
@@ -156,7 +157,9 @@
globalPluginConfig.setStringList(
JGROUPS_SECTION, null, SKIP_INTERFACE_KEY, ImmutableList.of("lo*", "eth0"));
- assertThat(getConfiguration().jgroups().skipInterface()).containsAllOf("lo*", "eth0").inOrder();
+ assertThat(getConfiguration().jgroups().skipInterface())
+ .containsExactly("lo*", "eth0")
+ .inOrder();
}
@Test
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcherTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcherTest.java
index 03c1928..9b6e66b 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcherTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CachePatternMatcherTest.java
@@ -20,6 +20,7 @@
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.common.collect.ImmutableList;
+import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -76,4 +77,28 @@
assertWithMessage(cache + " should not match").that(matcher.matches(cache)).isFalse();
}
}
+
+ @Test
+ public void testShouldNotMatchWebSessionsWhenNotSynchronized() {
+ String cache = "web_sessions";
+ when(configurationMock.cache().patterns()).thenReturn(Collections.emptyList());
+ when(configurationMock.websession().synchronize()).thenReturn(false);
+ CachePatternMatcher matcher = new CachePatternMatcher(configurationMock);
+
+ assertWithMessage(cache + " should NOT match when websession.synchronize is false")
+ .that(matcher.matches(cache))
+ .isFalse();
+ }
+
+ @Test
+ public void testShouldMatchWebSessionsWhenSynchronized() {
+ String cache = "web_sessions";
+ when(configurationMock.cache().patterns()).thenReturn(Collections.emptyList());
+ when(configurationMock.websession().synchronize()).thenReturn(true);
+ CachePatternMatcher matcher = new CachePatternMatcher(configurationMock);
+
+ assertWithMessage(cache + " should match when websession.synchronize is true")
+ .that(matcher.matches(cache))
+ .isTrue();
+ }
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/event/EventDeserializerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/event/EventDeserializerTest.java
new file mode 100644
index 0000000..2b9ee35
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/event/EventDeserializerTest.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2020 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.ericsson.gerrit.plugins.highavailability.event;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.GsonProvider;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.data.AccountAttribute;
+import com.google.gerrit.server.data.RefUpdateAttribute;
+import com.google.gerrit.server.events.RefUpdatedEvent;
+import com.google.gson.Gson;
+import org.junit.Test;
+
+public class EventDeserializerTest {
+ private static final String LEGACY_PROJECT_KEY = "{\"name\": \"project\"}";
+
+ private static final String NEW_PROJECT_KEY = "\"project\"";
+
+ private final Gson gson = new GsonProvider().get();
+
+ @Test
+ public void deserializePatchSetCreatedEventLegacyProjectKey() {
+ Project.NameKey n = gson.fromJson(LEGACY_PROJECT_KEY, Project.NameKey.class);
+ assertThat(n.get()).isEqualTo("project");
+ }
+
+ @Test
+ public void deserializePatchSetCreatedEventNewProjectKey() {
+ Project.NameKey n = gson.fromJson(NEW_PROJECT_KEY, Project.NameKey.class);
+ assertThat(n.get()).isEqualTo("project");
+ }
+
+ @Test
+ public void refUpdatedEventRoundTrip() {
+ RefUpdatedEvent refUpdatedEvent = new RefUpdatedEvent();
+
+ RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute();
+ refUpdatedAttribute.refName = "refs/heads/master";
+ refUpdatedEvent.refUpdate = createSupplier(refUpdatedAttribute);
+
+ AccountAttribute accountAttribute = new AccountAttribute();
+ accountAttribute.email = "some.user@domain.com";
+ refUpdatedEvent.submitter = createSupplier(accountAttribute);
+
+ String serializedEvent = gson.toJson(refUpdatedEvent);
+ RefUpdatedEvent e = gson.fromJson(serializedEvent, RefUpdatedEvent.class);
+
+ assertThat(e).isNotNull();
+ assertThat(e.refUpdate).isInstanceOf(Supplier.class);
+ assertThat(e.refUpdate.get().refName).isEqualTo(refUpdatedAttribute.refName);
+ assertThat(e.submitter).isInstanceOf(Supplier.class);
+ assertThat(e.submitter.get().email).isEqualTo(accountAttribute.email);
+ }
+
+ private static <T> Supplier<T> createSupplier(T value) {
+ return Suppliers.memoize(() -> value);
+ }
+}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
index ec5b1de..36cc953 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
@@ -39,7 +39,7 @@
DynamicSet<EventListener> set = DynamicSet.emptySet();
set.add("high-availability", listenerMock);
PluginSetContext<EventListener> listeners = new PluginSetContext<>(set, mockMetrics);
- broker = new ForwardedAwareEventBroker(null, listeners, null, null, null, null);
+ broker = new ForwardedAwareEventBroker(null, listeners, null, null, null);
}
@Test
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandlerTest.java
index a855c5e..8ef8e2f 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedEventHandlerTest.java
@@ -15,14 +15,14 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventDispatcher;
import com.google.gerrit.server.events.ProjectCreatedEvent;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,14 +76,18 @@
(Answer<Void>)
invocation -> {
assertThat(Context.isForwardedEvent()).isTrue();
- throw new OrmException("someMessage");
+ throw new PermissionBackendException("someMessage");
})
.when(dispatcherMock)
.postEvent(event);
assertThat(Context.isForwardedEvent()).isFalse();
- OrmException thrown = assertThrows(OrmException.class, () -> handler.dispatch(event));
- assertThat(thrown).hasMessageThat().contains("someMessage");
+ try {
+ handler.dispatch(event);
+ fail("should have throw a PermissionBackendException");
+ } catch (PermissionBackendException e) {
+ assertThat(e.getMessage()).isEqualTo("someMessage");
+ }
assertThat(Context.isForwardedEvent()).isFalse();
verify(dispatcherMock).postEvent(event);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandlerTest.java
index b88e9c0..14f3d54 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexAccountHandlerTest.java
@@ -19,9 +19,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.account.AccountIndexer;
@@ -38,16 +36,12 @@
public class ForwardedIndexAccountHandlerTest {
@Mock private AccountIndexer indexerMock;
- @Mock private Configuration configMock;
- @Mock private Configuration.Index indexMock;
private ForwardedIndexAccountHandler handler;
private Account.Id id;
@Before
public void setUp() throws Exception {
- when(configMock.index()).thenReturn(indexMock);
- when(indexMock.numStripedLocks()).thenReturn(10);
- handler = new ForwardedIndexAccountHandler(indexerMock, configMock);
+ handler = new ForwardedIndexAccountHandler(indexerMock);
id = new Account.Id(123);
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
index 6bfd47d..a6da8e2 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
@@ -15,11 +15,9 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,14 +26,11 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
import com.ericsson.gerrit.plugins.highavailability.index.ChangeChecker;
import com.ericsson.gerrit.plugins.highavailability.index.ChangeCheckerImpl;
-import com.ericsson.gerrit.plugins.highavailability.index.ChangeDb;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
@@ -54,16 +49,10 @@
private static String TEST_CHANGE_ID = TEST_PROJECT + "~" + TEST_CHANGE_NUMBER;
private static final boolean CHANGE_EXISTS = true;
private static final boolean CHANGE_DOES_NOT_EXIST = false;
- private static final boolean DO_NOT_THROW_IO_EXCEPTION = false;
- private static final boolean DO_NOT_THROW_ORM_EXCEPTION = false;
- private static final boolean THROW_IO_EXCEPTION = true;
- private static final boolean THROW_ORM_EXCEPTION = true;
private static final boolean CHANGE_UP_TO_DATE = true;
private static final boolean CHANGE_OUTDATED = false;
@Mock private ChangeIndexer indexerMock;
- @Mock private ChangeDb changeDbMock;
- @Mock private ReviewDb dbMock;
@Mock private ChangeNotes changeNotes;
@Mock private Configuration configMock;
@Mock private Configuration.Index indexMock;
@@ -77,35 +66,28 @@
@Before
public void setUp() throws Exception {
- when(changeDbMock.open()).thenReturn(dbMock);
id = new Change.Id(TEST_CHANGE_NUMBER);
Change change = new Change(null, id, null, null, TimeUtil.nowTs());
when(changeNotes.getChange()).thenReturn(change);
when(configMock.index()).thenReturn(indexMock);
- when(indexMock.numStripedLocks()).thenReturn(10);
when(changeCheckerFactoryMock.create(any())).thenReturn(changeCheckerAbsentMock);
handler =
new ForwardedIndexChangeHandler(
- indexerMock,
- changeDbMock,
- configMock,
- indexExecutorMock,
- ctxMock,
- changeCheckerFactoryMock);
+ indexerMock, configMock, indexExecutorMock, ctxMock, changeCheckerFactoryMock);
}
@Test
public void changeIsIndexedWhenUpToDate() throws Exception {
setupChangeAccessRelatedMocks(CHANGE_EXISTS, CHANGE_UP_TO_DATE);
handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.empty());
- verify(indexerMock, times(1)).index(any(ReviewDb.class), any(Change.class));
+ verify(indexerMock, times(1)).index(any(Change.class));
}
@Test
public void changeIsStillIndexedEvenWhenOutdated() throws Exception {
setupChangeAccessRelatedMocks(CHANGE_EXISTS, CHANGE_OUTDATED);
handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.of(new IndexEvent()));
- verify(indexerMock, times(1)).index(any(ReviewDb.class), any(Change.class));
+ verify(indexerMock, times(1)).index(any(Change.class));
}
@Test
@@ -122,21 +104,6 @@
}
@Test
- public void schemaThrowsExceptionWhenLookingUpForChange() throws Exception {
- setupChangeAccessRelatedMocks(CHANGE_EXISTS, THROW_ORM_EXCEPTION, CHANGE_UP_TO_DATE);
- assertThrows(
- OrmException.class, () -> handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.empty()));
- }
-
- @Test
- public void indexerThrowsIOExceptionTryingToIndexChange() throws Exception {
- setupChangeAccessRelatedMocks(
- CHANGE_EXISTS, DO_NOT_THROW_ORM_EXCEPTION, THROW_IO_EXCEPTION, CHANGE_UP_TO_DATE);
- assertThrows(
- IOException.class, () -> handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.empty()));
- }
-
- @Test
public void shouldSetAndUnsetForwardedContext() throws Exception {
setupChangeAccessRelatedMocks(CHANGE_EXISTS, CHANGE_UP_TO_DATE);
// this doAnswer is to allow to assert that context is set to forwarded
@@ -148,13 +115,13 @@
return null;
})
.when(indexerMock)
- .index(any(ReviewDb.class), any(Change.class));
+ .index(any(Change.class));
assertThat(Context.isForwardedEvent()).isFalse();
handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.empty());
assertThat(Context.isForwardedEvent()).isFalse();
- verify(indexerMock, times(1)).index(any(ReviewDb.class), any(Change.class));
+ verify(indexerMock, times(1)).index(any(Change.class));
}
@Test
@@ -167,7 +134,7 @@
throw new IOException("someMessage");
})
.when(indexerMock)
- .index(any(ReviewDb.class), any(Change.class));
+ .index(any(Change.class));
assertThat(Context.isForwardedEvent()).isFalse();
try {
@@ -178,39 +145,14 @@
}
assertThat(Context.isForwardedEvent()).isFalse();
- verify(indexerMock, times(1)).index(any(ReviewDb.class), any(Change.class));
+ verify(indexerMock, times(1)).index(any(Change.class));
}
- private void setupChangeAccessRelatedMocks(boolean changeExist, boolean changeUpToDate)
- throws Exception {
- setupChangeAccessRelatedMocks(
- changeExist, DO_NOT_THROW_ORM_EXCEPTION, DO_NOT_THROW_IO_EXCEPTION, changeUpToDate);
- }
-
- private void setupChangeAccessRelatedMocks(
- boolean changeExist, boolean ormException, boolean changeUpToDate)
- throws OrmException, IOException {
- setupChangeAccessRelatedMocks(
- changeExist, ormException, DO_NOT_THROW_IO_EXCEPTION, changeUpToDate);
- }
-
- private void setupChangeAccessRelatedMocks(
- boolean changeExists, boolean ormException, boolean ioException, boolean changeIsUpToDate)
- throws OrmException, IOException {
- if (ormException) {
- doThrow(new OrmException("")).when(changeDbMock).open();
- } else {
- when(changeDbMock.open()).thenReturn(dbMock);
- }
-
+ private void setupChangeAccessRelatedMocks(boolean changeExists, boolean changeIsUpToDate)
+ throws IOException {
if (changeExists) {
when(changeCheckerFactoryMock.create(TEST_CHANGE_ID)).thenReturn(changeCheckerPresentMock);
when(changeCheckerPresentMock.getChangeNotes()).thenReturn(Optional.of(changeNotes));
- if (ioException) {
- doThrow(new IOException("io-error"))
- .when(indexerMock)
- .index(any(ReviewDb.class), any(Change.class));
- }
}
when(changeCheckerPresentMock.isChangeUpToDate(any())).thenReturn(changeIsUpToDate);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandlerTest.java
index 845abb0..7e30dea 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexGroupHandlerTest.java
@@ -19,9 +19,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.index.group.GroupIndexer;
@@ -38,16 +36,12 @@
public class ForwardedIndexGroupHandlerTest {
@Mock private GroupIndexer indexerMock;
- @Mock private Configuration configMock;
- @Mock private Configuration.Index indexMock;
private ForwardedIndexGroupHandler handler;
private AccountGroup.UUID uuid;
@Before
public void setUp() throws Exception {
- when(configMock.index()).thenReturn(indexMock);
- when(indexMock.numStripedLocks()).thenReturn(10);
- handler = new ForwardedIndexGroupHandler(indexerMock, configMock);
+ handler = new ForwardedIndexGroupHandler(indexerMock);
uuid = new AccountGroup.UUID("123");
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandlerTest.java
index a0c6979..ea36532 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexProjectHandlerTest.java
@@ -19,9 +19,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
@@ -38,16 +36,12 @@
public class ForwardedIndexProjectHandlerTest {
@Mock private ProjectIndexer indexerMock;
- @Mock private Configuration configMock;
- @Mock private Configuration.Index indexMock;
private ForwardedIndexProjectHandler handler;
private Project.NameKey nameKey;
@Before
public void setUp() {
- when(configMock.index()).thenReturn(indexMock);
- when(indexMock.numStripedLocks()).thenReturn(10);
- handler = new ForwardedIndexProjectHandler(indexerMock, configMock);
+ handler = new ForwardedIndexProjectHandler(indexerMock);
nameKey = new Project.NameKey("project/name");
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
index 835977f..7872785 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
@@ -16,7 +16,6 @@
import static com.google.common.net.MediaType.JSON_UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
import static org.mockito.ArgumentMatchers.any;
@@ -29,7 +28,7 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.events.EventTypes;
import com.google.gerrit.server.events.RefEvent;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -83,11 +82,11 @@
+ "\"refs/changes/76/669676/2\",\"nodesCount\":1,\"type\":"
+ "\"ref-replication-done\",\"eventCreatedOn\":1451415011}";
when(requestMock.getReader()).thenReturn(new BufferedReader(new StringReader(event)));
- doThrow(new OrmException(ERR_MSG))
+ doThrow(new PermissionBackendException(ERR_MSG))
.when(forwardedEventHandlerMock)
.dispatch(any(RefReplicationDoneEvent.class));
eventRestApiServlet.doPost(requestMock, responseMock);
- verify(responseMock).sendError(SC_NOT_FOUND, "Change not found\n");
+ verify(responseMock).sendError(SC_BAD_REQUEST, ERR_MSG);
}
@Test
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
index 5e0d4c9..4071363 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
@@ -28,6 +28,7 @@
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.github.tomakehurst.wiremock.stubbing.Scenario;
+import com.google.gerrit.reviewdb.client.Project;
import java.net.SocketTimeoutException;
import org.junit.Before;
import org.junit.Rule;
@@ -70,7 +71,7 @@
when(configMock.http().socketTimeout()).thenReturn(TIMEOUT);
when(configMock.http().retryInterval()).thenReturn(RETRY_INTERVAL);
- httpSession = new HttpSession(new HttpClientProvider(configMock).get());
+ httpSession = new HttpSession(new HttpClientProvider(configMock).get(), new GsonProvider());
}
@Test
@@ -170,4 +171,12 @@
assertThat(httpSession.post(uri).isSuccessful()).isFalse();
}
+
+ @Test
+ public void encodeProjectName() {
+ String projectStr = "project";
+ Project.NameKey project = Project.nameKey(projectStr);
+ String json = httpSession.jsonEncode(project);
+ assertThat(json).isEqualTo("\"" + projectStr + "\"");
+ }
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
index c1fa765..fb3649e 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
@@ -15,7 +15,6 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
-import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -26,7 +25,6 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexChangeHandler;
import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -81,15 +79,6 @@
}
@Test
- public void indexerThrowsOrmExceptionTryingToIndexChange() throws Exception {
- doThrow(new OrmException("some message"))
- .when(handlerMock)
- .index(eq(CHANGE_ID), eq(Operation.INDEX), any());
- servlet.doPost(requestMock, responseMock);
- verify(responseMock).sendError(SC_NOT_FOUND, "Error trying to find change");
- }
-
- @Test
public void sendErrorThrowsIOException() throws Exception {
doThrow(new IOException(IO_ERROR))
.when(handlerMock)
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
index 5bd0cf8..cb90461 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
@@ -37,6 +37,8 @@
import com.google.inject.Provider;
import java.io.IOException;
import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import org.junit.Before;
import org.junit.Test;
@@ -91,17 +93,22 @@
private static final String EVENT_ENDPOINT =
Joiner.on("/").join(URL, PLUGINS, PLUGIN_NAME, "event", event.type);
+ private static final long TEST_TIMEOUT = 10;
+ private static final TimeUnit TEST_TIMEOUT_UNITS = TimeUnit.SECONDS;
+
private RestForwarder forwarder;
private HttpSession httpSessionMock;
+ private Configuration configMock;
+ Provider<Set<PeerInfo>> peersMock;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
httpSessionMock = mock(HttpSession.class);
- Configuration configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
+ configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
when(configMock.http().maxTries()).thenReturn(3);
when(configMock.http().retryInterval()).thenReturn(10);
- Provider<Set<PeerInfo>> peersMock = mock(Provider.class);
+ peersMock = mock(Provider.class);
when(peersMock.get()).thenReturn(ImmutableSet.of(new PeerInfo(URL)));
forwarder =
new RestForwarder(
@@ -109,126 +116,169 @@
PLUGIN_NAME,
configMock,
peersMock,
- gsonProvider); // TODO: Create provider
+ gsonProvider, // TODO: Create provider
+ new RestForwarderScheduler(Executors.newScheduledThreadPool(1)));
}
@Test
public void testIndexAccountOK() throws Exception {
when(httpSessionMock.post(eq(INDEX_ACCOUNT_ENDPOINT), any()))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.indexAccount(ACCOUNT_NUMBER, new IndexEvent())).isTrue();
+ assertThat(
+ forwarder
+ .indexAccount(ACCOUNT_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
public void testIndexAccountFailed() throws Exception {
when(httpSessionMock.post(eq(INDEX_ACCOUNT_ENDPOINT), any()))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.indexAccount(ACCOUNT_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .indexAccount(ACCOUNT_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexAccountThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).post(eq(INDEX_ACCOUNT_ENDPOINT), any());
- assertThat(forwarder.indexAccount(ACCOUNT_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .indexAccount(ACCOUNT_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexGroupOK() throws Exception {
when(httpSessionMock.post(eq(INDEX_GROUP_ENDPOINT), any()))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.indexGroup(UUID, new IndexEvent())).isTrue();
+ assertThat(forwarder.indexGroup(UUID, new IndexEvent()).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
public void testIndexGroupFailed() throws Exception {
when(httpSessionMock.post(eq(INDEX_GROUP_ENDPOINT), any()))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.indexGroup(UUID, new IndexEvent())).isFalse();
+ assertThat(forwarder.indexGroup(UUID, new IndexEvent()).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexGroupThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).post(eq(INDEX_GROUP_ENDPOINT), any());
- assertThat(forwarder.indexGroup(UUID, new IndexEvent())).isFalse();
+ assertThat(forwarder.indexGroup(UUID, new IndexEvent()).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexChangeOK() throws Exception {
when(httpSessionMock.post(eq(INDEX_CHANGE_ENDPOINT), any()))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isTrue();
+ assertThat(
+ forwarder
+ .indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
public void testIndexChangeFailed() throws Exception {
when(httpSessionMock.post(eq(INDEX_CHANGE_ENDPOINT), any()))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexChangeThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).post(eq(INDEX_CHANGE_ENDPOINT), any());
- assertThat(forwarder.indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .indexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testIndexBatchChangeOK() throws Exception {
when(httpSessionMock.post(eq(INDEX_BATCH_CHANGE_ENDPOINT), any()))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isTrue();
+ assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent()).get())
+ .isTrue();
}
@Test
public void testIndexBatchChangeFailed() throws Exception {
when(httpSessionMock.post(eq(INDEX_BATCH_CHANGE_ENDPOINT), any()))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent()).get())
+ .isFalse();
}
@Test
public void testIndexBatchChangeThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).post(eq(INDEX_BATCH_CHANGE_ENDPOINT), any());
- assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(forwarder.batchIndexChange(PROJECT_NAME, CHANGE_NUMBER, new IndexEvent()).get())
+ .isFalse();
}
@Test
public void testChangeDeletedFromIndexOK() throws Exception {
when(httpSessionMock.delete(eq(DELETE_CHANGE_ENDPOINT)))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())).isTrue();
+ assertThat(
+ forwarder
+ .deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
public void testChangeDeletedFromIndexFailed() throws Exception {
when(httpSessionMock.delete(eq(DELETE_CHANGE_ENDPOINT)))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testChangeDeletedFromThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).delete(eq(DELETE_CHANGE_ENDPOINT));
- assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())).isFalse();
+ assertThat(
+ forwarder
+ .deleteChangeFromIndex(CHANGE_NUMBER, new IndexEvent())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
public void testEventSentOK() throws Exception {
when(httpSessionMock.post(EVENT_ENDPOINT, event))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.send(event)).isTrue();
+ assertThat(forwarder.send(event).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS)).isTrue();
}
@Test
public void testEventSentFailed() throws Exception {
when(httpSessionMock.post(EVENT_ENDPOINT, event)).thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.send(event)).isFalse();
+ assertThat(forwarder.send(event).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS)).isFalse();
}
@Test
public void testEventSentThrowsException() throws Exception {
doThrow(new IOException()).when(httpSessionMock).post(EVENT_ENDPOINT, event);
- assertThat(forwarder.send(event)).isFalse();
+ assertThat(forwarder.send(event).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS)).isFalse();
}
@Test
@@ -237,7 +287,8 @@
String keyJson = gson.toJson(key);
when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.PROJECTS, key)).isTrue();
+ assertThat(forwarder.evict(Constants.PROJECTS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -246,7 +297,8 @@
String keyJson = gson.toJson(key);
when(httpSessionMock.post(buildCacheEndpoint(Constants.ACCOUNTS), keyJson))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.ACCOUNTS, key)).isTrue();
+ assertThat(forwarder.evict(Constants.ACCOUNTS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -255,7 +307,8 @@
String keyJson = gson.toJson(key);
String endpoint = buildCacheEndpoint(Constants.GROUPS);
when(httpSessionMock.post(endpoint, keyJson)).thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.GROUPS, key)).isTrue();
+ assertThat(forwarder.evict(Constants.GROUPS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -264,7 +317,9 @@
String keyJson = gson.toJson(key);
when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_BYINCLUDE), keyJson))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.GROUPS_BYINCLUDE, key)).isTrue();
+ assertThat(
+ forwarder.evict(Constants.GROUPS_BYINCLUDE, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -273,7 +328,8 @@
String keyJson = gson.toJson(key);
when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_MEMBERS), keyJson))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.GROUPS_MEMBERS, key)).isTrue();
+ assertThat(forwarder.evict(Constants.GROUPS_MEMBERS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -282,7 +338,8 @@
String keyJson = gson.toJson(key);
when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.evict(Constants.PROJECTS, key)).isFalse();
+ assertThat(forwarder.evict(Constants.PROJECTS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
@@ -292,7 +349,8 @@
doThrow(new IOException())
.when(httpSessionMock)
.post(buildCacheEndpoint(Constants.PROJECTS), keyJson);
- assertThat(forwarder.evict(Constants.PROJECTS, key)).isFalse();
+ assertThat(forwarder.evict(Constants.PROJECTS, key).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
private static String buildCacheEndpoint(String name) {
@@ -304,7 +362,8 @@
String projectName = PROJECT_TO_ADD;
when(httpSessionMock.post(buildProjectListCacheEndpoint(projectName), null))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.addToProjectList(projectName)).isTrue();
+ assertThat(forwarder.addToProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -312,7 +371,8 @@
String projectName = PROJECT_TO_ADD;
when(httpSessionMock.post(buildProjectListCacheEndpoint(projectName), null))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.addToProjectList(projectName)).isFalse();
+ assertThat(forwarder.addToProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
@@ -321,7 +381,8 @@
doThrow(new IOException())
.when(httpSessionMock)
.post(buildProjectListCacheEndpoint(projectName), null);
- assertThat(forwarder.addToProjectList(projectName)).isFalse();
+ assertThat(forwarder.addToProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
@@ -329,7 +390,8 @@
String projectName = PROJECT_TO_DELETE;
when(httpSessionMock.delete(buildProjectListCacheEndpoint(projectName)))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
- assertThat(forwarder.removeFromProjectList(projectName)).isTrue();
+ assertThat(forwarder.removeFromProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
@@ -337,7 +399,8 @@
String projectName = PROJECT_TO_DELETE;
when(httpSessionMock.delete(buildProjectListCacheEndpoint(projectName)))
.thenReturn(new HttpResult(FAILED, EMPTY_MSG));
- assertThat(forwarder.removeFromProjectList(projectName)).isFalse();
+ assertThat(forwarder.removeFromProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
@@ -346,7 +409,8 @@
doThrow(new IOException())
.when(httpSessionMock)
.delete((buildProjectListCacheEndpoint(projectName)));
- assertThat(forwarder.removeFromProjectList(projectName)).isFalse();
+ assertThat(forwarder.removeFromProjectList(projectName).get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
private static String buildProjectListCacheEndpoint(String projectName) {
@@ -354,41 +418,57 @@
}
@Test
- public void testRetryOnErrorThenSuccess() throws IOException {
+ public void testRetryOnErrorThenSuccess() throws Exception {
when(httpSessionMock.post(anyString(), anyString()))
.thenReturn(new HttpResult(false, ERROR))
.thenReturn(new HttpResult(false, ERROR))
.thenReturn(new HttpResult(true, SUCCESS));
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isTrue();
+ assertThat(
+ forwarder
+ .evict(Constants.PROJECT_LIST, new Object())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
- public void testRetryOnIoExceptionThenSuccess() throws IOException {
+ public void testRetryOnIoExceptionThenSuccess() throws Exception {
when(httpSessionMock.post(anyString(), anyString()))
.thenThrow(new IOException())
.thenThrow(new IOException())
.thenReturn(new HttpResult(true, SUCCESS));
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isTrue();
+ assertThat(
+ forwarder
+ .evict(Constants.PROJECT_LIST, new Object())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isTrue();
}
@Test
- public void testNoRetryAfterNonRecoverableException() throws IOException {
+ public void testNoRetryAfterNonRecoverableException() throws Exception {
when(httpSessionMock.post(anyString(), anyString()))
.thenThrow(new SSLException("Non Recoverable"))
.thenReturn(new HttpResult(true, SUCCESS));
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isFalse();
+ assertThat(
+ forwarder
+ .evict(Constants.PROJECT_LIST, new Object())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
@Test
- public void testFailureAfterMaxTries() throws IOException {
+ public void testFailureAfterMaxTries() throws Exception {
when(httpSessionMock.post(anyString(), anyString()))
.thenReturn(new HttpResult(false, ERROR))
.thenReturn(new HttpResult(false, ERROR))
.thenReturn(new HttpResult(false, ERROR));
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isFalse();
+ assertThat(
+ forwarder
+ .evict(Constants.PROJECT_LIST, new Object())
+ .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+ .isFalse();
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/AccountIndexForwardingIT.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/AccountIndexForwardingIT.java
index 4b2462e..a66e896 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/AccountIndexForwardingIT.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/AccountIndexForwardingIT.java
@@ -26,11 +26,11 @@
@Override
public String getExpectedRequest() {
- return "/plugins/high-availability/index/account/" + testAccount.id;
+ return "/plugins/high-availability/index/account/" + testAccount.id();
}
@Override
public void doAction() throws Exception {
- gApi.accounts().id(testAccount.id.get()).setActive(false);
+ gApi.accounts().id(testAccount.id().get()).setActive(false);
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerNoteDbIT.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerIT.java
similarity index 90%
rename from src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerNoteDbIT.java
rename to src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerIT.java
index b691034..bc5210d 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerNoteDbIT.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerIT.java
@@ -21,28 +21,20 @@
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.Optional;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.junit.BeforeClass;
import org.junit.Test;
@TestPlugin(
name = "high-availability",
sysModule = "com.ericsson.gerrit.plugins.highavailability.Module",
httpModule = "com.ericsson.gerrit.plugins.highavailability.HttpModule")
-public class ChangeCheckerNoteDbIT extends LightweightPluginDaemonTest {
+public class ChangeCheckerIT extends LightweightPluginDaemonTest {
ChangeCheckerImpl.Factory changeCheckerFactory;
- @BeforeClass
- public static void setupTestSuite() {
- System.setProperty("gerrit.notedb", NoteDbMode.ON.name());
- }
-
@Override
public void setUpTestPlugin() throws Exception {
super.setUpTestPlugin();
@@ -50,7 +42,7 @@
}
@Test
- public void shouldPopulateMetaShaWhenNoteDb() throws Exception {
+ public void shouldPopulateMetaSha() throws Exception {
Result change = createChange();
ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
Optional<IndexEvent> eventOption = changeChecker.newIndexEvent();
@@ -121,7 +113,7 @@
assertThat(changeChecker.isChangeUpToDate(event)).isFalse();
}
- private String readMetaSha(Result change) throws IOException, OrmException {
+ private String readMetaSha(Result change) throws IOException {
try (Repository repo = repoManager.openRepository(change.getChange().change().getProject())) {
String refName = RefNames.changeMetaRef(change.getChange().getId());
Ref ref = repo.exactRef(refName);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerReviewDbIT.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerReviewDbIT.java
deleted file mode 100644
index a6cb449..0000000
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerReviewDbIT.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2021 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.ericsson.gerrit.plugins.highavailability.index;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
-import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
-import com.google.gerrit.acceptance.PushOneCommit.Result;
-import com.google.gerrit.acceptance.TestPlugin;
-import com.google.gerrit.testing.NoteDbMode;
-import java.util.Optional;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-@TestPlugin(
- name = "high-availability",
- sysModule = "com.ericsson.gerrit.plugins.highavailability.Module",
- httpModule = "com.ericsson.gerrit.plugins.highavailability.HttpModule")
-public class ChangeCheckerReviewDbIT extends LightweightPluginDaemonTest {
-
- ChangeCheckerImpl.Factory changeCheckerFactory;
-
- @BeforeClass
- public static void setupTestSuite() {
- System.setProperty("gerrit.notedb", NoteDbMode.OFF.name());
- }
-
- @Override
- public void setUpTestPlugin() throws Exception {
- super.setUpTestPlugin();
- changeCheckerFactory = plugin.getSysInjector().getInstance(ChangeCheckerImpl.Factory.class);
- }
-
- @Test
- public void shouldNotPopulateMetaShaWhenReviewDb() throws Exception {
- Result change = createChange();
- ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
- Optional<IndexEvent> eventOption = changeChecker.newIndexEvent();
-
- assertThat(eventOption.isPresent()).isTrue();
- IndexEvent event = eventOption.get();
- assertThat(event.metaSha).isNull();
- }
-
- @Test
- public void shouldReturnIsUpToDateTrueWhenEventContainsCorrectMetaAndTargetSha()
- throws Exception {
- Result change = createChange();
- ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
- Optional<IndexEvent> event = changeChecker.newIndexEvent();
-
- assertThat(changeChecker.isChangeUpToDate(event)).isTrue();
- }
-
- @Test
- public void shouldReturnIsUpToDateTrueWhenMetaShaIsNull() throws Exception {
- Result change = createChange();
- ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
- Optional<IndexEvent> event =
- changeChecker
- .newIndexEvent()
- .map(
- e -> {
- e.metaSha = null;
- return e;
- });
-
- assertThat(changeChecker.isChangeUpToDate(event)).isTrue();
- }
-
- @Test
- public void shouldReturnIsUpToDateTrueWhenTargetShaIsNull() throws Exception {
- Result change = createChange();
- ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
- Optional<IndexEvent> event =
- changeChecker
- .newIndexEvent()
- .map(
- e -> {
- e.targetSha = null;
- return e;
- });
-
- assertThat(changeChecker.isChangeUpToDate(event)).isTrue();
- }
-
- @Test
- public void shouldReturnFalseWhenTargetShaIsNotUpToDate() throws Exception {
- String testTargetRefSha = "abed47baf2818a86b68cf712073a748a6b5b293e";
- Result change = createChange();
- ChangeChecker changeChecker = changeCheckerFactory.create(change.getChangeId());
- Optional<IndexEvent> event =
- changeChecker
- .newIndexEvent()
- .map(
- e -> {
- e.targetSha = testTargetRefSha;
- return e;
- });
-
- assertThat(changeChecker.isChangeUpToDate(event)).isFalse();
- }
-}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
index 4c20ae1..5f08cc5 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -32,20 +33,37 @@
import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexAccountTask;
import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexChangeTask;
import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexGroupTask;
-import com.google.common.util.concurrent.MoreExecutors;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexTask;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventLocks.VoidFunction;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import java.util.Collection;
+import java.util.List;
import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.function.Supplier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
@@ -56,6 +74,8 @@
private static final int ACCOUNT_ID = 2;
private static final String UUID = "3";
private static final String OTHER_UUID = "4";
+ private static final Integer INDEX_WAIT_TIMEOUT_MS = 5;
+ private static final int MAX_TEST_PARALLELISM = 4;
private IndexEventHandler indexEventHandler;
@Mock private Forwarder forwarder;
@@ -64,7 +84,13 @@
private Change.Id changeId;
private Account.Id accountId;
private AccountGroup.UUID accountGroupUUID;
+ private ScheduledExecutorService executor = new CurrentThreadScheduledExecutorService();
+ private ScheduledExecutorService batchExecutor = new CurrentThreadScheduledExecutorService();
+ private ScheduledExecutorService testExecutor =
+ Executors.newScheduledThreadPool(MAX_TEST_PARALLELISM);
@Mock private RequestContext mockCtx;
+ @Mock private Configuration configuration;
+ private IndexEventLocks idLocks;
private CurrentRequestContext currCtx =
new CurrentRequestContext(null, null, null) {
@@ -82,18 +108,48 @@
when(changeCheckerFactoryMock.create(any())).thenReturn(changeCheckerMock);
when(changeCheckerMock.newIndexEvent()).thenReturn(Optional.of(new IndexEvent()));
+ Configuration.Index cfgIndex = mock(Configuration.Index.class);
+ when(configuration.index()).thenReturn(cfgIndex);
+ when(cfgIndex.numStripedLocks()).thenReturn(Configuration.DEFAULT_NUM_STRIPED_LOCKS);
+
+ Configuration.Http http = mock(Configuration.Http.class);
+ when(configuration.http()).thenReturn(http);
+ when(http.maxTries()).thenReturn(Configuration.Http.DEFAULT_MAX_TRIES);
+ when(http.retryInterval()).thenReturn(Configuration.Http.DEFAULT_RETRY_INTERVAL);
+ when(forwarder.indexAccount(eq(ACCOUNT_ID), any()))
+ .thenReturn(CompletableFuture.completedFuture(true));
+ when(forwarder.deleteChangeFromIndex(eq(CHANGE_ID), any()))
+ .thenReturn(CompletableFuture.completedFuture(true));
+ when(forwarder.indexGroup(eq(UUID), any())).thenReturn(CompletableFuture.completedFuture(true));
+ when(forwarder.indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any()))
+ .thenReturn(CompletableFuture.completedFuture(true));
+
+ idLocks = new IndexEventLocks(configuration);
setUpIndexEventHandler(currCtx);
}
public void setUpIndexEventHandler(CurrentRequestContext currCtx) throws Exception {
+ setUpIndexEventHandler(currCtx, idLocks, configuration);
+ }
+
+ public void setUpIndexEventHandler(CurrentRequestContext currCtx, IndexEventLocks idLocks)
+ throws Exception {
+ setUpIndexEventHandler(currCtx, idLocks, configuration);
+ }
+
+ public void setUpIndexEventHandler(
+ CurrentRequestContext currCtx, IndexEventLocks idLocks, Configuration configuration)
+ throws Exception {
indexEventHandler =
new IndexEventHandler(
- MoreExecutors.directExecutor(),
- MoreExecutors.directExecutor(),
+ executor,
+ batchExecutor,
PLUGIN_NAME,
forwarder,
changeCheckerFactoryMock,
- currCtx);
+ currCtx,
+ configuration,
+ idLocks);
}
@Test
@@ -116,6 +172,160 @@
}
@Test
+ public void shouldNotIndexChangeWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+
+ verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
+ }
+
+ @Test
+ public void shouldNotIndexAccountWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onAccountIndexed(accountId.get());
+
+ verify(forwarder, never()).indexAccount(eq(ACCOUNT_ID), any());
+ }
+
+ @Test
+ public void shouldNotDeleteChangeWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onChangeDeleted(changeId.get());
+
+ verify(forwarder, never()).deleteChangeFromIndex(eq(CHANGE_ID), any());
+ }
+
+ @Test
+ public void shouldNotIndexGroupWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+
+ verify(forwarder, never()).indexGroup(eq(UUID), any());
+ }
+
+ @Test
+ public void shouldNotIndexProjectWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onProjectIndexed(PROJECT_NAME);
+
+ verify(forwarder, never()).indexProject(eq(PROJECT_NAME), any());
+ }
+
+ @Test
+ public void shouldRetryIndexChangeWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false, true);
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+
+ verify(locks, times(2)).withLock(any(), any(), any());
+ verify(forwarder, times(1)).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
+ }
+
+ @Test
+ public void shouldRetryUpToMaxTriesWhenCannotAcquireLock() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+
+ Configuration cfg = mock(Configuration.class);
+ Configuration.Http httpCfg = mock(Configuration.Http.class);
+ when(httpCfg.maxTries()).thenReturn(10);
+ when(cfg.http()).thenReturn(httpCfg);
+ setUpIndexEventHandler(currCtx, locks, cfg);
+
+ indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+
+ verify(locks, times(11)).withLock(any(), any(), any());
+ verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
+ }
+
+ @Test
+ public void shouldNotRetryWhenMaxTriesLowerThanOne() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore semaphore = mock(Semaphore.class);
+ when(locks.getSemaphore(anyString())).thenReturn(semaphore);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ when(semaphore.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+
+ Configuration cfg = mock(Configuration.class);
+ Configuration.Http httpCfg = mock(Configuration.Http.class);
+ when(httpCfg.maxTries()).thenReturn(0);
+ when(cfg.http()).thenReturn(httpCfg);
+ setUpIndexEventHandler(currCtx, locks, cfg);
+
+ indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+
+ verify(locks, times(1)).withLock(any(), any(), any());
+ verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
+ }
+
+ @Test
+ public void shouldLockPerIndexEventType() throws Exception {
+ IndexEventLocks locks = mock(IndexEventLocks.class);
+ Semaphore indexChangeLock = mock(Semaphore.class);
+ when(indexChangeLock.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(false);
+ Semaphore accountChangeLock = mock(Semaphore.class);
+ when(accountChangeLock.tryAcquire(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)))
+ .thenReturn(true);
+ when(locks.getSemaphore(eq("change/" + CHANGE_ID))).thenReturn(indexChangeLock);
+ when(locks.getSemaphore(eq("account/" + ACCOUNT_ID))).thenReturn(accountChangeLock);
+ Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any());
+ setUpIndexEventHandler(currCtx, locks);
+
+ indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+ indexEventHandler.onAccountIndexed(accountId.get());
+
+ verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
+ verify(forwarder).indexAccount(eq(ACCOUNT_ID), any());
+ }
+
+ @Test
public void shouldReindexInRemoteWhenContextIsMissingButForcedIndexingEnabled() throws Exception {
ThreadLocalRequestContext threadLocalCtxMock = mock(ThreadLocalRequestContext.class);
OneOffRequestContext oneOffCtxMock = mock(OneOffRequestContext.class);
@@ -182,7 +392,14 @@
ScheduledThreadPoolExecutor poolBatchMock = mock(ScheduledThreadPoolExecutor.class);
indexEventHandler =
new IndexEventHandler(
- poolMock, poolBatchMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx);
+ poolMock,
+ poolBatchMock,
+ PLUGIN_NAME,
+ forwarder,
+ changeCheckerFactoryMock,
+ currCtx,
+ configuration,
+ idLocks);
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
verify(poolMock, times(1))
@@ -195,7 +412,14 @@
ScheduledThreadPoolExecutor poolBatchMock = mock(ScheduledThreadPoolExecutor.class);
indexEventHandler =
new IndexEventHandler(
- poolMock, poolBatchMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx);
+ poolMock,
+ poolBatchMock,
+ PLUGIN_NAME,
+ forwarder,
+ changeCheckerFactoryMock,
+ currCtx,
+ configuration,
+ idLocks);
indexEventHandler.onAccountIndexed(accountId.get());
indexEventHandler.onAccountIndexed(accountId.get());
verify(poolMock, times(1)).execute(indexEventHandler.new IndexAccountTask(ACCOUNT_ID));
@@ -207,7 +431,14 @@
ScheduledThreadPoolExecutor poolBatchMock = mock(ScheduledThreadPoolExecutor.class);
indexEventHandler =
new IndexEventHandler(
- poolMock, poolBatchMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx);
+ poolMock,
+ poolBatchMock,
+ PLUGIN_NAME,
+ forwarder,
+ changeCheckerFactoryMock,
+ currCtx,
+ configuration,
+ idLocks);
indexEventHandler.onGroupIndexed(accountGroupUUID.get());
indexEventHandler.onGroupIndexed(accountGroupUUID.get());
verify(poolMock, times(1)).execute(indexEventHandler.new IndexGroupTask(UUID));
@@ -323,4 +554,226 @@
assertThat(task.equals(differentGroupIdTask)).isFalse();
assertThat(task.hashCode()).isNotEqualTo(differentGroupIdTask.hashCode());
}
+
+ class TestTask<T> implements Runnable {
+ private IndexTask task;
+ private CyclicBarrier testBarrier;
+ private Supplier<T> successFunc;
+ private VoidFunction failureFunc;
+ private CompletableFuture<T> future;
+
+ public TestTask(
+ IndexTask task,
+ CyclicBarrier testBarrier,
+ Supplier<T> successFunc,
+ VoidFunction failureFunc) {
+ this.task = task;
+ this.testBarrier = testBarrier;
+ this.successFunc = successFunc;
+ this.failureFunc = failureFunc;
+ this.future = new CompletableFuture<>();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ try {
+ testBarrier.await();
+ idLocks
+ .withLock(
+ task,
+ () ->
+ runLater(
+ INDEX_WAIT_TIMEOUT_MS * 2,
+ () -> CompletableFuture.completedFuture(successFunc.get())),
+ failureFunc)
+ .whenComplete(
+ (v, t) -> {
+ if (t == null) {
+ future.complete((T) v);
+ } else {
+ future.completeExceptionally(t);
+ }
+ });
+ } catch (Throwable t) {
+ future = new CompletableFuture<>();
+ future.completeExceptionally(t);
+ }
+ }
+
+ public void join() {
+ try {
+ future.join();
+ } catch (Exception e) {
+ }
+ }
+
+ private CompletableFuture<T> runLater(
+ long scheduledTimeMsec, Supplier<CompletableFuture<T>> supplier) {
+ CompletableFuture<T> resFuture = new CompletableFuture<>();
+ testExecutor.schedule(
+ () -> {
+ try {
+ return supplier
+ .get()
+ .whenComplete(
+ (v, t) -> {
+ if (t == null) {
+ resFuture.complete(v);
+ }
+ resFuture.completeExceptionally(t);
+ });
+ } catch (Throwable t) {
+ return resFuture.completeExceptionally(t);
+ }
+ },
+ scheduledTimeMsec,
+ TimeUnit.MILLISECONDS);
+ return resFuture;
+ }
+ }
+
+ @Test
+ public void indexLocksShouldBlockConcurrentIndexChange() throws Exception {
+ IndexChangeTask indexTask1 =
+ indexEventHandler.new IndexChangeTask(PROJECT_NAME, CHANGE_ID, new IndexEvent());
+ IndexChangeTask indexTask2 =
+ indexEventHandler.new IndexChangeTask(PROJECT_NAME, CHANGE_ID, new IndexEvent());
+ testIsolationOfCuncurrentIndexTasks(indexTask1, indexTask2);
+ }
+
+ @Test
+ public void indexLocksShouldBlockConcurrentIndexAndDeleteChange() throws Exception {
+ IndexChangeTask indexTask =
+ indexEventHandler.new IndexChangeTask(PROJECT_NAME, CHANGE_ID, new IndexEvent());
+ DeleteChangeTask deleteTask =
+ indexEventHandler.new DeleteChangeTask(CHANGE_ID, new IndexEvent());
+ testIsolationOfCuncurrentIndexTasks(indexTask, deleteTask);
+ }
+
+ private void testIsolationOfCuncurrentIndexTasks(IndexTask indexTask1, IndexTask indexTask2)
+ throws Exception {
+ AtomicInteger changeIndexedCount = new AtomicInteger();
+ AtomicInteger lockFailedCounts = new AtomicInteger();
+ CyclicBarrier changeThreadsSync = new CyclicBarrier(2);
+
+ TestTask<Integer> task1 =
+ new TestTask<>(
+ indexTask1,
+ changeThreadsSync,
+ () -> changeIndexedCount.incrementAndGet(),
+ () -> lockFailedCounts.incrementAndGet());
+ TestTask<Integer> task2 =
+ new TestTask<>(
+ indexTask2,
+ changeThreadsSync,
+ () -> changeIndexedCount.incrementAndGet(),
+ () -> lockFailedCounts.incrementAndGet());
+
+ new Thread(task1).start();
+ new Thread(task2).start();
+ task1.join();
+ task2.join();
+
+ /* Both assertions needs to be true, the order doesn't really matter:
+ * - Only one of the two tasks should succeed
+ * - Only one of the two tasks should fail to acquire the lock
+ */
+ assertThat(changeIndexedCount.get()).isEqualTo(1);
+ assertThat(lockFailedCounts.get()).isEqualTo(1);
+ }
+
+ private class CurrentThreadScheduledExecutorService implements ScheduledExecutorService {
+
+ @Override
+ public void shutdown() {}
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return null;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return false;
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return null;
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return null;
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return null;
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return null;
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return null;
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return null;
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return null;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ command.run();
+ return null;
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return null;
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return null;
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return null;
+ }
+ }
}
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.json
index 281515d..43ee6ec 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT1/a/config/server/caches/projects",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/config/server/caches/projects",
"entries": "PROJECTS_ENTRIES"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.json
index 3fd506d..f128e73 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT2/_PROJECT",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT2/_PROJECT",
"cmd": "clone"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.json
index b535c1d..a167922 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT1/a/changes/",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/changes/",
"project": "_PROJECT"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.json
index da3f028..2e5073b 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT1/a/projects/PROJECT"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/projects/PROJECT"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.json
index 8e0a304..4ecbe68 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT2/a/changes/",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT2/a/changes/",
"number": "NUMBER"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.json
index be47699..2405b03 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME/a/projects/PROJECT/delete-project~delete"
+ "url": "HTTP_SCHEME://HOSTNAME/a/projects/PROJECT/delete-project~delete"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.json
index c938973..ae904f5 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT2/a/config/server/caches/projects/flush"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT2/a/config/server/caches/projects/flush"
}
]
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.json
index f7450a4..25521c7 100644
--- a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.json
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME/a/config/server/caches/projects"
+ "url": "HTTP_SCHEME://HOSTNAME/a/config/server/caches/projects"
}
]
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.scala
index 10374f8..6a0d07f 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CheckProjectsCacheFlushEntriesUsingHAGerrit1.scala
@@ -32,7 +32,7 @@
this.producer = Some(producer)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
if (producer.nonEmpty) {
@@ -41,12 +41,12 @@
session
}
})
- .exec(http(unique).get("${url}")
+ .exec(http(uniqueName).get("${url}")
.check(regex("\"" + memKey + "\": (\\d+)")
.is(session => session(entriesKey).as[String])))
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.scala
index 13a741a..4fa0ca5 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CloneUsingHAGerrit2.scala
@@ -23,37 +23,37 @@
class CloneUsingHAGerrit2 extends GitSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private var default: String = name
+ private var projectName = className
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
override def replaceOverride(in: String): String = {
val next = replaceProperty("http_port2", 8082, in)
- replaceKeyWith("_project", default, next)
+ replaceKeyWith("_project", projectName, next)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(gitRequest)
- private val createProject = new CreateProjectUsingHAGerrit1(default)
- private val deleteProject = new DeleteProjectUsingHAGerrit(default)
+ private val createProject = new CreateProjectUsingHAGerrit1(projectName)
+ private val deleteProject = new DeleteProjectUsingHAGerrit(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
- ),
+ atOnceUsers(single)
+ ).protocols(gitProtocol),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
- ).protocols(gitProtocol, httpProtocol)
+ ).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.scala
index f07aabd..26732b6 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateChangeUsingHAGerrit1.scala
@@ -24,7 +24,7 @@
class CreateChangeUsingHAGerrit1 extends GerritSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private val default: String = name
+ private val projectName = className
private val numberKey = "_number"
override def relativeRuntimeWeight = 10
@@ -33,7 +33,7 @@
replaceProperty("http_port1", 8081, in)
}
- private val test: ScenarioBuilder = scenario(unique)
+ private val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest
.body(ElFileBody(body)).asJson
@@ -43,26 +43,26 @@
session
})
- private val createProject = new CreateProjectUsingHAGerrit1(default)
- private val deleteProject = new DeleteProjectUsingHAGerrit(default)
+ private val createProject = new CreateProjectUsingHAGerrit1(projectName)
+ private val deleteProject = new DeleteProjectUsingHAGerrit(projectName)
private val deleteChange = new DeleteChangeUsingHAGerrit2
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteChange.test.inject(
nothingFor(stepWaitTime(deleteChange) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.scala
index 964aadb..5773bcf 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerrit1.scala
@@ -22,9 +22,9 @@
class CreateProjectUsingHAGerrit1 extends ProjectSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
override def replaceOverride(in: String): String = {
@@ -32,12 +32,12 @@
super.replaceOverride(next)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest.body(RawFileBody(body)).asJson)
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerritTwice.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerritTwice.scala
index a4da93c..fd83e7a 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerritTwice.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/CreateProjectUsingHAGerritTwice.scala
@@ -20,34 +20,34 @@
import scala.concurrent.duration._
class CreateProjectUsingHAGerritTwice extends GitSimulation {
- private val default: String = name
+ private val projectName = className
- private val createProject = new CreateProjectUsingHAGerrit1(default)
- private val deleteProject = new DeleteProjectUsingHAGerrit(default)
- private val createItAgain = new CreateProjectUsingHAGerrit1(default)
- private val verifyProject = new CloneUsingHAGerrit2(default)
- private val deleteItAfter = new DeleteProjectUsingHAGerrit(default)
+ private val createProject = new CreateProjectUsingHAGerrit1(projectName)
+ private val deleteProject = new DeleteProjectUsingHAGerrit(projectName)
+ private val createItAgain = new CreateProjectUsingHAGerrit1(projectName)
+ private val verifyProject = new CloneUsingHAGerrit2(projectName)
+ private val deleteItAfter = new DeleteProjectUsingHAGerrit(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
createItAgain.test.inject(
nothingFor(stepWaitTime(createItAgain) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
verifyProject.test.inject(
nothingFor(stepWaitTime(verifyProject) seconds),
- atOnceUsers(1)
- ),
+ atOnceUsers(single)
+ ).protocols(gitProtocol),
deleteItAfter.test.inject(
nothingFor(stepWaitTime(deleteItAfter) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
- ).protocols(gitProtocol, httpProtocol)
+ ).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.scala
index 5b0f461..d3dc7c1 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteChangeUsingHAGerrit2.scala
@@ -30,7 +30,7 @@
replaceProperty("http_port2", 8082, in)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
if (number.nonEmpty) {
@@ -39,10 +39,10 @@
session
}
})
- .exec(http(unique).delete("${url}${number}"))
+ .exec(http(uniqueName).delete("${url}${number}"))
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.scala
index 428085d..cdc6e9e 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/DeleteProjectUsingHAGerrit.scala
@@ -22,17 +22,17 @@
class DeleteProjectUsingHAGerrit extends ProjectSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest)
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.scala
index 9c317d4..4f619b8 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/FlushProjectsCacheUsingHAGerrit2.scala
@@ -23,7 +23,7 @@
class FlushProjectsCacheUsingHAGerrit2 extends CacheFlushSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private val default: String = name
+ private val projectName = className
override def relativeRuntimeWeight = 2
@@ -31,35 +31,35 @@
replaceProperty("http_port2", 8082, in)
}
- private val flushCache: ScenarioBuilder = scenario(unique)
+ private val flushCache: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest)
- private val createProject = new CreateProjectUsingHAGerrit1(default)
+ private val createProject = new CreateProjectUsingHAGerrit1(projectName)
private val getCacheEntriesAfterProject = new GetProjectsCacheEntries(this)
private val checkCacheEntriesAfterFlush = new CheckProjectsCacheFlushEntriesUsingHAGerrit1(this)
- private val deleteProject = new DeleteProjectUsingHAGerrit(default)
+ private val deleteProject = new DeleteProjectUsingHAGerrit(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
getCacheEntriesAfterProject.test.inject(
nothingFor(stepWaitTime(getCacheEntriesAfterProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
flushCache.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
checkCacheEntriesAfterFlush.test.inject(
nothingFor(stepWaitTime(checkCacheEntriesAfterFlush) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
).protocols(httpProtocol)
}
diff --git a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.scala b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.scala
index 9565365..ed19c0b 100644
--- a/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.scala
+++ b/src/test/scala/com/ericsson/gerrit/plugins/highavailability/scenarios/GetProjectsCacheEntries.scala
@@ -28,9 +28,9 @@
this.consumer = Some(consumer)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
- .exec(http(unique).get("${url}")
+ .exec(http(uniqueName).get("${url}")
.check(regex("\"" + memKey + "\": (\\d+)").saveAs(entriesKey)))
.exec(session => {
if (consumer.nonEmpty) {
@@ -41,6 +41,6 @@
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}