blob: 5bb3669a14d21de89eac32622eeccef547e292f9 [file] [log] [blame]
// 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.server.schema;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
public class Schema_106 extends SchemaVersion {
// we can use multiple threads per CPU as we can expect that threads will be
// waiting for IO
private static final int THREADS_PER_CPU = 4;
private final GitRepositoryManager repoManager;
private final PersonIdent serverUser;
@Inject
Schema_106(
Provider<Schema_105> prior,
GitRepositoryManager repoManager,
@GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.repoManager = repoManager;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
if (!(repoManager instanceof LocalDiskRepositoryManager)) {
return;
}
ui.message("listing all repositories ...");
SortedSet<Project.NameKey> repoList = repoManager.list();
ui.message("done");
ui.message(String.format("creating reflog files for %s branches ...", RefNames.REFS_CONFIG));
ExecutorService executorPool = createExecutor(ui, repoList.size());
List<Future<Void>> futures = new ArrayList<>();
for (Project.NameKey project : repoList) {
Callable<Void> callable = new ReflogCreator(project);
futures.add(executorPool.submit(callable));
}
executorPool.shutdown();
try {
for (Future<Void> future : futures) {
try {
future.get();
} catch (ExecutionException e) {
ui.message(e.getCause().getMessage());
}
}
ui.message("done");
} catch (InterruptedException ex) {
String msg =
String.format(
"Migration step 106 was interrupted. "
+ "Reflog created in %d of %d repositories only.",
countDone(futures), repoList.size());
ui.message(msg);
}
}
private static int countDone(List<Future<Void>> futures) {
int count = 0;
for (Future<Void> future : futures) {
if (future.isDone()) {
count++;
}
}
return count;
}
private ExecutorService createExecutor(UpdateUI ui, int repoCount) {
int procs = Runtime.getRuntime().availableProcessors();
int threads = Math.min(procs * THREADS_PER_CPU, repoCount);
ui.message(String.format("... using %d threads ...", threads));
return Executors.newFixedThreadPool(threads);
}
private class ReflogCreator implements Callable<Void> {
private final Project.NameKey project;
ReflogCreator(Project.NameKey project) {
this.project = project;
}
@Override
public Void call() throws IOException {
try (Repository repo = repoManager.openRepository(project)) {
File metaConfigLog = new File(repo.getDirectory(), "logs/" + RefNames.REFS_CONFIG);
if (metaConfigLog.exists()) {
return null;
}
if (!metaConfigLog.getParentFile().mkdirs() || !metaConfigLog.createNewFile()) {
throw new IOException();
}
ObjectId metaConfigId = repo.resolve(RefNames.REFS_CONFIG);
if (metaConfigId != null) {
try (PrintWriter writer = new PrintWriter(metaConfigLog, UTF_8.name())) {
writer.print(ObjectId.zeroId().name());
writer.print(" ");
writer.print(metaConfigId.name());
writer.print(" ");
writer.print(serverUser.toExternalString());
writer.print("\t");
writer.print("create reflog");
writer.println();
}
}
return null;
} catch (IOException e) {
throw new IOException(
String.format(
"ERROR: Failed to create reflog file for the %s branch in repository %s",
RefNames.REFS_CONFIG, project.get()));
}
}
}
}