summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/pgm/Reindex.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/pgm/Reindex.java')
-rw-r--r--java/com/google/gerrit/pgm/Reindex.java213
1 files changed, 213 insertions, 0 deletions
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
new file mode 100644
index 0000000000..52d3bd387b
--- /dev/null
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -0,0 +1,213 @@
+// Copyright (C) 2013 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.pgm;
+
+import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toSet;
+
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.Die;
+import com.google.gerrit.elasticsearch.ElasticIndexModule;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.index.Index;
+import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.index.SiteIndexer;
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.lucene.LuceneIndexModule;
+import com.google.gerrit.pgm.util.BatchProgramModule;
+import com.google.gerrit.pgm.util.SiteProgram;
+import com.google.gerrit.pgm.util.ThreadLimiter;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.index.IndexModule;
+import com.google.gerrit.server.index.IndexModule.IndexType;
+import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
+import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.util.io.NullOutputStream;
+import org.kohsuke.args4j.Option;
+
+public class Reindex extends SiteProgram {
+ @Option(name = "--threads", usage = "Number of threads to use for indexing")
+ private int threads = Runtime.getRuntime().availableProcessors();
+
+ @Option(
+ name = "--changes-schema-version",
+ usage = "Schema version to reindex, for changes; default is most recent version")
+ private Integer changesVersion;
+
+ @Option(name = "--verbose", usage = "Output debug information for each change")
+ private boolean verbose;
+
+ @Option(name = "--list", usage = "List supported indices and exit")
+ private boolean list;
+
+ @Option(name = "--index", usage = "Only reindex specified indices")
+ private List<String> indices = new ArrayList<>();
+
+ private Injector dbInjector;
+ private Injector sysInjector;
+ private Injector cfgInjector;
+ private Config globalConfig;
+
+ @Inject private Collection<IndexDefinition<?, ?, ?>> indexDefs;
+
+ @Override
+ public int run() throws Exception {
+ mustHaveValidSite();
+ dbInjector = createDbInjector(MULTI_USER);
+ cfgInjector = dbInjector.createChildInjector();
+ globalConfig = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
+ threads = ThreadLimiter.limitThreads(dbInjector, threads);
+ overrideConfig();
+ LifecycleManager dbManager = new LifecycleManager();
+ dbManager.add(dbInjector);
+ dbManager.start();
+
+ sysInjector = createSysInjector();
+ sysInjector.getInstance(PluginGuiceEnvironment.class).setDbCfgInjector(dbInjector, cfgInjector);
+ LifecycleManager sysManager = new LifecycleManager();
+ sysManager.add(sysInjector);
+ sysManager.start();
+
+ sysInjector.injectMembers(this);
+ checkIndicesOption();
+
+ try {
+ boolean ok = list ? list() : reindex();
+ return ok ? 0 : 1;
+ } catch (Exception e) {
+ throw die(e.getMessage(), e);
+ } finally {
+ sysManager.stop();
+ dbManager.stop();
+ }
+ }
+
+ private boolean list() {
+ for (IndexDefinition<?, ?, ?> def : indexDefs) {
+ System.out.format("%s\n", def.getName());
+ }
+ return true;
+ }
+
+ private boolean reindex() throws IOException {
+ boolean ok = true;
+ for (IndexDefinition<?, ?, ?> def : indexDefs) {
+ if (indices.isEmpty() || indices.contains(def.getName())) {
+ ok &= reindex(def);
+ }
+ }
+ return ok;
+ }
+
+ private void checkIndicesOption() throws Die {
+ if (indices.isEmpty()) {
+ return;
+ }
+
+ requireNonNull(indexDefs, "Called this method before injectMembers?");
+ Set<String> valid = indexDefs.stream().map(IndexDefinition::getName).sorted().collect(toSet());
+ Set<String> invalid = Sets.difference(Sets.newHashSet(indices), valid);
+ if (invalid.isEmpty()) {
+ return;
+ }
+
+ throw die(
+ "invalid index name(s): " + new TreeSet<>(invalid) + " available indices are: " + valid);
+ }
+
+ private Injector createSysInjector() {
+ Map<String, Integer> versions = new HashMap<>();
+ if (changesVersion != null) {
+ versions.put(ChangeSchemaDefinitions.INSTANCE.getName(), changesVersion);
+ }
+ boolean slave = globalConfig.getBoolean("container", "slave", false);
+ List<Module> modules = new ArrayList<>();
+ Module indexModule;
+ switch (IndexModule.getIndexType(dbInjector)) {
+ case LUCENE:
+ indexModule = LuceneIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
+ break;
+ case ELASTICSEARCH:
+ indexModule =
+ ElasticIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
+ break;
+ default:
+ throw new IllegalStateException("unsupported index.type");
+ }
+ modules.add(indexModule);
+ modules.add(dbInjector.getInstance(BatchProgramModule.class));
+ modules.add(
+ new FactoryModule() {
+ @Override
+ protected void configure() {
+ factory(ChangeResource.Factory.class);
+ }
+ });
+
+ return dbInjector.createChildInjector(modules);
+ }
+
+ private void overrideConfig() {
+ // Disable auto-commit for speed; committing will happen at the end of the process.
+ if (IndexModule.getIndexType(dbInjector) == IndexType.LUCENE) {
+ globalConfig.setLong("index", "changes_open", "commitWithin", -1);
+ globalConfig.setLong("index", "changes_closed", "commitWithin", -1);
+ }
+
+ // Disable change cache.
+ globalConfig.setLong("cache", "changes", "maximumWeight", 0);
+
+ // Disable auto-reindexing if stale, since there are no concurrent writes to race with.
+ globalConfig.setBoolean("index", null, "autoReindexIfStale", false);
+ }
+
+ private <K, V, I extends Index<K, V>> boolean reindex(IndexDefinition<K, V, I> def)
+ throws IOException {
+ I index = def.getIndexCollection().getSearchIndex();
+ requireNonNull(
+ index, () -> String.format("no active search index configured for %s", def.getName()));
+ index.markReady(false);
+ index.deleteAll();
+
+ SiteIndexer<K, V, I> siteIndexer = def.getSiteIndexer();
+ siteIndexer.setProgressOut(System.err);
+ siteIndexer.setVerboseOut(verbose ? System.out : NullOutputStream.INSTANCE);
+ SiteIndexer.Result result = siteIndexer.indexAll(index);
+ int n = result.doneCount() + result.failedCount();
+ double t = result.elapsed(TimeUnit.MILLISECONDS) / 1000d;
+ System.out.format(
+ "Reindexed %d documents in %s index in %.01fs (%.01f/s)\n", n, def.getName(), t, n / t);
+ if (result.success()) {
+ index.markReady(true);
+ }
+ return result.success();
+ }
+}