Browse Source

Handle stale file handles on packed-refs file

On a local filesystem the packed-refs file will be orphaned if it is
replaced by another client while the current client is reading the old
one. However, since NFS servers do not keep track of open files, instead
of orphaning the old packed-refs file, such a replacement will cause the
old file to be garbage collected instead.  A stale file handle exception
will be raised on NFS servers if the file is garbage collected (deleted)
on the server while it is being read.  Since we no longer have access to
the old file in these cases, the previous code would just fail. However,
in these cases, reopening the file and rereading it will succeed (since
it will reopen the new replacement file).  So retrying the read is a
viable strategy to deal with stale file handles on the packed-refs file,
implement such a strategy.

Since it is possible that the packed-refs file could be replaced again
while rereading it (multiple consecutive updates can easily occur with
ref deletions), loop on stale file handle exceptions, up to 5 extra
times, trying to read the packed-refs file again, until we either read
the new file, or find that the file no longer exists. The limit of 5 is
arbitrary, and provides a safe upper bounds to prevent infinite loops
consuming resources in a potential unforeseen persistent error
condition.

Change-Id: I085c472bafa6e2f32f610a33ddc8368bb4ab1814
Signed-off-by: Martin Fick<mfick@codeaurora.org>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-4.1
Martin Fick 9 years ago committed by Matthias Sohn
parent
commit
06b446057c
  1. 1
      org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
  2. 1
      org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
  3. 52
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java

1
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties

@ -440,6 +440,7 @@ outputHasAlreadyBeenStarted=Output has already been started.
packChecksumMismatch=Pack checksum mismatch detected for pack file {0}
packCorruptedWhileWritingToFilesystem=Pack corrupted while writing to filesystem
packDoesNotMatchIndex=Pack {0} does not match index
packedRefsHandleIsStale=packed-refs handle is stale, {0}. retry
packetSizeMustBeAtLeast=packet size {0} must be >= {1}
packetSizeMustBeAtMost=packet size {0} must be <= {1}
packfileCorruptionDetected=Packfile corruption detected: {0}

1
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java

@ -499,6 +499,7 @@ public class JGitText extends TranslationBundle {
/***/ public String packChecksumMismatch;
/***/ public String packCorruptedWhileWritingToFilesystem;
/***/ public String packDoesNotMatchIndex;
/***/ public String packedRefsHandleIsStale;
/***/ public String packetSizeMustBeAtLeast;
/***/ public String packetSizeMustBeAtMost;
/***/ public String packfileCorruptionDetected;

52
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java

@ -98,6 +98,8 @@ import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.RefList;
import org.eclipse.jgit.util.RefMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Traditional file system based {@link RefDatabase}.
@ -115,6 +117,9 @@ import org.eclipse.jgit.util.RefMap;
* overall size of a Git repository on disk.
*/
public class RefDirectory extends RefDatabase {
private final static Logger LOG = LoggerFactory
.getLogger(RefDirectory.class);
/** Magic string denoting the start of a symbolic reference file. */
public static final String SYMREF = "ref: "; //$NON-NLS-1$
@ -746,22 +751,37 @@ public class RefDirectory extends RefDatabase {
}
private PackedRefList readPackedRefs() throws IOException {
final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
final BufferedReader br;
final MessageDigest digest = Constants.newMessageDigest();
try {
br = new BufferedReader(new InputStreamReader(
new DigestInputStream(new FileInputStream(packedRefsFile),
digest), CHARSET));
} catch (FileNotFoundException noPackedRefs) {
// Ignore it and leave the new list empty.
return PackedRefList.NO_PACKED_REFS;
}
try {
return new PackedRefList(parsePackedRefs(br), snapshot,
ObjectId.fromRaw(digest.digest()));
} finally {
br.close();
int maxStaleRetries = 5;
int retries = 0;
while (true) {
final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
final BufferedReader br;
final MessageDigest digest = Constants.newMessageDigest();
try {
br = new BufferedReader(new InputStreamReader(
new DigestInputStream(new FileInputStream(packedRefsFile),
digest), CHARSET));
} catch (FileNotFoundException noPackedRefs) {
// Ignore it and leave the new list empty.
return PackedRefList.NO_PACKED_REFS;
}
try {
return new PackedRefList(parsePackedRefs(br), snapshot,
ObjectId.fromRaw(digest.digest()));
} catch (IOException e) {
if (FileUtils.isStaleFileHandle(e) && retries < maxStaleRetries) {
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(
JGitText.get().packedRefsHandleIsStale,
Integer.valueOf(retries)), e);
}
retries++;
continue;
}
throw e;
} finally {
br.close();
}
}
}

Loading…
Cancel
Save