Merge "UploadPack: Expose PackWriter activity to a logger"

This commit is contained in:
Chris Aniszczyk 2011-02-14 18:15:34 -05:00 committed by Code Review
commit 8235b88a4b
4 changed files with 272 additions and 5 deletions

View File

@ -575,14 +575,26 @@ public void writePack(ProgressMonitor compressMonitor,
stats.totalObjects = objCnt;
writeMonitor.beginTask(JGitText.get().writingObjects, (int) objCnt);
long writeStart = System.currentTimeMillis();
long headerStart = out.length();
out.writeFileHeader(PACK_VERSION_GENERATED, objCnt);
out.flush();
long headerEnd = out.length();
writeObjects(out);
if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty())
stats.thinPackBytes = out.length() - (headerEnd - headerStart);
for (CachedPack pack : cachedPacks) {
stats.reusedObjects += pack.getObjectCount();
reuseSupport.copyPackAsIs(out, pack);
}
writeChecksum(out);
out.flush();
stats.timeWriting = System.currentTimeMillis() - writeStart;
stats.totalBytes = out.length();
stats.reusedPacks = Collections.unmodifiableList(cachedPacks);
reader.release();
writeMonitor.endTask();
@ -739,9 +751,16 @@ public int compare(ObjectToPack a, ObjectToPack b) {
if (cnt == 0)
return;
final long searchStart = System.currentTimeMillis();
monitor.beginTask(JGitText.get().compressingObjects, nonEdgeCnt);
searchForDeltas(monitor, list, cnt);
monitor.endTask();
stats.deltaSearchNonEdgeObjects = nonEdgeCnt;
stats.timeCompressing = System.currentTimeMillis() - searchStart;
for (int i = 0; i < cnt; i++)
if (!list[i].isEdge() && list[i].isDeltaRepresentation())
stats.deltasFound++;
}
private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type) {
@ -1079,12 +1098,16 @@ private void findObjectsToPack(final ProgressMonitor countingMonitor,
Collection<? extends ObjectId> have)
throws MissingObjectException, IOException,
IncorrectObjectTypeException {
final long countingStart = System.currentTimeMillis();
countingMonitor.beginTask(JGitText.get().countingObjects,
ProgressMonitor.UNKNOWN);
if (have == null)
have = Collections.emptySet();
stats.interestingObjects = Collections.unmodifiableSet(new HashSet(want));
stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet(have));
List<ObjectId> all = new ArrayList<ObjectId>(want.size() + have.size());
all.addAll(want);
all.addAll(have);
@ -1227,6 +1250,7 @@ private void findObjectsToPack(final ProgressMonitor countingMonitor,
for (CachedPack pack : cachedPacks)
countingMonitor.update((int) pack.getObjectCount());
countingMonitor.endTask();
stats.timeCounting = System.currentTimeMillis() - countingStart;
}
private void pruneObjectList(int typesToPrune, int typeCode) {
@ -1380,6 +1404,16 @@ else if (PACK_DELTA < nFmt && otp.isDeltaRepresentation())
/** Summary of how PackWriter created the pack. */
public static class Statistics {
Set<ObjectId> interestingObjects;
Set<ObjectId> uninterestingObjects;
Collection<CachedPack> reusedPacks;
int deltaSearchNonEdgeObjects;
int deltasFound;
long totalObjects;
long totalDeltas;
@ -1388,6 +1422,150 @@ public static class Statistics {
long reusedDeltas;
long totalBytes;
long thinPackBytes;
long timeCounting;
long timeCompressing;
long timeWriting;
/**
* @return unmodifiable collection of objects to be included in the
* pack. May be null if the pack was hand-crafted in a unit
* test.
*/
public Set<ObjectId> getInterestingObjects() {
return interestingObjects;
}
/**
* @return unmodifiable collection of objects that should be excluded
* from the pack, as the peer that will receive the pack already
* has these objects.
*/
public Set<ObjectId> getUninterestingObjects() {
return uninterestingObjects;
}
/**
* @return unmodifiable collection of the cached packs that were reused
* in the output, if any were selected for reuse.
*/
public Collection<CachedPack> getReusedPacks() {
return reusedPacks;
}
/**
* @return number of objects in the output pack that went through the
* delta search process in order to find a potential delta base.
*/
public int getDeltaSearchNonEdgeObjects() {
return deltaSearchNonEdgeObjects;
}
/**
* @return number of objects in the output pack that went through delta
* base search and found a suitable base. This is a subset of
* {@link #getDeltaSearchNonEdgeObjects()}.
*/
public int getDeltasFound() {
return deltasFound;
}
/**
* @return total number of objects output. This total includes the value
* of {@link #getTotalDeltas()}.
*/
public long getTotalObjects() {
return totalObjects;
}
/**
* @return total number of deltas output. This may be lower than the
* actual number of deltas if a cached pack was reused.
*/
public long getTotalDeltas() {
return totalDeltas;
}
/**
* @return number of objects whose existing representation was reused in
* the output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
return reusedObjects;
}
/**
* @return number of deltas whose existing representation was reused in
* the output, as their base object was also output or was
* assumed present for a thin pack. This may be lower than the
* actual number of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
return reusedDeltas;
}
/**
* @return total number of bytes written. This size includes the pack
* header, trailer, thin pack, and reused cached pack(s).
*/
public long getTotalBytes() {
return totalBytes;
}
/**
* @return size of the thin pack in bytes, if a thin pack was generated.
* A thin pack is created when the client already has objects
* and some deltas are created against those objects, or if a
* cached pack is being used and some deltas will reference
* objects in the cached pack. This size does not include the
* pack header or trailer.
*/
public long getThinPackBytes() {
return thinPackBytes;
}
/**
* @return time in milliseconds spent enumerating the objects that need
* to be included in the output. This time includes any restarts
* that occur when a cached pack is selected for reuse.
*/
public long getTimeCounting() {
return timeCounting;
}
/**
* @return time in milliseconds spent on delta compression. This is
* observed wall-clock time and does not accurately track CPU
* time used when multiple threads were used to perform the
* delta compression.
*/
public long getTimeCompressing() {
return timeCompressing;
}
/**
* @return time in milliseconds spent writing the pack output, from
* start of header until end of trailer. The transfer speed can
* be approximated by dividing {@link #getTotalBytes()} by this
* value.
*/
public long getTimeWriting() {
return timeWriting;
}
/**
* @return get the average output speed in terms of bytes-per-second.
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
*/
public double getTransferRate() {
return getTotalBytes() / (getTimeWriting() / 1000.0);
}
/** @return formatted message string for display to clients. */
public String getMessage() {
return MessageFormat.format(JGitText.get().packWriterStatistics, //

View File

@ -246,7 +246,6 @@ private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
List<ObjectId> remoteObjects = new ArrayList<ObjectId>(getRefs().size());
List<ObjectId> newObjects = new ArrayList<ObjectId>(refUpdates.size());
final long start;
final PackWriter writer = new PackWriter(transport.getPackConfig(),
local.newObjectReader());
try {
@ -263,13 +262,11 @@ private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
writer.setThin(thinPack);
writer.setDeltaBaseAsOffset(capableOfsDelta);
writer.preparePack(monitor, newObjects, remoteObjects);
start = System.currentTimeMillis();
writer.writePack(monitor, monitor, out);
} finally {
writer.release();
}
out.flush();
packTransferTime = System.currentTimeMillis() - start;
packTransferTime = writer.getStatistics().getTimeWriting();
}
private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)

View File

@ -179,6 +179,10 @@ public class UploadPack {
private MultiAck multiAck = MultiAck.OFF;
private PackWriter.Statistics statistics;
private UploadPackLogger logger;
/**
* Create a new pack upload for an open repository.
*
@ -283,6 +287,16 @@ public void setPackConfig(PackConfig pc) {
this.packConfig = pc;
}
/**
* Set the logger.
*
* @param logger
* the logger instance. If null, no logging occurs.
*/
public void setLogger(UploadPackLogger logger) {
this.logger = logger;
}
/**
* Execute the upload task on the socket.
*
@ -332,6 +346,17 @@ public void upload(final InputStream input, final OutputStream output,
}
}
/**
* Get the PackWriter's statistics if a pack was sent to the client.
*
* @return statistics about pack output, if a pack was sent. Null if no pack
* was sent, such as during the negotation phase of a smart HTTP
* connection, or if the client was already up-to-date.
*/
public PackWriter.Statistics getPackStatistics() {
return statistics;
}
private void service() throws IOException {
if (biDirectionalPipe)
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@ -704,7 +729,7 @@ private void sendPack() throws IOException {
}
pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
packOut.flush();
statistics = pw.getStatistics();
if (msgOut != null) {
String msg = pw.getStatistics().getMessage() + '\n';
@ -718,5 +743,8 @@ private void sendPack() throws IOException {
if (sideband)
pckOut.end();
if (logger != null && statistics != null)
logger.onPackStatistics(statistics);
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2011, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.transport;
import org.eclipse.jgit.storage.pack.PackWriter;
/**
* Logs activity that occurred within {@link UploadPack}.
* <p>
* Implementors of the interface are responsible for associating the current
* thread to a particular connection, if they need to also include connection
* information. One method is to use a {@link java.lang.ThreadLocal} to remember
* the connection information before invoking UploadPack.
*/
public interface UploadPackLogger {
/**
* Notice to the logger after a pack has been sent.
*
* @param stats
* the statistics after sending a pack to the client.
*/
public void onPackStatistics(PackWriter.Statistics stats);
}