Merge branch 'stable-2.16' into stable-3.0 * stable-2.16: CachePatternMatcher: Adapt the code to removal of account caches Upgrade bazlets to latest stable-2.16 to build with 2.16.23 API Since [1] the web_sessions were removed from defaults patterns. [1] 51154b0b184a032dabb082f75dfeffef72095dd5 Change-Id: I4ea82c3864e597fe8ec44f9a0ddca6c4e9a878cb
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 654cd40..6fd8a64 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -3,26 +3,15 @@ load("//:bazlets.bzl", "load_bazlets") load_bazlets( - commit = "7be854b98543c6429dd5038ff38846a1230b4dbe", + commit = "a010a539824bd467059f414412a47b80101352d7", #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 58a21b6..3bd58a3 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"; @@ -54,7 +57,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; @@ -333,6 +335,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"; @@ -341,10 +346,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; @@ -446,6 +447,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; @@ -453,6 +455,7 @@ private final int threadPoolSize; private final int retryInterval; private final int maxTries; + private final int waitTimeout; private final int numStripedLocks; private final boolean synchronizeForced; @@ -462,6 +465,7 @@ numStripedLocks = getInt(cfg, INDEX_SECTION, NUM_STRIPED_LOCKS, DEFAULT_NUM_STRIPED_LOCKS); retryInterval = getInt(cfg, INDEX_SECTION, RETRY_INTERVAL_KEY, DEFAULT_INDEX_RETRY_INTERVAL); maxTries = getInt(cfg, INDEX_SECTION, MAX_TRIES_KEY, DEFAULT_INDEX_MAX_TRIES); + waitTimeout = getInt(cfg, INDEX_SECTION, WAIT_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS); synchronizeForced = cfg.getBoolean(INDEX_SECTION, SYNCHRONIZE_FORCED_KEY, DEFAULT_SYNCHRONIZE_FORCED); } @@ -482,6 +486,10 @@ return maxTries; } + public int waitTimeout() { + return waitTimeout; + } + public boolean synchronizeForced() { return synchronizeForced; }
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 0e0602a..6255986 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; @@ -51,7 +49,6 @@ private final Path dataDir; private final ScheduledExecutorService exec; private final FlusherRunner flusher; - private final SchemaFactory<ReviewDb> schemaFactory; private final ChangeFinder changeFinder; private final CurrentRequestContext currCtx; @@ -87,13 +84,11 @@ public IndexTs( @PluginData Path dataDir, WorkQueue queue, - SchemaFactory<ReviewDb> schemaFactory, ChangeFinder changeFinder, CurrentRequestContext currCtx) { this.dataDir = dataDir; this.exec = queue.getDefaultQueue(); this.flusher = new FlusherRunner(); - this.schemaFactory = schemaFactory; this.changeFinder = changeFinder; this.currCtx = currCtx; } @@ -117,7 +112,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/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/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 49cdc8b..bb47f11 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,67 +36,75 @@ * @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 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/RestForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java index fb3aba9..ef85df8 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", @@ -80,13 +82,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); } @@ -100,24 +102,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 ", @@ -126,7 +128,7 @@ } @Override - public boolean removeFromProjectList(String projectName) { + public CompletableFuture<Boolean> removeFromProjectList(String projectName) { return execute( RequestMethod.DELETE, "Update project_list, remove ", @@ -138,19 +140,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( @@ -176,7 +178,7 @@ }; } - private abstract class Request { + protected abstract class Request { private final String action; private final Object key; private final String destination; @@ -189,42 +191,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 4cdf431..f88e10b 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
@@ -18,14 +18,12 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Comment; -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.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; @@ -39,7 +37,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; @@ -54,20 +51,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() { return getComputedChangeTs() .map( ts -> { @@ -79,7 +74,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; @@ -87,8 +82,7 @@ } @Override - public boolean isChangeUpToDate(Optional<IndexEvent> indexEvent) - throws IOException, OrmException { + public boolean isChangeUpToDate(Optional<IndexEvent> indexEvent) { getComputedChangeTs(); log.atFine().log("Checking change %s against index event %s", this, indexEvent); if (!computedChangeTs.isPresent()) { @@ -110,7 +104,7 @@ } @Override - public Optional<Long> getComputedChangeTs() throws IOException, OrmException { + public Optional<Long> getComputedChangeTs() { if (!computedChangeTs.isPresent()) { computedChangeTs = computeLastChangeTs(); } @@ -119,17 +113,12 @@ @Override public String toString() { - try { - return "change-id=" - + changeId - + "@" - + getComputedChangeTs().map(IndexEvent::format) - + "/" - + getBranchTargetSha(); - } catch (IOException | OrmException e) { - log.atSevere().withCause(e).log("Unable to render change %s", changeId); - return "change-id=" + changeId; - } + return "change-id=" + + changeId + + "@" + + getComputedChangeTs().map(IndexEvent::format) + + "/" + + getBranchTargetSha(); } private String getBranchTargetSha() { @@ -148,22 +137,16 @@ } } - private Optional<Long> computeLastChangeTs() throws OrmException { - try (ReviewDb db = changeDb.open()) { - return getChangeNotes().map(notes -> getTsFromChangeAndDraftComments(db, notes)); - } + private Optional<Long> computeLastChangeTs() { + return getChangeNotes().map(this::getTsFromChangeAndDraftComments); } - 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 0697ad7..b311db9 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,25 +39,34 @@ GroupIndexedListener, ProjectIndexedListener { private static final FluentLogger log = FluentLogger.forEnclosingClass(); - private final Executor executor; + private final ScheduledExecutorService executor; 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, + @IndexExecutor ScheduledExecutorService executor, @PluginName String pluginName, Forwarder forwarder, ChangeCheckerImpl.Factory changeChecker, - CurrentRequestContext currCtx) { + CurrentRequestContext currCtx, + Configuration cfg, + IndexEventLocks locks) { this.forwarder = forwarder; this.executor = executor; this.pluginName = pluginName; this.changeChecker = changeChecker; this.currCtx = currCtx; + this.locks = locks; + this.retryInterval = cfg.http().retryInterval(); + this.maxTries = cfg.http().maxTries(); } @Override @@ -128,6 +140,7 @@ abstract class IndexTask implements Runnable { protected final IndexEvent indexEvent; + private int retryCount = 0; IndexTask() { indexEvent = new IndexEvent(); @@ -139,11 +152,25 @@ @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(); } class IndexChangeTask extends IndexTask { @@ -157,8 +184,8 @@ } @Override - public void execute() { - forwarder.indexChange(projectName, changeId, indexEvent); + public CompletableFuture<Boolean> execute() { + return forwarder.indexChange(projectName, changeId, indexEvent); } @Override @@ -190,8 +217,8 @@ } @Override - public void execute() { - forwarder.deleteChangeFromIndex(changeId, indexEvent); + public CompletableFuture<Boolean> execute() { + return forwarder.deleteChangeFromIndex(changeId, indexEvent); } @Override @@ -222,8 +249,8 @@ } @Override - public void execute() { - forwarder.indexAccount(accountId, indexEvent); + public CompletableFuture<Boolean> execute() { + return forwarder.indexAccount(accountId, indexEvent); } @Override @@ -254,8 +281,8 @@ } @Override - public void execute() { - forwarder.indexGroup(groupUUID, indexEvent); + public CompletableFuture<Boolean> execute() { + return forwarder.indexGroup(groupUUID, indexEvent); } @Override @@ -286,8 +313,8 @@ } @Override - public void execute() { - forwarder.indexProject(projectName, indexEvent); + public CompletableFuture<Boolean> execute() { + return forwarder.indexProject(projectName, indexEvent); } @Override
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..4434c34 --- /dev/null +++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventLocks.java
@@ -0,0 +1,74 @@ +// 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.TimeUnit; +import java.util.concurrent.locks.Lock; + +public class IndexEventLocks { + private static final FluentLogger log = FluentLogger.forEnclosingClass(); + + private static final int NUMBER_OF_INDEX_TASK_TYPES = 4; + + private final Striped<Lock> locks; + private final long waitTimeout; + + @Inject + public IndexEventLocks(Configuration cfg) { + this.locks = Striped.lock(NUMBER_OF_INDEX_TASK_TYPES * cfg.index().numStripedLocks()); + this.waitTimeout = cfg.index().waitTimeout(); + } + + public void withLock( + IndexTask id, IndexCallFunction function, VoidFunction lockAcquireTimeoutCallback) { + Lock idLock = getLock(id); + try { + if (idLock.tryLock(waitTimeout, TimeUnit.MILLISECONDS)) { + function + .invoke() + .whenComplete( + (result, error) -> { + idLock.unlock(); + }); + } else { + lockAcquireTimeoutCallback.invoke(); + } + } catch (InterruptedException e) { + log.atSevere().withCause(e).log("%s was interrupted; giving up", id); + } + } + + @VisibleForTesting + protected Lock getLock(IndexTask id) { + return locks.get(id); + } + + @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 c17731f..4a25fc8 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,20 +20,25 @@ 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(ScheduledExecutorService.class) + .annotatedWith(IndexExecutor.class) + .toProvider(IndexExecutorProvider.class); bind(ScheduledExecutorService.class) .annotatedWith(ForwardedIndexExecutor.class) .toProvider(ForwardedIndexExecutorProvider.class); + bind(IndexEventLocks.class).in(Scopes.SINGLETON); 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 e79cbdd..f5c786f 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. @@ -211,6 +207,14 @@ : The interval of time in milliseconds between the subsequent auto-retries. Defaults to 30000 (30 seconds). +```index.waitTimeout``` +: Maximum interval of time in milliseconds the plugin waits to acquire + the lock for an indexing call. When not specified, the default value + is set to 5000ms. + +NOTE: the default settings for `http.socketTimeout`, `http.maxTries` and `index.waitTimeout` +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..65804b3 100755 --- a/src/test/docker/gerrit/start.sh +++ b/src/test/docker/gerrit/start.sh
@@ -1,7 +1,5 @@ #!/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" @@ -12,7 +10,7 @@ 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 2cf12fc..a9537bd 100644 --- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java +++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
@@ -18,6 +18,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; @@ -27,7 +28,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; @@ -114,15 +114,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 @@ -131,7 +132,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 @@ -155,7 +156,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/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/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 55a3c2a..44e6e73 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; @@ -83,17 +85,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( @@ -101,106 +108,146 @@ 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 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 @@ -209,7 +256,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 @@ -218,7 +266,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 @@ -227,7 +276,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 @@ -236,7 +286,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 @@ -245,7 +297,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 @@ -254,7 +307,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 @@ -264,7 +318,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) { @@ -276,7 +331,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 @@ -284,7 +340,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 @@ -293,7 +350,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 @@ -301,7 +359,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 @@ -309,7 +368,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 @@ -318,7 +378,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) { @@ -326,41 +387,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/IndexEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java index 2d12ca8..7097a3a 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
@@ -32,20 +32,32 @@ 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.IndexProjectTask; 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.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; import java.util.function.Consumer; 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) @@ -64,7 +76,10 @@ private Change.Id changeId; private Account.Id accountId; private AccountGroup.UUID accountGroupUUID; + private ScheduledExecutorService executor = new CurrentThreadScheduledExecutorService(); @Mock private RequestContext mockCtx; + @Mock private Configuration configuration; + private IndexEventLocks idLocks; private CurrentRequestContext currCtx = new CurrentRequestContext(null, null, null) { @@ -82,17 +97,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); + when(cfgIndex.waitTimeout()).thenReturn(Configuration.DEFAULT_TIMEOUT_MS); + + 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(), + executor, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, - currCtx); + currCtx, + configuration, + idLocks); } @Test @@ -115,6 +161,153 @@ } @Test + public void shouldNotIndexChangeWhenCannotAcquireLock() throws Exception { + IndexEventLocks locks = mock(IndexEventLocks.class); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexChangeTask.class))).thenReturn(lock); + Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any()); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexAccountTask.class))).thenReturn(lock); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(DeleteChangeTask.class))).thenReturn(lock); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexGroupTask.class))).thenReturn(lock); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexProjectTask.class))).thenReturn(lock); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexChangeTask.class))).thenReturn(lock); + Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any()); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexChangeTask.class))).thenReturn(lock); + Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any()); + when(lock.tryLock(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); + Lock lock = mock(Lock.class); + when(locks.getLock(any(IndexChangeTask.class))).thenReturn(lock); + Mockito.doCallRealMethod().when(locks).withLock(any(), any(), any()); + when(lock.tryLock(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); + Lock indexChangeLock = mock(Lock.class); + when(indexChangeLock.tryLock(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS))) + .thenReturn(false); + Lock accountChangeLock = mock(Lock.class); + when(accountChangeLock.tryLock(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS))) + .thenReturn(true); + when(locks.getLock(any(IndexChangeTask.class))).thenReturn(indexChangeLock); + when(locks.getLock(any(IndexAccountTask.class))).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); @@ -179,7 +372,14 @@ public void duplicateChangeEventOfAQueuedEventShouldGetDiscarded() { ScheduledThreadPoolExecutor poolMock = mock(ScheduledThreadPoolExecutor.class); indexEventHandler = - new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx); + new IndexEventHandler( + poolMock, + PLUGIN_NAME, + forwarder, + changeCheckerFactoryMock, + currCtx, + configuration, + idLocks); indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get()); indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get()); verify(poolMock, times(1)) @@ -190,7 +390,14 @@ public void duplicateAccountEventOfAQueuedEventShouldGetDiscarded() { ScheduledThreadPoolExecutor poolMock = mock(ScheduledThreadPoolExecutor.class); indexEventHandler = - new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx); + new IndexEventHandler( + poolMock, + 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)); @@ -200,7 +407,14 @@ public void duplicateGroupEventOfAQueuedEventShouldGetDiscarded() { ScheduledThreadPoolExecutor poolMock = mock(ScheduledThreadPoolExecutor.class); indexEventHandler = - new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder, changeCheckerFactoryMock, currCtx); + new IndexEventHandler( + poolMock, + PLUGIN_NAME, + forwarder, + changeCheckerFactoryMock, + currCtx, + configuration, + idLocks); indexEventHandler.onGroupIndexed(accountGroupUUID.get()); indexEventHandler.onGroupIndexed(accountGroupUUID.get()); verify(poolMock, times(1)).execute(indexEventHandler.new IndexGroupTask(UUID)); @@ -316,4 +530,98 @@ assertThat(task.equals(differentGroupIdTask)).isFalse(); assertThat(task.hashCode()).isNotEqualTo(differentGroupIdTask.hashCode()); } + + 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) }