aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test
diff options
context:
space:
mode:
authorFabio Ponciroli <ponch78@gmail.com>2021-06-03 16:15:17 +0200
committerMatthias Sohn <matthias.sohn@sap.com>2021-06-25 17:57:59 +0200
commit6976a30f443ece4815a977b0a5a897c0236018f7 (patch)
tree24309a81a94c81456c96fd888e009daa10f77266 /org.eclipse.jgit.test
parentf598e69529e0a1864e8224265ed82326f2a296f5 (diff)
downloadjgit-6976a30f443ece4815a977b0a5a897c0236018f7.tar.gz
jgit-6976a30f443ece4815a977b0a5a897c0236018f7.zip
searchForReuse might impact performance in large repositories
The search for reuse phase for *all* the objects scans *all* the packfiles, looking for the best candidate to serve back to the client. This can lead to an expensive operation when the number of packfiles and objects is high. Add parameter "pack.searchForReuseTimeout" to limit the time spent on this search. Change-Id: I54f5cddb6796fdc93ad9585c2ab4b44854fa6c48
Diffstat (limited to 'org.eclipse.jgit.test')
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java133
1 files changed, 133 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index e422ab9db3..71aca9d80c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -18,6 +18,10 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -25,6 +29,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -32,6 +37,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -43,6 +49,7 @@ import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.revwalk.DepthWalk;
@@ -58,6 +65,7 @@ import org.eclipse.jgit.transport.PackParser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
public class PackWriterTest extends SampleDataRepositoryTestCase {
@@ -626,6 +634,131 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
}
}
+ @Test
+ public void testTotalPackFilesScanWhenSearchForReuseTimeoutNotSet()
+ throws Exception {
+ FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
+ PackWriter mockedPackWriter = Mockito
+ .spy(new PackWriter(config, fileRepository.newObjectReader()));
+
+ doNothing().when(mockedPackWriter).select(any(), any());
+
+ try (FileOutputStream packOS = new FileOutputStream(
+ getPackFileToWrite(fileRepository, mockedPackWriter))) {
+ mockedPackWriter.writePack(NullProgressMonitor.INSTANCE,
+ NullProgressMonitor.INSTANCE, packOS);
+ }
+
+ long numberOfPackFiles = new GC(fileRepository)
+ .getStatistics().numberOfPackFiles;
+ int expectedSelectCalls =
+ // Objects contained in multiple packfiles * number of packfiles
+ 2 * (int) numberOfPackFiles +
+ // Objects in single packfile
+ 1;
+ verify(mockedPackWriter, times(expectedSelectCalls)).select(any(),
+ any());
+ }
+
+ @Test
+ public void testTotalPackFilesScanWhenSkippingSearchForReuseTimeoutCheck()
+ throws Exception {
+ FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
+ PackConfig packConfig = new PackConfig();
+ packConfig.setSearchForReuseTimeout(Duration.ofSeconds(-1));
+ PackWriter mockedPackWriter = Mockito.spy(
+ new PackWriter(packConfig, fileRepository.newObjectReader()));
+
+ doNothing().when(mockedPackWriter).select(any(), any());
+
+ try (FileOutputStream packOS = new FileOutputStream(
+ getPackFileToWrite(fileRepository, mockedPackWriter))) {
+ mockedPackWriter.writePack(NullProgressMonitor.INSTANCE,
+ NullProgressMonitor.INSTANCE, packOS);
+ }
+
+ long numberOfPackFiles = new GC(fileRepository)
+ .getStatistics().numberOfPackFiles;
+ int expectedSelectCalls =
+ // Objects contained in multiple packfiles * number of packfiles
+ 2 * (int) numberOfPackFiles +
+ // Objects contained in single packfile
+ 1;
+ verify(mockedPackWriter, times(expectedSelectCalls)).select(any(),
+ any());
+ }
+
+ @Test
+ public void testPartialPackFilesScanWhenDoingSearchForReuseTimeoutCheck()
+ throws Exception {
+ FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
+ PackConfig packConfig = new PackConfig();
+ packConfig.setSearchForReuseTimeout(Duration.ofSeconds(-1));
+ PackWriter mockedPackWriter = Mockito.spy(
+ new PackWriter(packConfig, fileRepository.newObjectReader()));
+ mockedPackWriter.enableSearchForReuseTimeout();
+
+ doNothing().when(mockedPackWriter).select(any(), any());
+
+ try (FileOutputStream packOS = new FileOutputStream(
+ getPackFileToWrite(fileRepository, mockedPackWriter))) {
+ mockedPackWriter.writePack(NullProgressMonitor.INSTANCE,
+ NullProgressMonitor.INSTANCE, packOS);
+ }
+
+ int expectedSelectCalls = 3; // Objects in packfiles
+ verify(mockedPackWriter, times(expectedSelectCalls)).select(any(),
+ any());
+ }
+
+ /**
+ * Creates objects and packfiles in the following order:
+ * <ul>
+ * <li>Creates 2 objects (C1 = commit, T1 = tree)
+ * <li>Creates packfile P1 (containing C1, T1)
+ * <li>Creates 1 object (C2 commit)
+ * <li>Creates packfile P2 (containing C1, T1, C2)
+ * <li>Create 1 object (C3 commit)
+ * </ul>
+ *
+ * @throws Exception
+ */
+ private FileRepository setUpRepoWithMultiplePackfiles() throws Exception {
+ FileRepository fileRepository = createWorkRepository();
+ try (Git git = new Git(fileRepository)) {
+ // Creates 2 objects (C1 = commit, T1 = tree)
+ git.commit().setMessage("First commit").call();
+ GC gc = new GC(fileRepository);
+ gc.setPackExpireAgeMillis(Long.MAX_VALUE);
+ gc.setExpireAgeMillis(Long.MAX_VALUE);
+ // Creates packfile P1 (containing C1, T1)
+ gc.gc();
+ // Creates 1 object (C2 commit)
+ git.commit().setMessage("Second commit").call();
+ // Creates packfile P2 (containing C1, T1, C2)
+ gc.gc();
+ // Create 1 object (C3 commit)
+ git.commit().setMessage("Third commit").call();
+ }
+ return fileRepository;
+ }
+
+ private PackFile getPackFileToWrite(FileRepository fileRepository,
+ PackWriter mockedPackWriter) throws IOException {
+ File packdir = fileRepository.getObjectDatabase().getPackDirectory();
+ PackFile packFile = new PackFile(packdir,
+ mockedPackWriter.computeName(), PackExt.PACK);
+
+ Set<ObjectId> all = new HashSet<>();
+ for (Ref r : fileRepository.getRefDatabase().getRefs()) {
+ all.add(r.getObjectId());
+ }
+
+ mockedPackWriter.preparePack(NullProgressMonitor.INSTANCE, all,
+ PackWriter.NONE);
+ return packFile;
+ }
+
private FileRepository setupRepoForShallowFetch() throws Exception {
FileRepository repo = createBareRepository();
// TestRepository will close the repo, but we need to return an open