Similar observed in 2.73.2, which has the getAll change:
"Handling GET /blue/rest/organizations/jenkins/pipelines/…/pipelines/…/branches/…/runs/2/ from … : RequestHandlerThread[#…]" … runnable …
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
at java.io.File.isDirectory(File.java:849)
at hudson.model.User$3.accept(User.java:688)
at java.io.File.listFiles(File.java:1291)
at hudson.model.User.getLegacyConfigFilesFor(User.java:685)
at hudson.model.User.getOrCreate(User.java:443)
at hudson.model.User.getById(User.java:541)
at hudson.model.User$UserIDCanonicalIdResolver.resolveCanonicalId(User.java:1107)
at hudson.model.User.get(User.java:411)
at hudson.model.User.get(User.java:380)
at hudson.plugins.git.GitChangeSet.findOrCreateUser(GitChangeSet.java:379)
at hudson.plugins.git.GitChangeSet.getAuthor(GitChangeSet.java:448)
at io.jenkins.blueocean.service.embedded.rest.ChangeSetResource.getAuthor(ChangeSetResource.java:45)
at …
at io.jenkins.blueocean.commons.stapler.export.MethodProperty.getValue(MethodProperty.java:72)
at io.jenkins.blueocean.commons.stapler.export.ExportInterceptor$1.getValue(ExportInterceptor.java:43)
at io.jenkins.blueocean.commons.stapler.Export$BlueOceanExportInterceptor.getValue(Export.java:167)
at io.jenkins.blueocean.commons.stapler.export.Property.writeTo(Property.java:136)
at io.jenkins.blueocean.commons.stapler.export.Model.writeNestedObjectTo(Model.java:228)
at io.jenkins.blueocean.commons.stapler.export.Model.writeNestedObjectTo(Model.java:224)
at io.jenkins.blueocean.commons.stapler.export.Property.writeValue(Property.java:314)
at io.jenkins.blueocean.commons.stapler.export.Property.writeBuffered(Property.java:178)
at io.jenkins.blueocean.commons.stapler.export.Property.writeValue(Property.java:239)
at io.jenkins.blueocean.commons.stapler.export.Property.writeValue(Property.java:172)
at io.jenkins.blueocean.commons.stapler.export.Property.writeTo(Property.java:157)
at io.jenkins.blueocean.commons.stapler.export.Model.writeNestedObjectTo(Model.java:228)
at io.jenkins.blueocean.commons.stapler.export.Model.writeNestedObjectTo(Model.java:224)
at io.jenkins.blueocean.commons.stapler.export.Model.writeNestedObjectTo(Model.java:224)
at io.jenkins.blueocean.commons.stapler.export.Model.writeTo(Model.java:199)
at io.jenkins.blueocean.commons.stapler.Export.writeOne(Export.java:148)
at io.jenkins.blueocean.commons.stapler.Export.serveExposedBean(Export.java:139)
at io.jenkins.blueocean.commons.stapler.Export.doJson(Export.java:79)
at io.jenkins.blueocean.commons.stapler.TreeResponse$Processor$1.generateResponse(TreeResponse.java:48)
at org.kohsuke.stapler.HttpResponseRenderer$Default.handleHttpResponse(HttpResponseRenderer.java:124)
at org.kohsuke.stapler.HttpResponseRenderer$Default.generateResponse(HttpResponseRenderer.java:69)
at …
There are multiple things going on here. First, Blue Ocean is using Stapler export to write out changelog information. Probably this is intentional (the information is slated to be consumed in the UI somewhere), as opposed to the common antipattern of external tools asking for /jobs/*/*/api/json (without tree) and then discarding most of the result. This particular instance had set -Dcom.cloudbees.workflow.rest.external.ChangeSetExt.resolveCommitAuthors=false, which would have bypassed the problem for pipeline-stage-view, but perhaps there is no equivalent in Blue Ocean.
Next there is the fact that GitChangeSet is doing a kind of reverse lookup, from User.fullName to User.id, which forces use of the relatively expensive CanonicalIdResolver. I tried to suppress this at one point, since it crops up all the time, but it turns out that this behavior is intentional and part of the expected behavior of the plugin.
Then we have the specific behaviors of ID resolvers. FullNameIdResolver should I guess have been improved in 2.71 by making getAll be fast. DefaultUserCanonicalIdResolver is trivial. The problem here is UserIDCanonicalIdResolver, which has two potential performance issues. One is the SECURITY-243 defense, which was left enabled in this instance. The other, visible in the stack trace, is getById, which is supposed to be a relatively cheap operation, but is in fact still doing a full filesystem search of $JENKINS_HOME/users/*/config.xml for each newly cached User.
So there are two problems visible in User. First, getLegacyConfigFilesFor is poorly optimized:
pathname.isDirectory() && new File(pathname, "config.xml").isFile()
is doing two stats when the single stat
new File(pathname, "config.xml").isFile()
would produce the same result (if a child file exists obviously the parent must be a directory); and IdStrategy.equals could be run first, avoiding any system calls unless and until there is a candidate match.
More broadly, it may be the case that after the getAll change in 2.71, the whole getLegacyConfigFilesFor check in getOrCreate could just be deleted: if there were some legacy config file lying around, presumably scanAll would have already found it at startup, adding the user to the in-memory cache. It looks like UserTest.migration is testing this logic but needs to be confirmed.
What is a “modern Jenkins core”?
Recheck after
JENKINS-45737in 2.71+; information about performance of this class predating that change is not really useful now.