Browse Source

Measure stored timestamp resolution instead of time to touch file

Measure granularity of timestamps stored in the filesystem by setting
and then getting lastModified timestamp until the read value changed.
Increase increment exponentially to limit number of iterations starting
with 1 microsecond since Java's FileTime (up to Java 12) truncates
timestamps to 1 microsecond resolution. The chosen algorithm yields 2000
steps between 1 ms and 2.5 s.

Also measure clock resolution and add that for the total timestamp
resolution. This avoids systematic measurement errors introduced by
doing IO to touch a file.

Change-Id: I9b37138619422452373e298d9d8c7cb2c384db3f
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-5.1
Matthias Sohn 5 years ago
parent
commit
99d351d0cb
  1. 49
      org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java

49
org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java

@ -299,46 +299,45 @@ public abstract class FS {
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$ Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
try { try {
Files.createFile(probe); Files.createFile(probe);
// ensure we always use the local system clock
FileUtils.touch(probe);
long wait = 512;
long start = System.nanoTime();
FileTime t1 = Files.getLastModifiedTime(probe); FileTime t1 = Files.getLastModifiedTime(probe);
FileTime t2 = t1; FileTime t2 = t1;
while (t2.compareTo(t1) <= 0) { Instant t1i = t1.toInstant();
TimeUnit.NANOSECONDS.sleep(wait); for (long i = 1; t2.compareTo(t1) <= 0; i += 1 + i / 20) {
checkTimeout(s, start); Files.setLastModifiedTime(probe,
FileUtils.touch(probe); FileTime.from(t1i.plusNanos(i * 1000)));
t2 = Files.getLastModifiedTime(probe); t2 = Files.getLastModifiedTime(probe);
if (wait < 100_000_000L) {
wait = wait * 2;
}
} }
Duration resolution = Duration.between(t1.toInstant(), t2.toInstant()); Duration fsResolution = Duration.between(t1.toInstant(), t2.toInstant());
saveFileTimeResolution(s, resolution); Duration clockResolution = measureClockResolution();
return Optional.of(resolution); fsResolution = fsResolution.plus(clockResolution);
saveFileTimeResolution(s, fsResolution);
return Optional.of(fsResolution);
} catch (AccessDeniedException e) { } catch (AccessDeniedException e) {
LOG.warn(e.getLocalizedMessage(), e); // see bug 548648 LOG.warn(e.getLocalizedMessage(), e); // see bug 548648
} catch (IOException | TimeoutException e) { } catch (IOException e) {
LOG.error(e.getLocalizedMessage(), e);
} catch (InterruptedException e) {
LOG.error(e.getLocalizedMessage(), e); LOG.error(e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} finally { } finally {
deleteProbe(probe); deleteProbe(probe);
} }
return Optional.empty(); return Optional.empty();
} }
private static void checkTimeout(FileStore s, long start) private static Duration measureClockResolution() {
throws TimeoutException { Duration clockResolution = Duration.ZERO;
if (System.nanoTime() - start >= FALLBACK_TIMESTAMP_RESOLUTION for (int i = 0; i < 10; i++) {
.toNanos()) { Instant t1 = Instant.now();
throw new TimeoutException(MessageFormat.format(JGitText Instant t2 = t1;
.get().timeoutMeasureFsTimestampResolution, while (t2.compareTo(t1) <= 0) {
s.toString())); t2 = Instant.now();
}
Duration r = Duration.between(t1, t2);
if (r.compareTo(clockResolution) > 0) {
clockResolution = r;
}
} }
return clockResolution;
} }
private static void deleteProbe(Path probe) { private static void deleteProbe(Path probe) {
if (Files.exists(probe)) { if (Files.exists(probe)) {
try { try {

Loading…
Cancel
Save