提交 534328b2 编写于 作者: J Jesse Glick

[FIXED JENKINS-25759] Avoid consuming too much memory while running validateAntFileMask.

Not fully solved, since the scannedDirs field can still grow to be large, but at least clearing files/dirsNotIncluded.
Also imposing a 5s timeout on the scan regardless of file count, and defining a user-customizable bound.
上级 b21827b3
...@@ -55,7 +55,9 @@ Upcoming changes</a> ...@@ -55,7 +55,9 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. --> <!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=--> <div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image> <ul class=image>
<li class=> <li class=bug>
Performance problems on large workspaces associated with validating file include patterns.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25759">issue 25759</a>)
</ul> </ul>
</div><!--=TRUNK-END=--> </div><!--=TRUNK-END=-->
......
...@@ -2324,11 +2324,18 @@ public final class FilePath implements Serializable { ...@@ -2324,11 +2324,18 @@ public final class FilePath implements Serializable {
* null if no error was found. Otherwise returns a human readable error message. * null if no error was found. Otherwise returns a human readable error message.
* @since 1.90 * @since 1.90
* @see #validateFileMask(FilePath, String) * @see #validateFileMask(FilePath, String)
* @deprecated use {@link #validateAntFileMask(String, int)} instead
*/ */
public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException { public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException {
return validateAntFileMask(fileMasks, Integer.MAX_VALUE); return validateAntFileMask(fileMasks, Integer.MAX_VALUE);
} }
/**
* Default bound for {@link #validateAntFileMask(String, int)}.
* @since 1.592
*/
public static int VALIDATE_ANT_FILE_MASK_BOUND = Integer.getInteger(FilePath.class.getName() + ".VALIDATE_ANT_FILE_MASK_BOUND", 10000);
/** /**
* Like {@link #validateAntFileMask(String)} but performing only a bounded number of operations. * Like {@link #validateAntFileMask(String)} but performing only a bounded number of operations.
* <p>Whereas the unbounded overload is appropriate for calling from cancelable, long-running tasks such as build steps, * <p>Whereas the unbounded overload is appropriate for calling from cancelable, long-running tasks such as build steps,
...@@ -2338,7 +2345,7 @@ public final class FilePath implements Serializable { ...@@ -2338,7 +2345,7 @@ public final class FilePath implements Serializable {
* A message is returned in case the file pattern can definitely be determined to not match anything in the directory within the alloted time. * A message is returned in case the file pattern can definitely be determined to not match anything in the directory within the alloted time.
* If the time runs out without finding a match but without ruling out the possibility that there might be one, {@link InterruptedException} is thrown, * If the time runs out without finding a match but without ruling out the possibility that there might be one, {@link InterruptedException} is thrown,
* in which case the calling code should give the user the benefit of the doubt and use {@link hudson.util.FormValidation.Kind#OK} (with or without a message). * in which case the calling code should give the user the benefit of the doubt and use {@link hudson.util.FormValidation.Kind#OK} (with or without a message).
* @param bound a maximum number of negative operations (deliberately left vague) to perform before giving up on a precise answer; 10_000 is a reasonable pick * @param bound a maximum number of negative operations (deliberately left vague) to perform before giving up on a precise answer; try {@link #VALIDATE_ANT_FILE_MASK_BOUND}
* @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches * @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches
* @since 1.484 * @since 1.484
*/ */
...@@ -2446,10 +2453,15 @@ public final class FilePath implements Serializable { ...@@ -2446,10 +2453,15 @@ public final class FilePath implements Serializable {
class Cancel extends RuntimeException {} class Cancel extends RuntimeException {}
DirectoryScanner ds = bound == Integer.MAX_VALUE ? new DirectoryScanner() : new DirectoryScanner() { DirectoryScanner ds = bound == Integer.MAX_VALUE ? new DirectoryScanner() : new DirectoryScanner() {
int ticks; int ticks;
long start = System.currentTimeMillis();
@Override public synchronized boolean isCaseSensitive() { @Override public synchronized boolean isCaseSensitive() {
if (!filesIncluded.isEmpty() || !dirsIncluded.isEmpty() || ticks++ > bound) { if (!filesIncluded.isEmpty() || !dirsIncluded.isEmpty() || ticks++ > bound || System.currentTimeMillis() - start > 5000) {
throw new Cancel(); throw new Cancel();
} }
filesNotIncluded.clear();
dirsNotIncluded.clear();
// notFollowedSymlinks might be large, but probably unusual
// scannedDirs will typically be largish, but seems to be needed
return super.isCaseSensitive(); return super.isCaseSensitive();
} }
}; };
...@@ -2512,7 +2524,7 @@ public final class FilePath implements Serializable { ...@@ -2512,7 +2524,7 @@ public final class FilePath implements Serializable {
if(!exists()) // no workspace. can't check if(!exists()) // no workspace. can't check
return FormValidation.ok(); return FormValidation.ok();
String msg = validateAntFileMask(value, 10000); String msg = validateAntFileMask(value, VALIDATE_ANT_FILE_MASK_BOUND);
if(errorIfNotExist) return FormValidation.error(msg); if(errorIfNotExist) return FormValidation.error(msg);
else return FormValidation.warning(msg); else return FormValidation.warning(msg);
} catch (InterruptedException e) { } catch (InterruptedException e) {
......
...@@ -227,7 +227,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { ...@@ -227,7 +227,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
listenerWarnOrError(listener, Messages.ArtifactArchiver_NoMatchFound(artifacts)); listenerWarnOrError(listener, Messages.ArtifactArchiver_NoMatchFound(artifacts));
String msg = null; String msg = null;
try { try {
msg = ws.validateAntFileMask(artifacts); msg = ws.validateAntFileMask(artifacts, FilePath.VALIDATE_ANT_FILE_MASK_BOUND);
} catch (Exception e) { } catch (Exception e) {
listenerWarnOrError(listener, e.getMessage()); listenerWarnOrError(listener, e.getMessage());
} }
......
...@@ -378,7 +378,7 @@ public abstract class FormFieldValidator { ...@@ -378,7 +378,7 @@ public abstract class FormFieldValidator {
return; return;
} }
String msg = ws.validateAntFileMask(value, 10000); String msg = ws.validateAntFileMask(value, FilePath.VALIDATE_ANT_FILE_MASK_BOUND);
if(errorIfNotExist) error(msg); if(errorIfNotExist) error(msg);
else warning(msg); else warning(msg);
} catch (InterruptedException e) { } catch (InterruptedException e) {
......
...@@ -56,6 +56,7 @@ import java.util.concurrent.Executors; ...@@ -56,6 +56,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.jvnet.hudson.test.Issue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
...@@ -496,11 +497,13 @@ public class FilePathTest extends ChannelTestCase { ...@@ -496,11 +497,13 @@ public class FilePathTest extends ChannelTestCase {
} }
} }
@SuppressWarnings("deprecation")
private static void assertValidateAntFileMask(String expected, FilePath d, String fileMasks) throws Exception { private static void assertValidateAntFileMask(String expected, FilePath d, String fileMasks) throws Exception {
assertEquals(expected, d.validateAntFileMask(fileMasks)); assertEquals(expected, d.validateAntFileMask(fileMasks));
} }
@Bug(7214) @Issue("JENKINS-7214")
@SuppressWarnings("deprecation")
public void testValidateAntFileMaskBounded() throws Exception { public void testValidateAntFileMaskBounded() throws Exception {
File tmp = Util.createTempDir(); File tmp = Util.createTempDir();
try { try {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册