diff --git a/Documentation/cmd-index-start.txt b/Documentation/cmd-index-start.txt new file mode 100644 index 0000000000..4148b2405c --- /dev/null +++ b/Documentation/cmd-index-start.txt @@ -0,0 +1,33 @@ += gerrit index start + +== NAME +gerrit index start - Start the online indexer + +== SYNOPSIS +-- +'ssh' -p @SSH_PORT@ @SSH_HOST@ 'gerrit index start' +-- + +== DESCRIPTION +Gerrit supports online index schema upgrades. When starting Gerrit for the first +time after an upgrade that requires an index schema upgrade, the online indexer +will be started. If the schema upgrade is a success, the new index will be +activated and if it fails, a statement in the logs will be printed with the +number of successfully/failed indexed changes. + +This command allows restarting the online indexer without having to restart +Gerrit. This command will not start the indexer if it is already running or if +the active index is the latest. + +== ACCESS +Caller must be a member of the privileged 'Administrators' group. + +== SCRIPTING +This command is intended to be used in scripts. + +GERRIT +------ +Part of link:index.html[Gerrit Code Review] + +SEARCHBOX +--------- diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt index 7c664ac728..6a5805a448 100644 --- a/Documentation/cmd-index.txt +++ b/Documentation/cmd-index.txt @@ -117,6 +117,9 @@ link:cmd-gc.html[gerrit gc]:: link:cmd-gsql.html[gerrit gsql]:: Administrative interface to active database. +link:cmd-index-start.html[gerrit index start]:: + Start the online indexer. + link:cmd-logging-ls-level.html[gerrit logging ls-level]:: List loggers and their logging level. diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java index c3570a1370..b75a4dee2d 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java @@ -43,7 +43,7 @@ import java.util.List; import java.util.TreeMap; @Singleton -class LuceneVersionManager implements LifecycleListener { +public class LuceneVersionManager implements LifecycleListener { private static final Logger log = LoggerFactory .getLogger(LuceneVersionManager.class); @@ -90,6 +90,7 @@ class LuceneVersionManager implements LifecycleListener { private final LuceneChangeIndex.Factory indexFactory; private final IndexCollection indexes; private final OnlineReindexer.Factory reindexerFactory; + private OnlineReindexer reindexer; @Inject LuceneVersionManager( @@ -160,7 +161,37 @@ class LuceneVersionManager implements LifecycleListener { int latest = write.get(0).version; if (latest != search.version) { - reindexerFactory.create(latest).start(); + reindexer = reindexerFactory.create(latest); + reindexer.start(); + } + } + + /** + * Start the online reindexer if the current index is not already the latest. + * + * @return true if started, otherwise false. + * @throws ReindexerAlreadyRunningException + */ + public synchronized boolean startReindexer() + throws ReindexerAlreadyRunningException { + validateReindexerNotRunning(); + if (!isCurrentIndexVersionLatest()) { + reindexer.start(); + return true; + } + return false; + } + + private boolean isCurrentIndexVersionLatest() { + return reindexer == null + || reindexer.getVersion() == indexes.getSearchIndex().getSchema() + .getVersion(); + } + + private void validateReindexerNotRunning() + throws ReindexerAlreadyRunningException { + if (reindexer != null && reindexer.isRunning()) { + throw new ReindexerAlreadyRunningException(); } } diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java index edded44f40..96c1719695 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public class OnlineReindexer { private static final Logger log = LoggerFactory @@ -42,6 +43,7 @@ public class OnlineReindexer { private final SiteIndexer batchIndexer; private final ProjectCache projectCache; private final int version; + private final AtomicBoolean running = new AtomicBoolean(); @Inject OnlineReindexer( @@ -56,15 +58,29 @@ public class OnlineReindexer { } public void start() { - Thread t = new Thread() { - @Override - public void run() { - reindex(); - } - }; - t.setName(String.format("Reindex v%d-v%d", - version(indexes.getSearchIndex()), version)); - t.start(); + if (running.compareAndSet(false, true)) { + Thread t = new Thread() { + @Override + public void run() { + try { + reindex(); + } finally { + running.set(false); + } + } + }; + t.setName(String.format("Reindex v%d-v%d", + version(indexes.getSearchIndex()), version)); + t.start(); + } + } + + public boolean isRunning() { + return running.get(); + } + + public int getVersion() { + return version; } private static int version(ChangeIndex i) { diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ReindexerAlreadyRunningException.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ReindexerAlreadyRunningException.java new file mode 100644 index 0000000000..0ca632bc71 --- /dev/null +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ReindexerAlreadyRunningException.java @@ -0,0 +1,24 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.lucene; + +public class ReindexerAlreadyRunningException extends Exception { + + private static final long serialVersionUID = 1L; + + public ReindexerAlreadyRunningException() { + super("Reindexer is already running."); + } +} diff --git a/gerrit-sshd/BUCK b/gerrit-sshd/BUCK index 4774cb3868..f4ff65bc29 100644 --- a/gerrit-sshd/BUCK +++ b/gerrit-sshd/BUCK @@ -8,6 +8,7 @@ java_library( '//gerrit-cache-h2:cache-h2', '//gerrit-common:annotations', '//gerrit-common:server', + '//gerrit-lucene:lucene', '//gerrit-patch-jgit:server', '//gerrit-reviewdb:server', '//gerrit-server:server', diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java index 4347d832b9..a61f9c8123 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java @@ -36,6 +36,7 @@ public class DefaultCommandModule extends CommandModule { protected void configure() { final CommandName git = Commands.named("git"); final CommandName gerrit = Commands.named("gerrit"); + CommandName index = Commands.named(gerrit, "index"); final CommandName logging = Commands.named(gerrit, "logging"); final CommandName plugin = Commands.named(gerrit, "plugin"); final CommandName testSubmit = Commands.named(gerrit, "test-submit"); @@ -56,8 +57,11 @@ public class DefaultCommandModule extends CommandModule { command(gerrit, StreamEvents.class); command(gerrit, VersionCommand.class); command(gerrit, GarbageCollectionCommand.class); - command(gerrit, "plugin").toProvider(new DispatchCommandProvider(plugin)); + command(index).toProvider(new DispatchCommandProvider(index)); + command(index, IndexStartCommand.class); + + command(gerrit, "plugin").toProvider(new DispatchCommandProvider(plugin)); command(plugin, PluginLsCommand.class); command(plugin, PluginEnableCommand.class); command(plugin, PluginInstallCommand.class); diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexStartCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexStartCommand.java new file mode 100644 index 0000000000..1b3b8190d8 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexStartCommand.java @@ -0,0 +1,47 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER; + +import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.lucene.LuceneVersionManager; +import com.google.gerrit.lucene.ReindexerAlreadyRunningException; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; + +@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) +@CommandMetaData(name = "start", description = "Start the online reindexer", + runsAt = MASTER) +public class IndexStartCommand extends SshCommand { + + @Inject + private LuceneVersionManager luceneVersionManager; + + @Override + protected void run() throws UnloggedFailure { + try { + if (luceneVersionManager.startReindexer()) { + stdout.println("Reindexer started"); + } else { + stdout.println("Nothing to reindex, index is already the latest version"); + } + } catch (ReindexerAlreadyRunningException e) { + throw new UnloggedFailure("Failed to start reindexer: " + e.getMessage()); + } + } +}