Package com.gitblit.service

Source Code of com.gitblit.service.GarbageCollectorService

/*
* Copyright 2012 gitblit.com.
*
* 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.gitblit.service;

import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.jgit.api.GarbageCollectCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.FileUtils;

/**
* The Garbage Collector Service handles periodic garbage collection in repositories.
*
* @author James Moger
*
*/
public class GarbageCollectorService implements Runnable {

  public static enum GCStatus {
    READY, COLLECTING;

    public boolean exceeds(GCStatus s) {
      return ordinal() > s.ordinal();
    }
  }
  private final Logger logger = LoggerFactory.getLogger(GarbageCollectorService.class);

  private final IStoredSettings settings;

  private final IRepositoryManager repositoryManager;

  private AtomicBoolean running = new AtomicBoolean(false);

  private AtomicBoolean forceClose = new AtomicBoolean(false);

  private final Map<String, GCStatus> gcCache = new ConcurrentHashMap<String, GCStatus>();

  public GarbageCollectorService(
      IStoredSettings settings,
      IRepositoryManager repositoryManager) {

    this.settings = settings;
    this.repositoryManager = repositoryManager;
  }

  /**
   * Indicates if the GC executor is ready to process repositories.
   *
   * @return true if the GC executor is ready to process repositories
   */
  public boolean isReady() {
    return settings.getBoolean(Keys.git.enableGarbageCollection, false);
  }

  public boolean isRunning() {
    return running.get();
  }

  public boolean lock(String repositoryName) {
    return setGCStatus(repositoryName, GCStatus.COLLECTING);
  }

  /**
   * Tries to set a GCStatus for the specified repository.
   *
   * @param repositoryName
   * @return true if the status has been set
   */
  private boolean setGCStatus(String repositoryName, GCStatus status) {
    String key = repositoryName.toLowerCase();
    if (gcCache.containsKey(key)) {
      if (gcCache.get(key).exceeds(GCStatus.READY)) {
        // already collecting or blocked
        return false;
      }
    }
    gcCache.put(key, status);
    return true;
  }

  /**
   * Returns true if Gitblit is actively collecting garbage in this repository.
   *
   * @param repositoryName
   * @return true if actively collecting garbage
   */
  public boolean isCollectingGarbage(String repositoryName) {
    String key = repositoryName.toLowerCase();
    return gcCache.containsKey(key) && GCStatus.COLLECTING.equals(gcCache.get(key));
  }

  /**
   * Resets the GC status to ready.
   *
   * @param repositoryName
   */
  public void releaseLock(String repositoryName) {
    gcCache.put(repositoryName.toLowerCase(), GCStatus.READY);
  }

  public void close() {
    forceClose.set(true);
  }

  @Override
  public void run() {
    if (!isReady()) {
      return;
    }

    running.set(true);
    Date now = new Date();

    for (String repositoryName : repositoryManager.getRepositoryList()) {
      if (forceClose.get()) {
        break;
      }
      if (isCollectingGarbage(repositoryName)) {
        logger.warn(MessageFormat.format("Already collecting garbage from {0}?!?", repositoryName));
        continue;
      }
      boolean garbageCollected = false;
      RepositoryModel model = null;
      Repository repository = null;
      try {
        model = repositoryManager.getRepositoryModel(repositoryName);
        repository = repositoryManager.getRepository(repositoryName);
        if (repository == null) {
          logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName));
          continue;
        }

        if (!repositoryManager.isIdle(repository)) {
          logger.debug(MessageFormat.format("GCExecutor is skipping {0} because it is not idle", repositoryName));
          continue;
        }

        // By setting the GCStatus to COLLECTING we are
        // disabling *all* access to this repository from Gitblit.
        // Think of this as a clutch in a manual transmission vehicle.
        if (!setGCStatus(repositoryName, GCStatus.COLLECTING)) {
          logger.warn(MessageFormat.format("Can not acquire GC lock for {0}, skipping", repositoryName));
          continue;
        }

        logger.debug(MessageFormat.format("GCExecutor locked idle repository {0}", repositoryName));

        Git git = new Git(repository);
        GarbageCollectCommand gc = git.gc();
        Properties stats = gc.getStatistics();

        // determine if this is a scheduled GC
        Calendar cal = Calendar.getInstance();
        cal.setTime(model.lastGC);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        cal.add(Calendar.DATE, model.gcPeriod);
        Date gcDate = cal.getTime();
        boolean shouldCollectGarbage = now.after(gcDate);

        // determine if filesize triggered GC
        long gcThreshold = FileUtils.convertSizeToLong(model.gcThreshold, 500*1024L);
        long sizeOfLooseObjects = (Long) stats.get("sizeOfLooseObjects");
        boolean hasEnoughGarbage = sizeOfLooseObjects >= gcThreshold;

        // if we satisfy one of the requirements, GC
        boolean hasGarbage = sizeOfLooseObjects > 0;
        if (hasGarbage && (hasEnoughGarbage || shouldCollectGarbage)) {
          long looseKB = sizeOfLooseObjects/1024L;
          logger.info(MessageFormat.format("Collecting {1} KB of loose objects from {0}", repositoryName, looseKB));

          // do the deed
          gc.call();

          garbageCollected = true;
        }
      } catch (Exception e) {
        logger.error("Error collecting garbage in " + repositoryName, e);
      } finally {
        // cleanup
        if (repository != null) {
          if (garbageCollected) {
            // update the last GC date
            model.lastGC = new Date();
            repositoryManager.updateConfiguration(repository, model);
          }

          repository.close();
        }

        // reset the GC lock
        releaseLock(repositoryName);
        logger.debug(MessageFormat.format("GCExecutor released GC lock for {0}", repositoryName));
      }
    }

    running.set(false);
  }
}
TOP

Related Classes of com.gitblit.service.GarbageCollectorService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.