Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JEP-210] Log handling rewrite #27

Merged
merged 71 commits into from
Oct 4, 2018
Merged

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
</pluginRepository>
</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. :-)

@@ -200,8 +181,10 @@ public WorkflowRun(WorkflowJob job, File dir) throws IOException {
}
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.

@@ -825,6 +646,10 @@ private String key() {
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.

*/
public class WorkflowRunConsoleNote extends ConsoleNote<Run<?, ?>> {

/**
* 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.

// TODO log-related overrides pending JEP-207:

@Override public InputStream getLogInputStream() throws IOException {
// Inefficient but probably rarely used anyway.
Copy link
Member

Choose a reason for hiding this comment

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

And comments like this make me realize that the log-handling rewrite that was supposed to deliver some great performance improvements may fall well short of that mark.

Copy link
Member Author

Choose a reason for hiding this comment

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

My interest is in writes, not reads.

Copy link
Member

@svanoort svanoort Oct 1, 2018

Choose a reason for hiding this comment

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

Not much point of a log that nobody reads though, is there? Anyway, we can see if it comes up as an issue.

Also, please do not mark this one "resolved" unless the code has been changed due to feedback -- it'll make it easier to address this if it comes up as a problem in the future.

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 particular method is rarely called AFAIK.


@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.

@@ -1291,6 +1124,15 @@ public void save() throws IOException {
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!) 🐙

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