Skip to content

Commit bdf22a2

Browse files
authored
Optimizations & simplifications pertaining to lazy-loading (#11320)
* Avoid `TransientActionFactory` from `Job.getPermalinks` * Optimizing `PeepholePermalink.RunListener` to avoid resolving old targets * Simplified `RunMixIn` handling of next/previous build
1 parent 503b9ac commit bdf22a2

File tree

4 files changed

+32
-130
lines changed

4 files changed

+32
-130
lines changed

core/src/main/java/hudson/model/Job.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,12 +1119,15 @@ public long getEstimatedDuration() {
11191119
*
11201120
* @return never null
11211121
*/
1122+
@SuppressWarnings("deprecation")
11221123
public PermalinkList getPermalinks() {
11231124
PeepholePermalink.initialized();
11241125
// TODO: shall we cache this?
11251126
PermalinkList permalinks = new PermalinkList(Permalink.BUILTIN);
1126-
for (PermalinkProjectAction ppa : getActions(PermalinkProjectAction.class)) {
1127-
permalinks.addAll(ppa.getPermalinks());
1127+
for (var action : getActions()) {
1128+
if (action instanceof PermalinkProjectAction ppa) {
1129+
permalinks.addAll(ppa.getPermalinks());
1130+
}
11281131
}
11291132
return permalinks;
11301133
}

core/src/main/java/hudson/model/Run.java

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -191,23 +191,6 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
191191
*/
192192
private long queueId = Run.QUEUE_ID_UNKNOWN;
193193

194-
/**
195-
* Previous build. Can be null.
196-
* TODO JENKINS-22052 this is not actually implemented any more
197-
*
198-
* External code should use {@link #getPreviousBuild()}
199-
*/
200-
@Restricted(NoExternalUse.class)
201-
protected transient volatile RunT previousBuild;
202-
203-
/**
204-
* Next build. Can be null.
205-
*
206-
* External code should use {@link #getNextBuild()}
207-
*/
208-
@Restricted(NoExternalUse.class)
209-
protected transient volatile RunT nextBuild;
210-
211194
/**
212195
* Pointer to the next younger build in progress. This data structure is lazily updated,
213196
* so it may point to the build that's already completed. This pointer is set to 'this'
@@ -801,23 +784,19 @@ public int getNumber() {
801784
}
802785

803786
/**
804-
* Called by {@link RunMap} to drop bi-directional links in preparation for
805-
* deleting a build.
787+
* Called by {@link RunMap} in preparation for deleting a build.
806788
* @see jenkins.model.lazy.LazyBuildMixIn.RunMixIn#dropLinks
807789
* @since 1.556
808790
*/
809791
protected void dropLinks() {
810-
if (nextBuild != null)
811-
nextBuild.previousBuild = previousBuild;
812-
if (previousBuild != null)
813-
previousBuild.nextBuild = nextBuild;
814792
}
815793

816794
/**
817795
* @see jenkins.model.lazy.LazyBuildMixIn.RunMixIn#getPreviousBuild
818796
*/
819797
public @CheckForNull RunT getPreviousBuild() {
820-
return previousBuild;
798+
// TODO could be implemented for benefit of ExternalRun
799+
return null;
821800
}
822801

823802
/**
@@ -960,7 +939,8 @@ protected void dropLinks() {
960939
* @see jenkins.model.lazy.LazyBuildMixIn.RunMixIn#getNextBuild
961940
*/
962941
public @CheckForNull RunT getNextBuild() {
963-
return nextBuild;
942+
// TODO could be implemented for benefit of ExternalRun
943+
return null;
964944
}
965945

966946

core/src/main/java/jenkins/model/PeepholePermalink.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,24 @@ protected File getPermalinkFile(Job<?, ?> job) {
9090
*/
9191
@Override
9292
public Run<?, ?> resolve(Job<?, ?> job) {
93-
return ExtensionList.lookupFirst(Cache.class).get(job, getId()).resolve(this, job, getId());
93+
return get(job).resolve(this, job, getId());
94+
}
95+
96+
Cache.PermalinkTarget get(Job<?, ?> job) {
97+
return ExtensionList.lookupFirst(Cache.class).get(job, getId());
98+
}
99+
100+
int resolveNumber(Job<?, ?> job) {
101+
// TODO Java 21+ use patterns
102+
var pt = get(job);
103+
if (pt instanceof Cache.Some some) {
104+
return some.number;
105+
} else if (pt instanceof Cache.None) {
106+
return 0;
107+
} else { // Unknown
108+
var b = pt.resolve(this, job, getId());
109+
return b != null ? b.number : 0;
110+
}
94111
}
95112

96113
/**
@@ -312,7 +329,7 @@ public static class RunListenerImpl extends RunListener<Run<?, ?>> {
312329
public void onDeleted(Run run) {
313330
Job<?, ?> j = run.getParent();
314331
for (PeepholePermalink pp : Util.filter(j.getPermalinks(), PeepholePermalink.class)) {
315-
if (pp.resolve(j) == run) {
332+
if (pp.resolveNumber(j) == run.number) {
316333
Run<?, ?> r = pp.find(run.getPreviousBuild());
317334
LOGGER.fine(() -> "Updating " + pp.getId() + " permalink from deleted " + run + " to " + (r == null ? -1 : r.getNumber()));
318335
pp.updateCache(j, r);
@@ -328,8 +345,7 @@ public void onCompleted(Run<?, ?> run, @NonNull TaskListener listener) {
328345
Job<?, ?> j = run.getParent();
329346
for (PeepholePermalink pp : Util.filter(j.getPermalinks(), PeepholePermalink.class)) {
330347
if (pp.apply(run)) {
331-
Run<?, ?> cur = pp.resolve(j);
332-
if (cur == null || cur.getNumber() < run.getNumber()) {
348+
if (pp.resolveNumber(j) < run.getNumber()) {
333349
LOGGER.fine(() -> "Updating " + pp.getId() + " permalink to completed " + run);
334350
pp.updateCache(j, run);
335351
}

core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
package jenkins.model.lazy;
2626

27-
import static java.util.logging.Level.FINER;
2827

2928
import edu.umd.cs.findbugs.annotations.CheckForNull;
3029
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -46,7 +45,6 @@
4645
import java.nio.file.Files;
4746
import java.util.ArrayList;
4847
import java.util.List;
49-
import java.util.Objects;
5048
import java.util.TreeMap;
5149
import java.util.logging.Level;
5250
import java.util.logging.Logger;
@@ -194,7 +192,6 @@ public final synchronized RunT newBuild() throws IOException {
194192
return newBuild();
195193
}
196194
builds.put(lastBuild);
197-
lastBuild.getPreviousBuild(); // JENKINS-20662: create connection to previous build
198195
return lastBuild;
199196
} catch (InvocationTargetException e) {
200197
LOGGER.log(Level.WARNING, String.format("A new build could not be created in job %s", asJob().getFullName()), e);
@@ -334,32 +331,6 @@ public interface LazyLoadingRun<JobT extends Job<JobT, RunT> & Queue.Task & Lazy
334331
*/
335332
public abstract static class RunMixIn<JobT extends Job<JobT, RunT> & Queue.Task & LazyBuildMixIn.LazyLoadingJob<JobT, RunT>, RunT extends Run<JobT, RunT> & LazyLoadingRun<JobT, RunT>> {
336333

337-
/**
338-
* Pointers to form bi-directional link between adjacent runs using
339-
* {@link LazyBuildMixIn}.
340-
*
341-
* <p>
342-
* Some {@link Run}s do lazy-loading, so we don't use
343-
* {@link #previousBuildR} and {@link #nextBuildR}, and instead use these
344-
* fields and point to {@link #selfReference} (or {@link #none}) of
345-
* adjacent builds.
346-
*/
347-
private volatile BuildReference<RunT> previousBuildR, nextBuildR;
348-
349-
/**
350-
* Used in {@link #previousBuildR} and {@link #nextBuildR} to indicate
351-
* that we know there is no next/previous build (as opposed to {@code null},
352-
* which is used to indicate we haven't determined if there is a next/previous
353-
* build.)
354-
*/
355-
@SuppressWarnings({"unchecked", "rawtypes"})
356-
private static final BuildReference NONE = new BuildReference("NONE", null);
357-
358-
@SuppressWarnings("unchecked")
359-
private BuildReference<RunT> none() {
360-
return NONE;
361-
}
362-
363334
private BuildReference<RunT> selfReference;
364335

365336
protected RunMixIn() {}
@@ -380,19 +351,6 @@ public final synchronized BuildReference<RunT> createReference() {
380351
* To implement {@link Run#dropLinks}.
381352
*/
382353
public final void dropLinks() {
383-
if (nextBuildR != null) {
384-
RunT nb = nextBuildR.get();
385-
if (nb != null) {
386-
nb.getRunMixIn().previousBuildR = previousBuildR;
387-
}
388-
}
389-
if (previousBuildR != null) {
390-
RunT pb = previousBuildR.get();
391-
if (pb != null) {
392-
pb.getRunMixIn().nextBuildR = nextBuildR;
393-
}
394-
}
395-
396354
// make this build object unreachable by other Runs
397355
createReference().clear();
398356
}
@@ -401,69 +359,14 @@ public final void dropLinks() {
401359
* To implement {@link Run#getPreviousBuild}.
402360
*/
403361
public final RunT getPreviousBuild() {
404-
while (true) {
405-
BuildReference<RunT> r = previousBuildR; // capture the value once
406-
407-
if (r == null) {
408-
// having two neighbors pointing to each other is important to make RunMap.removeValue work
409-
JobT _parent = Objects.requireNonNull(asRun().getParent(), "no parent for " + asRun().number);
410-
RunT pb = _parent.getLazyBuildMixIn()._getRuns().search(asRun().number - 1, AbstractLazyLoadRunMap.Direction.DESC);
411-
if (pb != null) {
412-
pb.getRunMixIn().nextBuildR = createReference(); // establish bi-di link
413-
this.previousBuildR = pb.getRunMixIn().createReference();
414-
LOGGER.log(FINER, "Linked {0}<->{1} in getPreviousBuild()", new Object[]{this, pb});
415-
return pb;
416-
} else {
417-
this.previousBuildR = none();
418-
return null;
419-
}
420-
}
421-
if (r == none()) {
422-
return null;
423-
}
424-
425-
RunT referent = r.get();
426-
if (referent != null) {
427-
return referent;
428-
}
429-
430-
// the reference points to a GC-ed object, drop the reference and do it again
431-
this.previousBuildR = null;
432-
}
362+
return asRun().getParent().getLazyBuildMixIn()._getRuns().search(asRun().number - 1, AbstractLazyLoadRunMap.Direction.DESC);
433363
}
434364

435365
/**
436366
* To implement {@link Run#getNextBuild}.
437367
*/
438368
public final RunT getNextBuild() {
439-
while (true) {
440-
BuildReference<RunT> r = nextBuildR; // capture the value once
441-
442-
if (r == null) {
443-
// having two neighbors pointing to each other is important to make RunMap.removeValue work
444-
RunT nb = asRun().getParent().getLazyBuildMixIn()._getRuns().search(asRun().number + 1, AbstractLazyLoadRunMap.Direction.ASC);
445-
if (nb != null) {
446-
nb.getRunMixIn().previousBuildR = createReference(); // establish bi-di link
447-
this.nextBuildR = nb.getRunMixIn().createReference();
448-
LOGGER.log(FINER, "Linked {0}<->{1} in getNextBuild()", new Object[]{this, nb});
449-
return nb;
450-
} else {
451-
this.nextBuildR = none();
452-
return null;
453-
}
454-
}
455-
if (r == none()) {
456-
return null;
457-
}
458-
459-
RunT referent = r.get();
460-
if (referent != null) {
461-
return referent;
462-
}
463-
464-
// the reference points to a GC-ed object, drop the reference and do it again
465-
this.nextBuildR = null;
466-
}
369+
return asRun().getParent().getLazyBuildMixIn()._getRuns().search(asRun().number + 1, AbstractLazyLoadRunMap.Direction.ASC);
467370
}
468371

469372
}

0 commit comments

Comments
 (0)