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

Avoid saving nextBuildNumber while loading Job #9778

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jglick
Copy link
Member

@jglick jglick commented Sep 23, 2024

I spent a while looking into a semi-reproducible test failure affecting CloudBees CI running in high availability mode. The scenario involves a job being created and then a build immediately being scheduled; config.xml is created and then nextBuildNumber is created shortly thereafter. If another process is loading the job in between, it can attempt to write out nextBuildNumber itself, despite not actually running any build of the job. The details are probably not relevant to OSS Jenkins users but the essential guideline is that you should not be writing files to disk just because you are loading some configuration from disk, at least not without a good reason. The file should be written only if and when a build is scheduled; its absence should be treated as an implicit value of 1. (lastBuildNumber with an implicit value of 0 would perhaps have been more intuitive.)

I did keep the clause which recomputes and writes out nextBuildNumber in case the file existed but was corrupt (not parsable as an integer), since otherwise you might be printing an error on every Jenkins startup, if the job is never built again.

Testing done

Seems to resolve a timing-dependent test failure observed in CloudBees CI proprietary code.

Proposed changelog entries

N/A, likely not interesting.

Before the changes are marked as ready-for-merge:

Maintainer checklist

Comment on lines -224 to -226
// starting 1.28, we store nextBuildNumber in a separate file.
// but old Hudson didn't do it, so if the file doesn't exist,
// assume that nextBuildNumber was read from config.xml
Copy link
Member Author

Choose a reason for hiding this comment

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

A comment about Hudson 1.27 did not seem particularly helpful at this point!

try {
synchronized (this) {
this.nextBuildNumber = Integer.parseInt(f.readTrim());
}
} catch (NumberFormatException e) {
LOGGER.log(Level.WARNING, "Corruption in {0}: {1}", new Object[] {f, e});
//noinspection StatementWithEmptyBody
if (this instanceof LazyBuildMixIn.LazyLoadingJob) {
// allow LazyBuildMixIn.onLoad to fix 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.

It no longer does so, so simplify and use the same logic for all Job types. (In practice all jobs in common usage use LazyBuildMixIn.)

}
} else {
// From the old Hudson, or doCreateItem. Create this file now.
Copy link
Member Author

Choose a reason for hiding this comment

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

In fact doCreateItem calls onCreatedFromScratch, which did not create this file, so that was misleading.

Comment on lines +236 to +237
} else if (nextBuildNumber == 0) {
nextBuildNumber = 1;
Copy link
Member Author

@jglick jglick Sep 23, 2024

Choose a reason for hiding this comment

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

Can happen if the file was absent when a job which had never been built was loaded from disk. The field initializer

protected transient volatile int nextBuildNumber = 1;
is not called by XStream.

@@ -109,8 +109,8 @@ public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOExcep
int max = _builds.maxNumberOnDisk();
int next = asJob().getNextBuildNumber();
if (next <= max) {
LOGGER.log(Level.WARNING, "JENKINS-27530: improper nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max});
asJob().updateNextBuildNumber(max + 1);
LOGGER.log(Level.FINE, "nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max});
Copy link
Member Author

Choose a reason for hiding this comment

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

compare #2439

LOGGER.log(Level.WARNING, "JENKINS-27530: improper nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max});
asJob().updateNextBuildNumber(max + 1);
LOGGER.log(Level.FINE, "nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max});
asJob().fastUpdateNextBuildNumber(max + 1);
Copy link
Member Author

Choose a reason for hiding this comment

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

introduced in #9019

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.

2 participants