| // Copyright (C) 2012 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.git; |
| |
| import com.google.common.collect.Sets; |
| import com.google.gerrit.common.data.GarbageCollectionResult; |
| import com.google.gerrit.reviewdb.client.Project; |
| import com.google.inject.Inject; |
| |
| import org.eclipse.jgit.api.GarbageCollectCommand; |
| import org.eclipse.jgit.api.Git; |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.errors.RepositoryNotFoundException; |
| import org.eclipse.jgit.lib.Config; |
| import org.eclipse.jgit.lib.ConfigConstants; |
| import org.eclipse.jgit.lib.NullProgressMonitor; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.TextProgressMonitor; |
| import org.eclipse.jgit.storage.pack.PackConfig; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| public class GarbageCollection { |
| private static final Logger log = LoggerFactory |
| .getLogger(GarbageCollection.class); |
| |
| public static final String LOG_NAME = "gc_log"; |
| private static final Logger gcLog = LoggerFactory.getLogger(LOG_NAME); |
| |
| |
| private final GitRepositoryManager repoManager; |
| private final GarbageCollectionQueue gcQueue; |
| |
| public interface Factory { |
| GarbageCollection create(); |
| } |
| |
| @Inject |
| GarbageCollection(GitRepositoryManager repoManager, GarbageCollectionQueue gcQueue) { |
| this.repoManager = repoManager; |
| this.gcQueue = gcQueue; |
| } |
| |
| public GarbageCollectionResult run(List<Project.NameKey> projectNames) { |
| return run(projectNames, null); |
| } |
| |
| public GarbageCollectionResult run(List<Project.NameKey> projectNames, |
| PrintWriter writer) { |
| GarbageCollectionResult result = new GarbageCollectionResult(); |
| Set<Project.NameKey> projectsToGc = gcQueue.addAll(projectNames); |
| for (Project.NameKey projectName : Sets.difference( |
| Sets.newHashSet(projectNames), projectsToGc)) { |
| result.addError(new GarbageCollectionResult.Error( |
| GarbageCollectionResult.Error.Type.GC_ALREADY_SCHEDULED, projectName)); |
| } |
| for (Project.NameKey p : projectsToGc) { |
| Repository repo = null; |
| try { |
| repo = repoManager.openRepository(p); |
| logGcConfiguration(p, repo); |
| print(writer, "collecting garbage for \"" + p + "\":\n"); |
| GarbageCollectCommand gc = Git.wrap(repo).gc(); |
| logGcInfo(p, "before:", gc.getStatistics()); |
| gc.setProgressMonitor(writer != null ? new TextProgressMonitor(writer) |
| : NullProgressMonitor.INSTANCE); |
| Properties statistics = gc.call(); |
| logGcInfo(p, "after: ", statistics); |
| print(writer, "done.\n\n"); |
| } catch (RepositoryNotFoundException e) { |
| logGcError(writer, p, e); |
| result.addError(new GarbageCollectionResult.Error( |
| GarbageCollectionResult.Error.Type.REPOSITORY_NOT_FOUND, |
| p)); |
| } catch (IOException e) { |
| logGcError(writer, p, e); |
| result.addError(new GarbageCollectionResult.Error( |
| GarbageCollectionResult.Error.Type.GC_FAILED, p)); |
| } catch (GitAPIException e) { |
| logGcError(writer, p, e); |
| result.addError(new GarbageCollectionResult.Error( |
| GarbageCollectionResult.Error.Type.GC_FAILED, p)); |
| } catch (JGitInternalException e) { |
| logGcError(writer, p, e); |
| result.addError(new GarbageCollectionResult.Error( |
| GarbageCollectionResult.Error.Type.GC_FAILED, p)); |
| } finally { |
| if (repo != null) { |
| repo.close(); |
| } |
| gcQueue.gcFinished(p); |
| } |
| } |
| return result; |
| } |
| |
| private static void logGcInfo(Project.NameKey projectName, String msg) { |
| logGcInfo(projectName, msg, null); |
| } |
| |
| private static void logGcInfo(Project.NameKey projectName, String msg, |
| Properties statistics) { |
| StringBuilder b = new StringBuilder(); |
| b.append("[").append(projectName.get()).append("] "); |
| b.append(msg); |
| if (statistics != null) { |
| b.append(" "); |
| String s = statistics.toString(); |
| if (s.startsWith("{") && s.endsWith("}")) { |
| s = s.substring(1, s.length() - 1); |
| } |
| b.append(s); |
| } |
| gcLog.info(b.toString()); |
| } |
| |
| private static void logGcConfiguration(Project.NameKey projectName, |
| Repository repo) { |
| StringBuilder b = new StringBuilder(); |
| Config cfg = repo.getConfig(); |
| b.append(formatConfigValues(cfg, ConfigConstants.CONFIG_GC_SECTION, null)); |
| for (String subsection : cfg.getSubsections(ConfigConstants.CONFIG_GC_SECTION)) { |
| b.append(formatConfigValues(cfg, ConfigConstants.CONFIG_GC_SECTION, |
| subsection)); |
| } |
| if (b.length() == 0) { |
| b.append("no set"); |
| } |
| |
| logGcInfo(projectName, "gc config: " + b.toString()); |
| logGcInfo(projectName, "pack config: " + (new PackConfig(repo)).toString()); |
| } |
| |
| private static String formatConfigValues(Config config, String section, |
| String subsection) { |
| StringBuilder b = new StringBuilder(); |
| Set<String> names = config.getNames(section, subsection); |
| for (String name : names) { |
| String value = config.getString(section, subsection, name); |
| b.append(section); |
| if (subsection != null) { |
| b.append(".").append(subsection); |
| } |
| b.append("."); |
| b.append(name).append("=").append(value); |
| b.append("; "); |
| } |
| return b.toString(); |
| } |
| |
| private static void logGcError(PrintWriter writer, |
| Project.NameKey projectName, Exception e) { |
| print(writer, "failed.\n\n"); |
| StringBuilder b = new StringBuilder(); |
| b.append("[").append(projectName.get()).append("]"); |
| gcLog.error(b.toString(), e); |
| log.error(b.toString(), e); |
| } |
| |
| private static void print(PrintWriter writer, String message) { |
| if (writer != null) { |
| writer.print(message); |
| } |
| } |
| } |