diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2019-05-05 03:18:23 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2019-05-22 08:13:22 +0200 |
commit | b513b7747713a505e19e237ac2e7f8d9c699bc4d (patch) | |
tree | 9664e6d59d3c5f185c9257ae218fd8ed1ef49eb7 /org.eclipse.jgit.test | |
parent | 201bbd6eadabef7f386fa936a505b9312519ab4e (diff) | |
download | jgit-b513b7747713a505e19e237ac2e7f8d9c699bc4d.tar.gz jgit-b513b7747713a505e19e237ac2e7f8d9c699bc4d.zip |
Measure file timestamp resolution used in FileSnapshot
FileSnapshot.notRacyClean() assumed a worst case filesystem timestamp
resolution of 2.5 sec (FAT has a resolution of 2 sec). Instead measure
timestamp resolution to avoid unnecessary IO caused by false positives
in detecting the racy git problem caused by finite filesystem timestamp
resolution [1].
Cache the measured resolution per FileStore since timestamp resolution
depends on the respective filesystem type. If timestamp resolution
cannot be measured or fails due to an exception fallback to the worst
case FAT timestamp resolution and avoid caching this value.
Add a 10% safety margin in FileSnapshot.notRacyClean(), though running
FsTest.testFsTimestampResolution() 1000 times which is not using a
safety margin didn't fail on Mac using APFS and Java 8, 11, 12.
Measured Java file timestamp resolution: [2]
[1] https://github.com/git/git/blob/master/Documentation/technical/racy-git.txt
[2] https://docs.google.com/spreadsheets/d/1imy0y6WmRqBf0kjCxzxj2X7M50eIVfa7oaUIzEOHmjo
Bug: 546891
Change-Id: I493f3b57b6b306285ffa7d392339d253e5966ab8
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.test')
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java index 2c8273d03c..59c8e31c03 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java @@ -52,9 +52,16 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFilePermission; +import java.time.Duration; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.eclipse.jgit.errors.CommandFailedException; import org.eclipse.jgit.junit.RepositoryTestCase; @@ -186,4 +193,34 @@ public class FSTest { new String[] { "this-command-does-not-exist" }, Charset.defaultCharset().name()); } + + @Test + public void testFsTimestampResolution() throws Exception { + DateTimeFormatter formatter = DateTimeFormatter + .ofPattern("uuuu-MMM-dd HH:mm:ss.nnnnnnnnn", Locale.ENGLISH) + .withZone(ZoneId.systemDefault()); + Path dir = Files.createTempDirectory("probe-filesystem"); + Duration resolution = FS.getFsTimerResolution(dir); + long resolutionNs = resolution.toNanos(); + assertTrue(resolutionNs > 0); + for (int i = 0; i < 10; i++) { + Path f = null; + try { + f = dir.resolve("testTimestampResolution" + i); + Files.createFile(f); + FileUtils.touch(f); + FileTime t1 = Files.getLastModifiedTime(f); + TimeUnit.NANOSECONDS.sleep(resolutionNs); + FileUtils.touch(f); + FileTime t2 = Files.getLastModifiedTime(f); + assertTrue(String.format( + "expected t2=%s to be larger than t1=%s\nsince file timestamp resolution was measured to be %,d ns", + formatter.format(t2.toInstant()), + formatter.format(t1.toInstant()), + Long.valueOf(resolutionNs)), t2.compareTo(t1) > 0); + } finally { + Files.delete(f); + } + } + } } |