Add debug trace for FileSnapshot

Checking lastModified is time critical hence debug trace is the only way
to analyze issues since debugging is impractical.

Also add configuration for buffering of log4j output to reduce runtime
impact when debug trace is on. Limit buffer to 1MiB and comment this
configuration out since we may not always want to use buffering.

Change-Id: Ib1a0537b67c8dc3fac994a77b42badd974ce6c97
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Matthias Sohn 2019-07-10 10:19:01 +02:00
parent 8a0ed8a004
commit 09fec1a102
2 changed files with 51 additions and 12 deletions

View File

@ -7,3 +7,5 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#log4j.appender.fileLogger.bufferedIO = true
#log4j.appender.fileLogger.bufferSize = 1024

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.internal.storage.file; package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.lib.Constants.FALLBACK_TIMESTAMP_RESOLUTION; import static org.eclipse.jgit.lib.Constants.FALLBACK_TIMESTAMP_RESOLUTION;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
@ -57,6 +58,8 @@
import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Caches when a file was last read, making it possible to detect future edits. * Caches when a file was last read, making it possible to detect future edits.
@ -75,6 +78,8 @@
* file is less than 3 seconds ago. * file is less than 3 seconds ago.
*/ */
public class FileSnapshot { public class FileSnapshot {
private static final Logger LOG = LoggerFactory
.getLogger(FileSnapshot.class);
/** /**
* An unknown file size. * An unknown file size.
* *
@ -188,17 +193,19 @@ public static FileSnapshot save(long modified) {
*/ */
private final Object fileKey; private final Object fileKey;
private final File file;
/** /**
* Record a snapshot for a specific file path. * Record a snapshot for a specific file path.
* <p> * <p>
* This method should be invoked before the file is accessed. * This method should be invoked before the file is accessed.
* *
* @param path * @param file
* the path to remember meta data for. The path's current status * the path to remember meta data for. The path's current status
* information is saved. * information is saved.
*/ */
protected FileSnapshot(File path) { protected FileSnapshot(File file) {
this(path, true); this(file, true);
} }
/** /**
@ -206,30 +213,35 @@ protected FileSnapshot(File path) {
* <p> * <p>
* This method should be invoked before the file is accessed. * This method should be invoked before the file is accessed.
* *
* @param path * @param file
* the path to remember meta data for. The path's current status * the path to remember meta data for. The path's current status
* information is saved. * information is saved.
* @param useConfig * @param useConfig
* if {@code true} read filesystem time resolution from * if {@code true} read filesystem time resolution from
* configuration file otherwise use fallback resolution * configuration file otherwise use fallback resolution
*/ */
protected FileSnapshot(File path, boolean useConfig) { protected FileSnapshot(File file, boolean useConfig) {
this.file = file;
this.lastRead = System.currentTimeMillis(); this.lastRead = System.currentTimeMillis();
this.fsTimestampResolution = useConfig this.fsTimestampResolution = useConfig
? FS.getFsTimerResolution(path.toPath().getParent()) ? FS.getFsTimerResolution(file.toPath().getParent())
: FALLBACK_TIMESTAMP_RESOLUTION; : FALLBACK_TIMESTAMP_RESOLUTION;
BasicFileAttributes fileAttributes = null; BasicFileAttributes fileAttributes = null;
try { try {
fileAttributes = FS.DETECTED.fileAttributes(path); fileAttributes = FS.DETECTED.fileAttributes(file);
} catch (IOException e) { } catch (IOException e) {
this.lastModified = path.lastModified(); this.lastModified = file.lastModified();
this.size = path.length(); this.size = file.length();
this.fileKey = MISSING_FILEKEY; this.fileKey = MISSING_FILEKEY;
return; return;
} }
this.lastModified = fileAttributes.lastModifiedTime().toMillis(); this.lastModified = fileAttributes.lastModifiedTime().toMillis();
this.size = fileAttributes.size(); this.size = fileAttributes.size();
this.fileKey = getFileKey(fileAttributes); this.fileKey = getFileKey(fileAttributes);
LOG.debug("file={}, create new FileSnapshot: lastRead={} ms" //$NON-NLS-1$
+ ", lastModified={} ms, size={}, fileKey={}", //$NON-NLS-1$
file, Long.valueOf(lastRead), Long.valueOf(lastModified),
Long.valueOf(size), fileKey);
} }
private boolean sizeChanged; private boolean sizeChanged;
@ -242,6 +254,7 @@ protected FileSnapshot(File path, boolean useConfig) {
private FileSnapshot(long read, long modified, long size, private FileSnapshot(long read, long modified, long size,
@NonNull Duration fsTimestampResolution, @NonNull Object fileKey) { @NonNull Duration fsTimestampResolution, @NonNull Object fileKey) {
this.file = null;
this.lastRead = read; this.lastRead = read;
this.lastModified = modified; this.lastModified = modified;
this.fsTimestampResolution = fsTimestampResolution; this.fsTimestampResolution = fsTimestampResolution;
@ -432,7 +445,14 @@ public String toString() {
private boolean isRacyClean(long read) { private boolean isRacyClean(long read) {
// add a 10% safety margin // add a 10% safety margin
long racyNanos = (fsTimestampResolution.toNanos() + 1) * 11 / 10; long racyNanos = (fsTimestampResolution.toNanos() + 1) * 11 / 10;
return wasRacyClean = (read - lastModified) * 1_000_000 <= racyNanos; long delta = (read - lastModified) * 1_000_000;
wasRacyClean = delta <= racyNanos;
LOG.debug("file={}, isRacyClean={}, read={} ms, lastModified={} ms," //$NON-NLS-1$
+ " delta={} ns, racy<={} ns", //$NON-NLS-1$
file, Boolean.valueOf(wasRacyClean), Long.valueOf(read),
Long.valueOf(lastModified), Long.valueOf(delta),
Long.valueOf(racyNanos));
return wasRacyClean;
} }
private boolean isModified(long currLastModified) { private boolean isModified(long currLastModified) {
@ -440,6 +460,9 @@ private boolean isModified(long currLastModified) {
lastModifiedChanged = lastModified != currLastModified; lastModifiedChanged = lastModified != currLastModified;
if (lastModifiedChanged) { if (lastModifiedChanged) {
LOG.debug("file={}, lastModified changed from {} to {}", //$NON-NLS-1$
file, Long.valueOf(lastModified),
Long.valueOf(currLastModified));
return true; return true;
} }
@ -447,26 +470,40 @@ private boolean isModified(long currLastModified) {
// after the last modification that any new modifications // after the last modification that any new modifications
// are certain to change the last modified time. // are certain to change the last modified time.
if (cannotBeRacilyClean) { if (cannotBeRacilyClean) {
LOG.debug("file={}, cannot be racily clean", file); //$NON-NLS-1$
return false; return false;
} }
if (!isRacyClean(lastRead)) { if (!isRacyClean(lastRead)) {
// Our last read should have marked cannotBeRacilyClean, // Our last read should have marked cannotBeRacilyClean,
// but this thread may not have seen the change. The read // but this thread may not have seen the change. The read
// of the volatile field lastRead should have fixed that. // of the volatile field lastRead should have fixed that.
LOG.debug("file={}, is unmodified", file); //$NON-NLS-1$
return false; return false;
} }
// We last read this path too close to its last observed // We last read this path too close to its last observed
// modification time. We may have missed a modification. // modification time. We may have missed a modification.
// Scan again, to ensure we still see the same state. // Scan again, to ensure we still see the same state.
LOG.debug("file={}, is racily clean", file); //$NON-NLS-1$
return true; return true;
} }
private boolean isFileKeyChanged(Object currFileKey) { private boolean isFileKeyChanged(Object currFileKey) {
return currFileKey != MISSING_FILEKEY && !currFileKey.equals(fileKey); boolean changed = currFileKey != MISSING_FILEKEY
&& !currFileKey.equals(fileKey);
if (changed) {
LOG.debug("file={}, FileKey changed from {} to {}", //$NON-NLS-1$
file, fileKey, currFileKey);
}
return changed;
} }
private boolean isSizeChanged(long currSize) { private boolean isSizeChanged(long currSize) {
return currSize != UNKNOWN_SIZE && currSize != size; boolean changed = (currSize != UNKNOWN_SIZE) && (currSize != size);
if (changed) {
LOG.debug("file={}, size changed from {} to {} bytes", //$NON-NLS-1$
file, Long.valueOf(size), Long.valueOf(currSize));
}
return changed;
} }
} }