Browse Source

GC: don't loosen doomed objects

If the pruneexpire config is set to "now", then any unreferenced loose
objects are immediately eligible for gc.  So there is no need to
actually write the loose objects.

Users who run hosting services which sometimes accept large, entirely
garbage packs might set the following configurations:

gc.pruneExpire = now
gc.prunePackExpire = 2.weeks

Then garbage objects will be kept around in packs, but after two weeks
the packs themselves will get deleted.

For client-side users of jgit, the default settings will loosen
garbage objects, and, after an hour, delete the old packs in which
they resided.

Change-Id: I8f686ac60b40181b1ee92ac6c313c3f33b55c44c
Signed-off-by: David Turner <dturner@twosigma.com>
stable-4.7
David Turner 8 years ago
parent
commit
d3962fef6b
  1. 39
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
  2. 14
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java

39
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java

@ -55,8 +55,10 @@ import java.util.Date;
import java.util.List; import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder; import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackConfig;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.DataPoints;
@ -247,7 +249,44 @@ public class GcBasicPackingTest extends GcTestCase {
// times // times
assertEquals(6, stats.numberOfPackedObjects); assertEquals(6, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles); assertEquals(1, stats.numberOfPackFiles);
}
@Test
public void testImmediatePruning() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().message("M").add("M", "M").create();
String tempRef = "refs/heads/soon-to-be-unreferenced";
BranchBuilder bb2 = tr.branch(tempRef);
bb2.commit().message("M").add("M", "M").create();
gc.setExpireAgeMillis(0);
gc.gc();
stats = gc.getStatistics();
fsTick();
// delete the temp ref, orphaning its commit
RefUpdate update = tr.getRepository().getRefDatabase().newUpdate(tempRef, false);
update.setForceUpdate(true);
update.delete();
bb.commit().message("B").add("B", "Q").create();
// We want to immediately prune deleted objects
FileBasedConfig config = repo.getConfig();
config.setString(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE, "now");
config.save();
//And we don't want to keep packs full of dead objects
gc.setPackExpireAgeMillis(0);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(6, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
} }
@Test @Test

14
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java

@ -274,7 +274,8 @@ public class GC {
ObjectReader reader = repo.newObjectReader(); ObjectReader reader = repo.newObjectReader();
ObjectDirectory dir = repo.getObjectDatabase(); ObjectDirectory dir = repo.getObjectDatabase();
ObjectDirectoryInserter inserter = dir.newInserter(); ObjectDirectoryInserter inserter = dir.newInserter();
boolean shouldLoosen = getExpireDate() < Long.MAX_VALUE; boolean shouldLoosen = !"now".equals(getPruneExpireStr()) && //$NON-NLS-1$
getExpireDate() < Long.MAX_VALUE;
prunePreserved(); prunePreserved();
long packExpireDate = getPackExpireDate(); long packExpireDate = getPackExpireDate();
@ -297,6 +298,7 @@ public class GC {
prunePack(oldName); prunePack(oldName);
} }
} }
// close the complete object database. That's my only chance to force // close the complete object database. That's my only chance to force
// rescanning and to detect that certain pack files are now deleted. // rescanning and to detect that certain pack files are now deleted.
repo.getObjectDatabase().close(); repo.getObjectDatabase().close();
@ -599,9 +601,7 @@ public class GC {
long expireDate = Long.MAX_VALUE; long expireDate = Long.MAX_VALUE;
if (expire == null && expireAgeMillis == -1) { if (expire == null && expireAgeMillis == -1) {
String pruneExpireStr = repo.getConfig().getString( String pruneExpireStr = getPruneExpireStr();
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
if (pruneExpireStr == null) if (pruneExpireStr == null)
pruneExpireStr = PRUNE_EXPIRE_DEFAULT; pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
expire = GitDateParser.parse(pruneExpireStr, null, SystemReader expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
@ -615,6 +615,12 @@ public class GC {
return expireDate; return expireDate;
} }
private String getPruneExpireStr() {
return repo.getConfig().getString(
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
}
private long getPackExpireDate() throws ParseException { private long getPackExpireDate() throws ParseException {
long packExpireDate = Long.MAX_VALUE; long packExpireDate = Long.MAX_VALUE;

Loading…
Cancel
Save