]> source.dussan.org Git - jgit.git/commitdiff
ReceivePackStats: Add size and count of unnecessary pushed objects 04/160004/31
authorYunjie Li <yunjieli@google.com>
Wed, 25 Mar 2020 17:59:06 +0000 (10:59 -0700)
committerYunjie Li <yunjieli@google.com>
Fri, 11 Sep 2020 23:19:57 +0000 (16:19 -0700)
Since there is no negotiation for a push, the client is probably sending
redundant objects and bytes which already exist in the server.

Add more metrics in the stats to quantify it. Duplicated size and number
to measure the size and the number of duplicated objects which should
not be pushed.

Change-Id: Iaacd4761ee9366a0a7ec4e26c508eff45c8744de
Signed-off-by: Yunjie Li <yunjieli@google.com>
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java

index e9b4af932ea659aeedbb572e4c0b30b0517eb3f4..945900f14b8e584edaee57ca1368bf436e5473ca 100644 (file)
@@ -1059,6 +1059,67 @@ public class UploadPackTest {
                assertTrue(client.getObjectDatabase().has(tag.toObjectId()));
        }
 
+       @Test
+       public void testUploadNewBytes() throws Exception {
+               String commonInBlob = "abcdefghijklmnopqrstuvwx";
+
+               RevBlob parentBlob = remote.blob(commonInBlob + "a");
+               RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
+               RevBlob childBlob = remote.blob(commonInBlob + "b");
+               RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
+               remote.update("branch1", child);
+
+               ByteArrayInputStream recvStream = uploadPackV2(
+                       "command=fetch\n",
+                       PacketLineIn.delimiter(),
+                       "want " + child.toObjectId().getName() + "\n",
+                       "ofs-delta\n",
+                       "done\n",
+                               PacketLineIn.end());
+               PacketLineIn pckIn = new PacketLineIn(recvStream);
+               assertThat(pckIn.readString(), is("packfile"));
+               ReceivedPackStatistics receivedStats = parsePack(recvStream);
+               assertTrue(receivedStats.getNumBytesDuplicated() == 0);
+               assertTrue(receivedStats.getNumObjectsDuplicated() == 0);
+       }
+
+       @Test
+       public void testUploadRedundantBytes() throws Exception {
+               String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+               RevBlob parentBlob = remote.blob(commonInBlob + "a");
+               RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
+               RevBlob childBlob = remote.blob(commonInBlob + "b");
+               RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
+               remote.update("branch1", child);
+
+               TestRepository<InMemoryRepository> local = new TestRepository<>(client);
+               RevBlob localParentBlob = local.blob(commonInBlob + "a");
+               RevCommit localParent = local.commit(local.tree(local.file("foo", localParentBlob)));
+               RevBlob localChildBlob = local.blob(commonInBlob + "b");
+               RevCommit localChild = local.commit(
+                               local.tree(local.file("foo", localChildBlob)), localParent);
+               local.update("branch1", localChild);
+
+               ByteArrayInputStream recvStream = uploadPackV2(
+                               "command=fetch\n",
+                               PacketLineIn.delimiter(),
+                               "want " + child.toObjectId().getName() + "\n",
+                               "ofs-delta\n",
+                               "done\n",
+                                       PacketLineIn.end());
+               PacketLineIn pckIn = new PacketLineIn(recvStream);
+               assertThat(pckIn.readString(), is("packfile"));
+               ReceivedPackStatistics receivedStats = parsePack(recvStream);
+
+               long sizeOfHeader = 12;
+               long sizeOfTrailer = 20;
+               long expectedSize = receivedStats.getNumBytesRead() - sizeOfHeader
+                               - sizeOfTrailer;
+               assertTrue(receivedStats.getNumBytesDuplicated() == expectedSize);
+               assertTrue(receivedStats.getNumObjectsDuplicated() == 6);
+       }
+
        @Test
        public void testV2FetchOfsDelta() throws Exception {
                String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
index 0801b8a86ae25de5b1232d52bad02eaa727c02e9..715cbb48fb66a668b7b1f45249fbc0643ccae559 100644 (file)
@@ -679,7 +679,8 @@ public abstract class PackParser {
 
                        verifySafeObject(tempObjectId, type, visit.data);
                        if (isCheckObjectCollisions() && readCurs.has(tempObjectId)) {
-                               checkObjectCollision(tempObjectId, type, visit.data);
+                               checkObjectCollision(tempObjectId, type, visit.data,
+                                               visit.delta.sizeBeforeInflating);
                        }
 
                        PackedObjectInfo oe;
@@ -999,6 +1000,7 @@ public abstract class PackParser {
                        UnresolvedDelta n = onEndDelta();
                        n.position = streamPosition;
                        n.next = baseByPos.put(base, n);
+                       n.sizeBeforeInflating = streamPosition() - streamPosition;
                        deltaCount++;
                        break;
                }
@@ -1020,6 +1022,7 @@ public abstract class PackParser {
                        inflateAndSkip(Source.INPUT, sz);
                        UnresolvedDelta n = onEndDelta();
                        n.position = streamPosition;
+                       n.sizeBeforeInflating = streamPosition() - streamPosition;
                        r.add(n);
                        deltaCount++;
                        break;
@@ -1071,9 +1074,11 @@ public abstract class PackParser {
                        verifySafeObject(tempObjectId, type, data);
                }
 
+               long sizeBeforeInflating = streamPosition() - pos;
                PackedObjectInfo obj = newInfo(tempObjectId, null, null);
                obj.setOffset(pos);
                obj.setType(type);
+               obj.setSize(sizeBeforeInflating);
                onEndWholeObject(obj);
                if (data != null)
                        onInflatedObjectData(obj, type, data);
@@ -1148,6 +1153,8 @@ public abstract class PackParser {
                                        sz -= n;
                                }
                        }
+                       stats.incrementObjectsDuplicated();
+                       stats.incrementNumBytesDuplicated(obj.getSize());
                } catch (MissingObjectException notLocal) {
                        // This is OK, we don't have a copy of the object locally
                        // but the API throws when we try to read it as usually it's
@@ -1155,15 +1162,17 @@ public abstract class PackParser {
                }
        }
 
-       private void checkObjectCollision(AnyObjectId obj, int type, byte[] data)
-                       throws IOException {
+       private void checkObjectCollision(AnyObjectId obj, int type, byte[] data,
+                       long sizeBeforeInflating) throws IOException {
                try {
                        final ObjectLoader ldr = readCurs.open(obj, type);
                        final byte[] existingData = ldr.getCachedBytes(data.length);
                        if (!Arrays.equals(data, existingData)) {
-                               throw new IOException(MessageFormat.format(
-                                               JGitText.get().collisionOn, obj.name()));
+                               throw new IOException(MessageFormat
+                                               .format(JGitText.get().collisionOn, obj.name()));
                        }
+                       stats.incrementObjectsDuplicated();
+                       stats.incrementNumBytesDuplicated(sizeBeforeInflating);
                } catch (MissingObjectException notLocal) {
                        // This is OK, we don't have a copy of the object locally
                        // but the API throws when we try to read it as usually its
@@ -1653,6 +1662,8 @@ public abstract class PackParser {
 
                UnresolvedDelta next;
 
+               long sizeBeforeInflating;
+
                /** @return offset within the input stream. */
                public long getOffset() {
                        return position;
index fc906de2a8a43b011598eaa1bb436e8f6a18b862..fe1209b6af473c52fbe9a12fc9e9a554b189e352 100644 (file)
@@ -29,6 +29,8 @@ public class PackedObjectInfo extends ObjectIdOwnerMap.Entry {
 
        private int type = Constants.OBJ_BAD;
 
+       private long sizeBeforeInflating;
+
        PackedObjectInfo(final long headerOffset, final int packedCRC,
                        final AnyObjectId id) {
                super(id);
@@ -108,4 +110,12 @@ public class PackedObjectInfo extends ObjectIdOwnerMap.Entry {
        public void setType(int type) {
                this.type = type;
        }
+
+       void setSize(long sizeBeforeInflating) {
+               this.sizeBeforeInflating = sizeBeforeInflating;
+       }
+
+       long getSize() {
+               return sizeBeforeInflating;
+       }
 }
index bd8f5585d246c73e8123baad3192425f0403fe0b..d7bc40006bf45785d0e0fc15937760127c535127 100644 (file)
@@ -19,6 +19,7 @@ import org.eclipse.jgit.lib.Constants;
  */
 public class ReceivedPackStatistics {
        private long numBytesRead;
+       private long numBytesDuplicated;
 
        private long numWholeCommit;
        private long numWholeTree;
@@ -26,6 +27,7 @@ public class ReceivedPackStatistics {
        private long numWholeTag;
        private long numOfsDelta;
        private long numRefDelta;
+       private long numObjectsDuplicated;
 
        private long numDeltaCommit;
        private long numDeltaTree;
@@ -41,6 +43,17 @@ public class ReceivedPackStatistics {
                return numBytesRead;
        }
 
+       /**
+        * Get number of bytes of objects already in the local database
+        *
+        * @return number of bytes of objects appeared in both the pack sent by the
+        *         client and the local database
+        * @since 5.10
+        */
+       public long getNumBytesDuplicated() {
+               return numBytesDuplicated;
+       }
+
        /**
         * Get number of whole commit objects in the pack
         *
@@ -95,6 +108,17 @@ public class ReceivedPackStatistics {
                return numRefDelta;
        }
 
+       /**
+        * Get number of objects already in the local database
+        *
+        * @return number of objects appeared in both the pack sent by the client
+        *         and the local database
+        * @since 5.10
+        */
+       public long getNumObjectsDuplicated() {
+               return numObjectsDuplicated;
+       }
+
        /**
         * Get number of delta commit objects in the pack
         *
@@ -134,6 +158,7 @@ public class ReceivedPackStatistics {
        /** A builder for {@link ReceivedPackStatistics}. */
        public static class Builder {
                private long numBytesRead;
+               private long numBytesDuplicated;
 
                private long numWholeCommit;
                private long numWholeTree;
@@ -141,6 +166,7 @@ public class ReceivedPackStatistics {
                private long numWholeTag;
                private long numOfsDelta;
                private long numRefDelta;
+               private long numObjectsDuplicated;
 
                private long numDeltaCommit;
                private long numDeltaTree;
@@ -156,6 +182,17 @@ public class ReceivedPackStatistics {
                        return this;
                }
 
+               /**
+                * @param size
+                *            additional bytes already in the local database
+                * @return this
+                * @since 5.10
+                */
+               Builder incrementNumBytesDuplicated(long size) {
+                       numBytesDuplicated += size;
+                       return this;
+               }
+
                /**
                 * Increment a whole object count.
                 *
@@ -195,6 +232,17 @@ public class ReceivedPackStatistics {
                        return this;
                }
 
+               /**
+                * Increment the duplicated object count.
+                *
+                * @return this
+                * @since 5.10
+                */
+               Builder incrementObjectsDuplicated() {
+                       numObjectsDuplicated++;
+                       return this;
+               }
+
                /**
                 * Increment a delta object count.
                 *
@@ -226,6 +274,7 @@ public class ReceivedPackStatistics {
                ReceivedPackStatistics build() {
                        ReceivedPackStatistics s = new ReceivedPackStatistics();
                        s.numBytesRead = numBytesRead;
+                       s.numBytesDuplicated = numBytesDuplicated;
                        s.numWholeCommit = numWholeCommit;
                        s.numWholeTree = numWholeTree;
                        s.numWholeBlob = numWholeBlob;
@@ -236,6 +285,7 @@ public class ReceivedPackStatistics {
                        s.numDeltaTree = numDeltaTree;
                        s.numDeltaBlob = numDeltaBlob;
                        s.numDeltaTag = numDeltaTag;
+                       s.numObjectsDuplicated = numObjectsDuplicated;
                        return s;
                }
        }