提交 7790dbca 编写于 作者: J Jesse Glick

[FIXED JENKINS-19515] Try to avoid truncation of fingerprint storage files.

1. Use AtomicFileWriter on the hypothesis that an exception during save() caused truncation.
2. Improve logging from the cleanup thread since that may be related to the root cause.
3. Regardless of the cause, recover more gracefully by deleting any truncated file rather than throwing the error up.
上级 89a11462
...@@ -67,6 +67,9 @@ Upcoming changes</a> ...@@ -67,6 +67,9 @@ Upcoming changes</a>
<li class=bug> <li class=bug>
Since 1.518, fingerprint serialization broke when job or file names contained XML special characters like ampersands. Since 1.518, fingerprint serialization broke when job or file names contained XML special characters like ampersands.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18337">issue 18337</a>) (<a href="https://issues.jenkins-ci.org/browse/JENKINS-18337">issue 18337</a>)
<li class=bug>
Robustness against truncated fingerprint files.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19515">issue 19515</a>)
<li class=rfe> <li class=rfe>
JavaScript error in the checkUrl computation shouldn't break the job configuration page. JavaScript error in the checkUrl computation shouldn't break the job configuration page.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19457">issue 19457</a>) (<a href="https://issues.jenkins-ci.org/browse/JENKINS-19457">issue 19457</a>)
......
...@@ -38,11 +38,13 @@ import hudson.BulkChange; ...@@ -38,11 +38,13 @@ import hudson.BulkChange;
import hudson.Extension; import hudson.Extension;
import hudson.model.listeners.ItemListener; import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener; import hudson.model.listeners.SaveableListener;
import hudson.util.AtomicFileWriter;
import hudson.util.HexBinaryConverter; import hudson.util.HexBinaryConverter;
import hudson.util.Iterators; import hudson.util.Iterators;
import hudson.util.PersistedList; import hudson.util.PersistedList;
import hudson.util.RunList; import hudson.util.RunList;
import hudson.util.XStream2; import hudson.util.XStream2;
import java.io.EOFException;
import jenkins.model.FingerprintFacet; import jenkins.model.FingerprintFacet;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import jenkins.model.TransientFingerprintFacetFactory; import jenkins.model.TransientFingerprintFacetFactory;
...@@ -995,8 +997,12 @@ public class Fingerprint implements ModelObject, Saveable { ...@@ -995,8 +997,12 @@ public class Fingerprint implements ModelObject, Saveable {
} }
} }
if (modified) if (modified) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Saving trimmed {0}", getFingerprintFile(md5sum));
}
save(); save();
}
return modified; return modified;
} }
...@@ -1101,8 +1107,9 @@ public class Fingerprint implements ModelObject, Saveable { ...@@ -1101,8 +1107,9 @@ public class Fingerprint implements ModelObject, Saveable {
if (facets.isEmpty()) { if (facets.isEmpty()) {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
// JENKINS-16301: fast path for the common case. // JENKINS-16301: fast path for the common case.
PrintWriter w = new PrintWriter(file, "UTF-8"); AtomicFileWriter afw = new AtomicFileWriter(file);
try { try {
PrintWriter w = new PrintWriter(afw);
w.println("<?xml version='1.0' encoding='UTF-8'?>"); w.println("<?xml version='1.0' encoding='UTF-8'?>");
w.println("<fingerprint>"); w.println("<fingerprint>");
w.print(" <timestamp>"); w.print(" <timestamp>");
...@@ -1139,8 +1146,9 @@ public class Fingerprint implements ModelObject, Saveable { ...@@ -1139,8 +1146,9 @@ public class Fingerprint implements ModelObject, Saveable {
w.println(" <facets/>"); w.println(" <facets/>");
w.print("</fingerprint>"); w.print("</fingerprint>");
w.flush(); w.flush();
afw.commit();
} finally { } finally {
w.close(); afw.abort();
} }
} else { } else {
// Slower fallback that can persist facets. // Slower fallback that can persist facets.
...@@ -1230,7 +1238,7 @@ public class Fingerprint implements ModelObject, Saveable { ...@@ -1230,7 +1238,7 @@ public class Fingerprint implements ModelObject, Saveable {
file.delete(); file.delete();
return null; return null;
} }
String parseError = messageOfXmlPullParserException(e); String parseError = messageOfParseException(e);
if (parseError != null) { if (parseError != null) {
logger.log(Level.WARNING, "Malformed XML in {0}: {1}", new Object[] {configFile, parseError}); logger.log(Level.WARNING, "Malformed XML in {0}: {1}", new Object[] {configFile, parseError});
file.delete(); file.delete();
...@@ -1240,13 +1248,13 @@ public class Fingerprint implements ModelObject, Saveable { ...@@ -1240,13 +1248,13 @@ public class Fingerprint implements ModelObject, Saveable {
throw e; throw e;
} }
} }
private static String messageOfXmlPullParserException(Throwable t) { private static String messageOfParseException(Throwable t) {
if (t instanceof XmlPullParserException) { if (t instanceof XmlPullParserException || t instanceof EOFException) {
return t.getMessage(); return t.getMessage();
} }
Throwable t2 = t.getCause(); Throwable t2 = t.getCause();
if (t2 != null) { if (t2 != null) {
return messageOfXmlPullParserException(t2); return messageOfParseException(t2);
} else { } else {
return null; return null;
} }
......
...@@ -101,11 +101,13 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork { ...@@ -101,11 +101,13 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
try { try {
Fingerprint fp = Fingerprint.load(fingerprintFile); Fingerprint fp = Fingerprint.load(fingerprintFile);
if (fp == null || !fp.isAlive()) { if (fp == null || !fp.isAlive()) {
logger.fine("deleting obsolete " + fingerprintFile);
fingerprintFile.delete(); fingerprintFile.delete();
return true; return true;
} else { } else {
// get the fingerprint in the official map so have the changes visible to Jenkins // get the fingerprint in the official map so have the changes visible to Jenkins
// otherwise the mutation made in FingerprintMap can override our trimming. // otherwise the mutation made in FingerprintMap can override our trimming.
logger.finer("possibly trimming " + fingerprintFile);
fp = Jenkins.getInstance()._getFingerprint(fp.getHashString()); fp = Jenkins.getInstance()._getFingerprint(fp.getHashString());
return fp.trim(); return fp.trim();
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册