Skip to content

Conversation

@jglick
Copy link
Member

@jglick jglick commented Sep 23, 2016

Downstream of jenkinsci/workflow-api-plugin#17 and jenkinsci/workflow-support-plugin#15. Cf. #21.

JEP-210

  • basics
  • avoid overhead of serialized ConsoleNote on every line
  • pluggable log location
  • tests of annotation, single-step log display
  • basic show/hide UI for steps (adapted from [JENKINS-27394] Collapsible sections in log #21)
  • show/hide entire sections
  • optimize PipelineLargeText.<init>
  • optimize WorkflowRun.getLogInputStream

Nice-to-have things I am deferring:

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far

if (executor != null) {
AsynchronousExecution asynchronousExecution = executor.getAsynchronousExecution();
if (asynchronousExecution != null) {
asynchronousExecution.completed(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we need it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uses to be part of copyLogsTask (line 278); that is now gone, so we need to somehow release the flyweight executor slot. This seems like the appropriate place to do it.

pom.xml Outdated
</pluginRepositories>
<properties>
<jenkins.version>1.642.3</jenkins.version>
<hpi-plugin.version>1.120</hpi-plugin.version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally should be updated in the Parent POM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review jenkinsci/plugin-pom#35. :-)

}
try {
onStartBuilding();
// TODO introduce API to wrap out log location
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw that, but it is not going to work.

return TaskListener.NULL;
}
}
@Override public InputStream getLog() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Stream is not a perfect solution for Logging. I would rather go after an event-based model similar to JUL, but it will require more changes (it's in my design btw)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tons of Jenkins APIs expect a TaskListener or OutputStream for output, and expect an AnnotatedLargeText or InputStream for input, so streams are mandated at some level I think. Of course a logging implementation might internally work with lines.

* Prefix used in metadata lines.
*/
public static final String CONSOLE_NOTE_PREFIX = "[Pipeline] ";
private static final String CONSOLE_NOTE_PREFIX = "[Pipeline] ";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐜 binary compat change without a real need

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just minimizing access of things which were never really supposed to be public.


@Override public List<String> getLog(int maxLines) throws IOException {
int lineCount = 0;
List<String> logLines = new LinkedList<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐜 Why you should never use a LinkedList. TL;DR: Locality-of-reference matters, allocate a darned ArrayList.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just copied from core.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the core implementation of this method has since been rewritten. As I pointed out in jenkinsci/jenkins#2607 (comment) that is irrelevant, since this method is not used in production workflows. Anyway the rewrite is impossible to apply here since it relies on RandomAccessFile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy and pasted code? Still...

I'm going to keep pointing out that LinkedList is a bad idea until people stop (ab)using it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not used in production. The performance does not matter.

synchronized (this) {
PipelineIOUtils.writeByXStream(this, loc, XSTREAM2, isAtomic);
SaveableListener.fireOnChange(this, file);
if (Boolean.TRUE.equals(completed)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jglick No! 🐛 🐛 🐛 and a whole nest of 🐛s. This is not the right place to be terminating the AsynchronousExecution - if an IOException never occurs, the build never finishes.

You're welcome to do an extra on-save check somewhere logical -- because we have had hard-to-trace issues with the build not getting killed before, but this makes undead Pipelines more likely.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is explanation elsewhere of why we have to do the termination here. I can think about whether it is possible to do so more forcefully, such as in a finally block.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if an IOException never occurs

I suppose you mean if an IOException does occur.

the build never finishes

As noted in a separate thread, even if no subsequent call to save is ever made, it is misleading to say that the build is “not finished”. Rather, there would be a stale AsynchronousExecution, which likely has little or no visible impact.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(see #27 (comment) for fuller discussion)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A stale AsynchronousExecution translates to essentially a "zombie" job that proves highly irritating to users -- it seems like it never completes even if the build is marked as done. Also it holds onto the OneShotExecutor indefinitely.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again the AsynchronousExecution normally has no visible effect on anything—it only appears while we are actively running Groovy code, which of course we are not if the build is completed.

And again if some problem in existing code (whether in workflow-job or workflow-cps) results in a condition whereby <completed>true</completed> never gets written to build.xml, the AsynchronousExecution is the least of your concerns, since after the next restart onLoad will be confused. Possibly #93 will defend against that case—figure out that completed is wrong and save the correction—but at that point any old Executors are no longer an issue anyway (and there should be no new AfterRestartTask).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the after-restart case should have been fixed by #104.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, @jglick we have explicit handling for that onRestart case now (as Devin noted, #104 does the exactly cleanup you called out a need for).

Copy link
Member

@svanoort svanoort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done my best to give this at least a decent readthrough (though I don't imagine every bug has been caught). There are a couple bugs that need fixing but in general the implementation is pretty sane.

Or as sane as Pipeline internals are going to be anyway, given some of the Lovecraftian back-compatibility horrors they're dragging along with them... (ia ia FlowGraph f'htagn!) 🐙

@jglick jglick requested a review from svanoort September 28, 2018 15:44
Copy link
Member

@svanoort svanoort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handling of AsynchronousExecutions here is wrong and will probably break in the face of some fairly common cases (IOExceptions, etc). But that's not going to cause Critical/Blocker issues for users, just annoying ones. So, I'm removing my "changes requested" and this can go forward without my approval (I don't feel right "approving" something which I see actual bugs in).

The more critical risks here are the problems we can't anticipate, anyway.

Copy link
Member

@svanoort svanoort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still does not feel like the correct place to mark the AsynchronousExecution completed, but it seems to work as far as I can tell from cross-testing, whereas for reasons I'm still at a loss to explain, the correct place to mark it generates test flakes (primarily in CI) -- see #108 -- so I say we roll with this and release it -- with appropriate cautions that the log handling rewrite is a more-than-normal risk for people updating.

@jglick
Copy link
Member Author

jglick commented Oct 4, 2018

the correct place to mark it generates test flakes (primarily in CI)

Also unclear to me why. I note that the last build of this PR not only passed, but acc. to the log had no flakes. Anyway, this is something that could be sorted out later—there is no API or settings impact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants