Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn2020-01-09 01:19:03 +0000
committerMatthias Sohn2020-01-09 01:19:03 +0000
commitc5c1bbbe49313ee1e6ada9c0b7bfc02d27bcf5cd (patch)
tree2c8ad1dded18393bfe6e0d7f18637f08f1211761
parent839359425d619db31584955f4e0fb0d19ed4cfb8 (diff)
parent6f177b3f6cf5cea4086be35c0d5ceeb73694b9c0 (diff)
downloadjgit-c5c1bbbe49313ee1e6ada9c0b7bfc02d27bcf5cd.tar.gz
jgit-c5c1bbbe49313ee1e6ada9c0b7bfc02d27bcf5cd.tar.xz
jgit-c5c1bbbe49313ee1e6ada9c0b7bfc02d27bcf5cd.zip
Merge branch 'stable-5.2' into stable-5.3
* stable-5.2: Fix API problem filters Fix unclosed resource warning in SmartOutputStream JschConfigSessionFactory: fix boxing warning SshSupport#runSshCommand: don't throw exception in finally block Don't override already managed maven-compiler-plugin version Remove unused import from CreateFileSnapshotBenchmark Remove duplicate ignore_optional_problems entry in .classpath Update maven-site-plugin used by benchmark module to 3.8.2 Add dependency to enable site generation for benchmark module Ignore warnings for generated source code in org.eclipse.jgit.benchmark Fix MBean registration Enhance WindowCache statistics Change-Id: I67a07d92718188bdf7f8a13b83e9f538ecf4b22f Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.jgit.benchmarks/pom.xml27
-rw-r--r--org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/CreateFileSnapshotBenchmark.java1
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java31
-rw-r--r--org.eclipse.jgit.lfs.server/.settings/.api_filters8
-rw-r--r--org.eclipse.jgit.lfs/.settings/.api_filters11
-rw-r--r--org.eclipse.jgit.packaging/pom.xml2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java43
-rw-r--r--org.eclipse.jgit/.settings/.api_filters23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java258
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java176
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java14
-rw-r--r--pom.xml2
14 files changed, 606 insertions, 85 deletions
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index 9d175754fd..b91a141795 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -189,6 +189,33 @@
</lifecycleMappingMetadata>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>3.8.2</version>
+ <dependencies>
+ <dependency><!-- add support for ssh/scp -->
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh</artifactId>
+ <version>3.3.4</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>3.0.0-M3</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>3.0.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>3.0.0</version>
+ </plugin>
</plugins>
</pluginManagement>
</build>
diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/CreateFileSnapshotBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/CreateFileSnapshotBenchmark.java
index ffe4a26d8b..21c54c563f 100644
--- a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/CreateFileSnapshotBenchmark.java
+++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/CreateFileSnapshotBenchmark.java
@@ -45,7 +45,6 @@ package org.eclipse.jgit.benchmarks;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.internal.storage.file.FileSnapshot;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
index 06bdce679d..0dfaec2567 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
@@ -105,18 +105,16 @@ class SmartOutputStream extends TemporaryBuffer {
// If output hasn't started yet, the entire thing fit into our
// buffer. Try to use a proper Content-Length header, and also
// deflate the response with gzip if it will be smaller.
- @SuppressWarnings("resource")
- TemporaryBuffer out = this;
-
- if (256 < out.length() && acceptsGzipEncoding(req)) {
+ if (256 < this.length() && acceptsGzipEncoding(req)) {
TemporaryBuffer gzbuf = new TemporaryBuffer.Heap(LIMIT);
try {
try (GZIPOutputStream gzip = new GZIPOutputStream(gzbuf)) {
- out.writeTo(gzip, null);
+ this.writeTo(gzip, null);
}
- if (gzbuf.length() < out.length()) {
- out = gzbuf;
+ if (gzbuf.length() < this.length()) {
rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP);
+ writeResponse(gzbuf);
+ return;
}
} catch (IOException err) {
// Most likely caused by overflowing the buffer, meaning
@@ -124,15 +122,18 @@ class SmartOutputStream extends TemporaryBuffer {
// copy and use the original.
}
}
+ writeResponse(this);
+ }
+ }
- // The Content-Length cannot overflow when cast to an int, our
- // hardcoded LIMIT constant above assures us we wouldn't store
- // more than 2 GiB of content in memory.
- rsp.setContentLength((int) out.length());
- try (OutputStream os = rsp.getOutputStream()) {
- out.writeTo(os, null);
- os.flush();
- }
+ private void writeResponse(TemporaryBuffer out) throws IOException {
+ // The Content-Length cannot overflow when cast to an int, our
+ // hardcoded LIMIT constant above assures us we wouldn't store
+ // more than 2 GiB of content in memory.
+ rsp.setContentLength((int) out.length());
+ try (OutputStream os = rsp.getOutputStream()) {
+ out.writeTo(os, null);
+ os.flush();
}
}
}
diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters
index 3e60680cf7..6609c3d40d 100644
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters
@@ -1,13 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit.lfs.server" version="2">
- <resource path="META-INF/MANIFEST.MF">
- <filter id="924844039">
- <message_arguments>
- <message_argument value="5.2.3"/>
- <message_argument value="5.2.0"/>
- </message_arguments>
- </filter>
- </resource>
<resource path="src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java" type="org.eclipse.jgit.lfs.server.fs.ObjectUploadListener">
<filter id="1142947843">
<message_arguments>
diff --git a/org.eclipse.jgit.lfs/.settings/.api_filters b/org.eclipse.jgit.lfs/.settings/.api_filters
deleted file mode 100644
index 95a45574ec..0000000000
--- a/org.eclipse.jgit.lfs/.settings/.api_filters
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.lfs" version="2">
- <resource path="META-INF/MANIFEST.MF">
- <filter id="924844039">
- <message_arguments>
- <message_argument value="5.2.3"/>
- <message_argument value="5.2.0"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index a9f4885874..fa57f7d87c 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -279,7 +279,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>3.7.1</version>
+ <version>3.8.2</version>
</plugin>
</plugins>
</pluginManagement>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
index 9063b65189..f1a18b096c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
@@ -61,6 +61,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
+import org.eclipse.jgit.storage.file.WindowCacheStats;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.MutableInteger;
import org.junit.Before;
@@ -102,8 +103,21 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
checkLimits(cfg);
final WindowCache cache = WindowCache.getInstance();
- assertEquals(6, cache.getOpenFiles());
- assertEquals(17346, cache.getOpenBytes());
+ WindowCacheStats s = cache.getStats();
+ assertEquals(6, s.getOpenFileCount());
+ assertEquals(17346, s.getOpenByteCount());
+ assertEquals(0, s.getEvictionCount());
+ assertEquals(90, s.getHitCount());
+ assertTrue(s.getHitRatio() > 0.0 && s.getHitRatio() < 1.0);
+ assertEquals(6, s.getLoadCount());
+ assertEquals(0, s.getLoadFailureCount());
+ assertEquals(0, s.getLoadFailureRatio(), 0.001);
+ assertEquals(6, s.getLoadSuccessCount());
+ assertEquals(6, s.getMissCount());
+ assertTrue(s.getMissRatio() > 0.0 && s.getMissRatio() < 1.0);
+ assertEquals(96, s.getRequestCount());
+ assertTrue(s.getAverageLoadTime() > 0.0);
+ assertTrue(s.getTotalLoadTime() > 0.0);
}
@Test
@@ -127,10 +141,27 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
private static void checkLimits(WindowCacheConfig cfg) {
final WindowCache cache = WindowCache.getInstance();
- assertTrue(cache.getOpenFiles() <= cfg.getPackedGitOpenFiles());
- assertTrue(cache.getOpenBytes() <= cfg.getPackedGitLimit());
- assertTrue(0 < cache.getOpenFiles());
- assertTrue(0 < cache.getOpenBytes());
+ WindowCacheStats s = cache.getStats();
+ assertTrue(0 < s.getAverageLoadTime());
+ assertTrue(0 < s.getOpenByteCount());
+ assertTrue(0 < s.getOpenByteCount());
+ assertTrue(0.0 < s.getAverageLoadTime());
+ assertTrue(0 <= s.getEvictionCount());
+ assertTrue(0 < s.getHitCount());
+ assertTrue(0 < s.getHitRatio());
+ assertTrue(1 > s.getHitRatio());
+ assertTrue(0 < s.getLoadCount());
+ assertTrue(0 <= s.getLoadFailureCount());
+ assertTrue(0.0 <= s.getLoadFailureRatio());
+ assertTrue(1 > s.getLoadFailureRatio());
+ assertTrue(0 < s.getLoadSuccessCount());
+ assertTrue(s.getOpenByteCount() <= cfg.getPackedGitLimit());
+ assertTrue(s.getOpenFileCount() <= cfg.getPackedGitOpenFiles());
+ assertTrue(0 <= s.getMissCount());
+ assertTrue(0 <= s.getMissRatio());
+ assertTrue(1 > s.getMissRatio());
+ assertTrue(0 < s.getRequestCount());
+ assertTrue(0 < s.getTotalLoadTime());
}
private void doCacheTests() throws IOException {
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 14856a2c8c..abdf350044 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -37,6 +37,12 @@
<resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
<filter id="1142947843">
<message_arguments>
+ <message_argument value="5.1.13"/>
+ <message_argument value="CONFIG_JMX_SECTION"/>
+ </message_arguments>
+ </filter>
+ <filter id="1142947843">
+ <message_arguments>
<message_argument value="5.1.9"/>
<message_argument value="CONFIG_FILESYSTEM_SECTION"/>
</message_arguments>
@@ -84,6 +90,15 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/storage/file/WindowCacheStats.java" type="org.eclipse.jgit.storage.file.WindowCacheStats">
+ <filter id="337809484">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.storage.file.WindowCacheStats"/>
+ <message_argument value="4"/>
+ <message_argument value="8"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
<filter id="336658481">
<message_arguments>
@@ -276,6 +291,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/util/Monitoring.java" type="org.eclipse.jgit.util.Monitoring">
+ <filter id="1109393411">
+ <message_arguments>
+ <message_argument value="5.1.13"/>
+ <message_argument value="org.eclipse.jgit.util.Monitoring"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/util/SimpleLruCache.java" type="org.eclipse.jgit.util.SimpleLruCache">
<filter id="1109393411">
<message_arguments>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index e8fac514be..015866e101 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -48,13 +48,16 @@ import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantLock;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
+import org.eclipse.jgit.storage.file.WindowCacheStats;
+import org.eclipse.jgit.util.Monitoring;
/**
* Caches slices of a {@link org.eclipse.jgit.internal.storage.file.PackFile} in
@@ -124,6 +127,196 @@ import org.eclipse.jgit.storage.file.WindowCacheConfig;
* other threads.
*/
public class WindowCache {
+
+ /**
+ * Record statistics for a cache
+ */
+ static interface StatsRecorder {
+ /**
+ * Record cache hits. Called when cache returns a cached entry.
+ *
+ * @param count
+ * number of cache hits to record
+ */
+ void recordHits(int count);
+
+ /**
+ * Record cache misses. Called when the cache returns an entry which had
+ * to be loaded.
+ *
+ * @param count
+ * number of cache misses to record
+ */
+ void recordMisses(int count);
+
+ /**
+ * Record a successful load of a cache entry
+ *
+ * @param loadTimeNanos
+ * time to load a cache entry
+ */
+ void recordLoadSuccess(long loadTimeNanos);
+
+ /**
+ * Record a failed load of a cache entry
+ *
+ * @param loadTimeNanos
+ * time used trying to load a cache entry
+ */
+ void recordLoadFailure(long loadTimeNanos);
+
+ /**
+ * Record cache evictions due to the cache evictions strategy
+ *
+ * @param count
+ * number of evictions to record
+ */
+ void recordEvictions(int count);
+
+ /**
+ * Record files opened by cache
+ *
+ * @param count
+ * delta of number of files opened by cache
+ */
+ void recordOpenFiles(int count);
+
+ /**
+ * Record cached bytes
+ *
+ * @param count
+ * delta of cached bytes
+ */
+ void recordOpenBytes(int count);
+
+ /**
+ * Returns a snapshot of this recorder's stats. Note that this may be an
+ * inconsistent view, as it may be interleaved with update operations.
+ *
+ * @return a snapshot of this recorder's stats
+ */
+ @NonNull
+ WindowCacheStats getStats();
+ }
+
+ static class StatsRecorderImpl
+ implements StatsRecorder, WindowCacheStats {
+ private final LongAdder hitCount;
+ private final LongAdder missCount;
+ private final LongAdder loadSuccessCount;
+ private final LongAdder loadFailureCount;
+ private final LongAdder totalLoadTime;
+ private final LongAdder evictionCount;
+ private final LongAdder openFileCount;
+ private final LongAdder openByteCount;
+
+ /**
+ * Constructs an instance with all counts initialized to zero.
+ */
+ public StatsRecorderImpl() {
+ hitCount = new LongAdder();
+ missCount = new LongAdder();
+ loadSuccessCount = new LongAdder();
+ loadFailureCount = new LongAdder();
+ totalLoadTime = new LongAdder();
+ evictionCount = new LongAdder();
+ openFileCount = new LongAdder();
+ openByteCount = new LongAdder();
+ }
+
+ @Override
+ public void recordHits(int count) {
+ hitCount.add(count);
+ }
+
+ @Override
+ public void recordMisses(int count) {
+ missCount.add(count);
+ }
+
+ @Override
+ public void recordLoadSuccess(long loadTimeNanos) {
+ loadSuccessCount.increment();
+ totalLoadTime.add(loadTimeNanos);
+ }
+
+ @Override
+ public void recordLoadFailure(long loadTimeNanos) {
+ loadFailureCount.increment();
+ totalLoadTime.add(loadTimeNanos);
+ }
+
+ @Override
+ public void recordEvictions(int count) {
+ evictionCount.add(count);
+ }
+
+ @Override
+ public void recordOpenFiles(int count) {
+ openFileCount.add(count);
+ }
+
+ @Override
+ public void recordOpenBytes(int count) {
+ openByteCount.add(count);
+ }
+
+ @Override
+ public WindowCacheStats getStats() {
+ return this;
+ }
+
+ @Override
+ public long getHitCount() {
+ return hitCount.sum();
+ }
+
+ @Override
+ public long getMissCount() {
+ return missCount.sum();
+ }
+
+ @Override
+ public long getLoadSuccessCount() {
+ return loadSuccessCount.sum();
+ }
+
+ @Override
+ public long getLoadFailureCount() {
+ return loadFailureCount.sum();
+ }
+
+ @Override
+ public long getEvictionCount() {
+ return evictionCount.sum();
+ }
+
+ @Override
+ public long getTotalLoadTime() {
+ return totalLoadTime.sum();
+ }
+
+ @Override
+ public long getOpenFileCount() {
+ return openFileCount.sum();
+ }
+
+ @Override
+ public long getOpenByteCount() {
+ return openByteCount.sum();
+ }
+
+ @Override
+ public void resetCounters() {
+ hitCount.reset();
+ missCount.reset();
+ loadSuccessCount.reset();
+ loadFailureCount.reset();
+ totalLoadTime.reset();
+ evictionCount.reset();
+ }
+ }
+
private static final int bits(int newSize) {
if (newSize < 4096)
throw new IllegalArgumentException(JGitText.get().invalidWindowSize);
@@ -228,9 +421,9 @@ public class WindowCache {
private final int windowSize;
- private final AtomicInteger openFiles;
+ private final StatsRecorder statsRecorder;
- private final AtomicLong openBytes;
+ private final StatsRecorderImpl mbean;
private WindowCache(WindowCacheConfig cfg) {
tableSize = tableSize(cfg);
@@ -263,8 +456,9 @@ public class WindowCache {
windowSizeShift = bits(cfg.getPackedGitWindowSize());
windowSize = 1 << windowSizeShift;
- openFiles = new AtomicInteger();
- openBytes = new AtomicLong();
+ mbean = new StatsRecorderImpl();
+ statsRecorder = mbean;
+ Monitoring.registerMBean(mbean, "block_cache"); //$NON-NLS-1$
if (maxFiles < 1)
throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1);
@@ -273,61 +467,63 @@ public class WindowCache {
}
/**
- * @return the number of open files.
+ * @return cache statistics for the WindowCache
*/
- public int getOpenFiles() {
- return openFiles.get();
+ public WindowCacheStats getStats() {
+ return statsRecorder.getStats();
}
/**
- * @return the number of open bytes.
+ * Reset stats. Does not reset open bytes and open files stats.
*/
- public long getOpenBytes() {
- return openBytes.get();
+ public void resetStats() {
+ mbean.resetCounters();
}
private int hash(int packHash, long off) {
return packHash + (int) (off >>> windowSizeShift);
}
- private ByteWindow load(PackFile pack, long offset)
- throws IOException {
+ private ByteWindow load(PackFile pack, long offset) throws IOException {
+ long startTime = System.nanoTime();
if (pack.beginWindowCache())
- openFiles.incrementAndGet();
+ statsRecorder.recordOpenFiles(1);
try {
if (mmap)
return pack.mmap(offset, windowSize);
- return pack.read(offset, windowSize);
- } catch (IOException e) {
- close(pack);
- throw e;
- } catch (RuntimeException e) {
- close(pack);
- throw e;
- } catch (Error e) {
+ ByteArrayWindow w = pack.read(offset, windowSize);
+ statsRecorder.recordLoadSuccess(System.nanoTime() - startTime);
+ return w;
+ } catch (IOException | RuntimeException | Error e) {
close(pack);
+ statsRecorder.recordLoadFailure(System.nanoTime() - startTime);
throw e;
+ } finally {
+ statsRecorder.recordMisses(1);
}
}
private Ref createRef(PackFile p, long o, ByteWindow v) {
final Ref ref = new Ref(p, o, v, queue);
- openBytes.addAndGet(ref.size);
+ statsRecorder.recordOpenBytes(ref.size);
return ref;
}
private void clear(Ref ref) {
- openBytes.addAndGet(-ref.size);
+ statsRecorder.recordOpenBytes(-ref.size);
+ statsRecorder.recordEvictions(1);
close(ref.pack);
}
private void close(PackFile pack) {
- if (pack.endWindowCache())
- openFiles.decrementAndGet();
+ if (pack.endWindowCache()) {
+ statsRecorder.recordOpenFiles(-1);
+ }
}
private boolean isFull() {
- return maxFiles < openFiles.get() || maxBytes < openBytes.get();
+ return maxFiles < mbean.getOpenFileCount()
+ || maxBytes < mbean.getOpenByteCount();
}
private long toStart(long offset) {
@@ -365,15 +561,19 @@ public class WindowCache {
final int slot = slot(pack, position);
final Entry e1 = table.get(slot);
ByteWindow v = scan(e1, pack, position);
- if (v != null)
+ if (v != null) {
+ statsRecorder.recordHits(1);
return v;
+ }
synchronized (lock(pack, position)) {
Entry e2 = table.get(slot);
if (e2 != e1) {
v = scan(e2, pack, position);
- if (v != null)
+ if (v != null) {
+ statsRecorder.recordHits(1);
return v;
+ }
}
v = load(pack, position);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 5ae9d41db2..7877391854 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -495,4 +495,10 @@ public final class ConfigConstants {
* @since 5.1.9
*/
public static final String CONFIG_KEY_MIN_RACY_THRESHOLD = "minRacyThreshold";
+
+ /**
+ * The "jmx" section
+ * @since 5.1.13
+ */
+ public static final String CONFIG_JMX_SECTION = "jmx";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
index 3570733e41..b7f6394df6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
@@ -40,29 +40,193 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.eclipse.jgit.storage.file;
+import javax.management.MXBean;
+
import org.eclipse.jgit.internal.storage.file.WindowCache;
/**
- * Accessor for stats about {@link WindowCache}.
+ * Cache statistics for {@link WindowCache}.
*
* @since 4.11
- *
*/
-public class WindowCacheStats {
+@MXBean
+public interface WindowCacheStats {
/**
* @return the number of open files.
+ * @deprecated use {@link #getOpenFileCount()} instead
*/
+ @Deprecated
public static int getOpenFiles() {
- return WindowCache.getInstance().getOpenFiles();
+ return (int) WindowCache.getInstance().getStats().getOpenFileCount();
}
/**
* @return the number of open bytes.
+ * @deprecated use {@link #getOpenByteCount()} instead
*/
+ @Deprecated
public static long getOpenBytes() {
- return WindowCache.getInstance().getOpenBytes();
+ return WindowCache.getInstance().getStats().getOpenByteCount();
+ }
+
+ /**
+ * @return cache statistics for the WindowCache
+ * @since 5.1.13
+ */
+ public static WindowCacheStats getStats() {
+ return WindowCache.getInstance().getStats();
+ }
+
+ /**
+ * Number of cache hits
+ *
+ * @return number of cache hits
+ */
+ long getHitCount();
+
+ /**
+ * Ratio of cache requests which were hits defined as
+ * {@code hitCount / requestCount}, or {@code 1.0} when
+ * {@code requestCount == 0}. Note that {@code hitRate + missRate =~ 1.0}.
+ *
+ * @return the ratio of cache requests which were hits
+ */
+ default double getHitRatio() {
+ long requestCount = getRequestCount();
+ return (requestCount == 0) ? 1.0
+ : (double) getHitCount() / requestCount;
+ }
+
+ /**
+ * Number of cache misses.
+ *
+ * @return number of cash misses
+ */
+ long getMissCount();
+
+ /**
+ * Ratio of cache requests which were misses defined as
+ * {@code missCount / requestCount}, or {@code 0.0} when
+ * {@code requestCount == 0}. Note that {@code hitRate + missRate =~ 1.0}.
+ * Cache misses include all requests which weren't cache hits, including
+ * requests which resulted in either successful or failed loading attempts.
+ *
+ * @return the ratio of cache requests which were misses
+ */
+ default double getMissRatio() {
+ long requestCount = getRequestCount();
+ return (requestCount == 0) ? 0.0
+ : (double) getMissCount() / requestCount;
+ }
+
+ /**
+ * Number of successful loads
+ *
+ * @return number of successful loads
+ */
+ long getLoadSuccessCount();
+
+ /**
+ * Number of failed loads
+ *
+ * @return number of failed loads
+ */
+ long getLoadFailureCount();
+
+ /**
+ * Ratio of cache load attempts which threw exceptions. This is defined as
+ * {@code loadFailureCount / (loadSuccessCount + loadFailureCount)}, or
+ * {@code 0.0} when {@code loadSuccessCount + loadFailureCount == 0}.
+ *
+ * @return the ratio of cache loading attempts which threw exceptions
+ */
+ default double getLoadFailureRatio() {
+ long loadFailureCount = getLoadFailureCount();
+ long totalLoadCount = getLoadSuccessCount() + loadFailureCount;
+ return (totalLoadCount == 0) ? 0.0
+ : (double) loadFailureCount / totalLoadCount;
}
+
+ /**
+ * Total number of times that the cache attempted to load new values. This
+ * includes both successful load operations, as well as failed loads. This
+ * is defined as {@code loadSuccessCount + loadFailureCount}.
+ *
+ * @return the {@code loadSuccessCount + loadFailureCount}
+ */
+ default long getLoadCount() {
+ return getLoadSuccessCount() + getLoadFailureCount();
+ }
+
+ /**
+ * Number of cache evictions
+ *
+ * @return number of evictions
+ */
+ long getEvictionCount();
+
+ /**
+ * Ratio of cache evictions. This is defined as
+ * {@code evictionCount / requestCount}, or {@code 0.0} when
+ * {@code requestCount == 0}.
+ *
+ * @return the ratio of cache loading attempts which threw exceptions
+ */
+ default double getEvictionRatio() {
+ long evictionCount = getEvictionCount();
+ long requestCount = getRequestCount();
+ return (requestCount == 0) ? 0.0
+ : (double) evictionCount / requestCount;
+ }
+
+ /**
+ * Number of times the cache returned either a cached or uncached value.
+ * This is defined as {@code hitCount + missCount}.
+ *
+ * @return the {@code hitCount + missCount}
+ */
+ default long getRequestCount() {
+ return getHitCount() + getMissCount();
+ }
+
+ /**
+ * Average time in nanoseconds for loading new values. This is
+ * {@code totalLoadTime / (loadSuccessCount + loadFailureCount)}.
+ *
+ * @return the average time spent loading new values
+ */
+ default double getAverageLoadTime() {
+ long totalLoadCount = getLoadSuccessCount() + getLoadFailureCount();
+ return (totalLoadCount == 0) ? 0.0
+ : (double) getTotalLoadTime() / totalLoadCount;
+ }
+
+ /**
+ * Total time in nanoseconds the cache spent loading new values.
+ *
+ * @return the total number of nanoseconds the cache has spent loading new
+ * values
+ */
+ long getTotalLoadTime();
+
+ /**
+ * Number of pack files kept open by the cache
+ *
+ * @return number of files kept open by cache
+ */
+ long getOpenFileCount();
+
+ /**
+ * Number of bytes cached
+ *
+ * @return number of bytes cached
+ */
+ long getOpenByteCount();
+
+ /**
+ * Reset counters. Does not reset open bytes and open files counters.
+ */
+ void resetCounters();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java
new file mode 100644
index 0000000000..83bf695f70
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019 Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Enables monitoring JGit via JMX
+ *
+ * @since 5.1.13
+ */
+public class Monitoring {
+ private final static Logger LOG = LoggerFactory.getLogger(Monitoring.class);
+
+ /**
+ * Register a MBean with the platform MBean server
+ *
+ * @param mbean
+ * the mbean object to register
+ * @param metricName
+ * name of the JGit metric, will be prefixed with
+ * "org.eclipse.jgit/"
+ * @return the registered mbean's object instance
+ */
+ public static @Nullable ObjectInstance registerMBean(Object mbean,
+ String metricName) {
+ boolean register = false;
+ try {
+ Class<?> interfaces[] = mbean.getClass().getInterfaces();
+ for (Class<?> i : interfaces) {
+ register = SystemReader.getInstance().getUserConfig()
+ .getBoolean(
+ ConfigConstants.CONFIG_JMX_SECTION,
+ i.getSimpleName(), false);
+ if (register) {
+ break;
+ }
+ }
+ } catch (IOException | ConfigInvalidException e) {
+ LOG.error(e.getMessage(), e);
+ return null;
+ }
+ if (!register) {
+ return null;
+ }
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ try {
+ ObjectName mbeanName = objectName(mbean.getClass(), metricName);
+ if (server.isRegistered(mbeanName)) {
+ server.unregisterMBean(mbeanName);
+ }
+ return server.registerMBean(mbean, mbeanName);
+ } catch (MalformedObjectNameException | InstanceAlreadyExistsException
+ | MBeanRegistrationException | NotCompliantMBeanException
+ | InstanceNotFoundException e) {
+ LOG.error(e.getMessage(), e);
+ return null;
+ }
+ }
+
+ private static ObjectName objectName(Class mbean, String metricName)
+ throws MalformedObjectNameException {
+ return new ObjectName(String.format("org.eclipse.jgit/%s:type=%s", //$NON-NLS-1$
+ metricName, mbean.getSimpleName()));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
index 913aa72867..ac5be1bf20 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
@@ -94,6 +94,7 @@ public class SshSupport {
CommandFailedException failure = null;
@SuppressWarnings("resource")
MessageWriter stderr = new MessageWriter();
+ String out;
try (MessageWriter stdout = new MessageWriter()) {
session = SshSessionFactory.getInstance().getSession(sshUri,
provider, fs, 1000 * timeout);
@@ -108,12 +109,12 @@ public class SshSupport {
// waitFor with timeout has a bug - JSch' exitValue() throws the
// wrong exception type :(
if (process.waitFor() == 0) {
- return stdout.toString();
+ out = stdout.toString();
} else {
- return null; // still running after timeout
+ out = null; // still running after timeout
}
} catch (InterruptedException e) {
- return null; // error
+ out = null; // error
}
} finally {
if (errorThread != null) {
@@ -147,10 +148,11 @@ public class SshSupport {
if (session != null) {
SshSessionFactory.getInstance().releaseSession(session);
}
- if (failure != null) {
- throw failure;
- }
}
+ if (failure != null) {
+ throw failure;
+ }
+ return out;
}
}
diff --git a/pom.xml b/pom.xml
index c839617dda..bd81c5a516 100644
--- a/pom.xml
+++ b/pom.xml
@@ -836,7 +836,6 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <version>${maven-compiler-plugin-version}</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
@@ -905,7 +904,6 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <version>${maven-compiler-plugin-version}</version>
<configuration>
<compilerId>eclipse</compilerId>
<encoding>UTF-8</encoding>

Back to the top