forked from jenkinsci/pipeline-graph-view-plugin
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLiveGraphRegistry.java
More file actions
132 lines (116 loc) · 4.78 KB
/
LiveGraphRegistry.java
File metadata and controls
132 lines (116 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package io.jenkins.plugins.pipelinegraphview.livestate;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineGraph;
import io.jenkins.plugins.pipelinegraphview.utils.PipelineStepList;
import java.time.Duration;
import jenkins.util.SystemProperties;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
/**
* Singleton holding one {@link LiveGraphState} per in-progress run.
* Entries are created on demand by the listener / lifecycle code, removed on completion,
* and otherwise bounded by a Caffeine LRU so abandoned entries (deleted runs, listener
* bugs) don't leak.
*/
public final class LiveGraphRegistry {
// IMPORTANT: keep CACHE_MAX_SIZE declared BEFORE INSTANCE. Static fields initialise in
// source order, and the instance's Caffeine builder reads CACHE_MAX_SIZE — if INSTANCE
// were declared first, CACHE_MAX_SIZE would still be 0 during construction and Caffeine
// would evict every entry immediately (maximumSize(0) means "no entries allowed").
private static final int CACHE_MAX_SIZE =
SystemProperties.getInteger(LiveGraphRegistry.class.getName() + ".size", 512);
private static final LiveGraphRegistry INSTANCE = new LiveGraphRegistry();
public static LiveGraphRegistry get() {
return INSTANCE;
}
private final Cache<String, LiveGraphState> states = Caffeine.newBuilder()
.maximumSize(CACHE_MAX_SIZE)
.expireAfterAccess(Duration.ofMinutes(30))
.build();
LiveGraphRegistry() {}
/**
* Escape hatch. Setting this system property to {@code false} makes
* {@link #snapshot(WorkflowRun)} always return {@code null}, forcing callers to use the
* scanner fallback. Useful if a regression lands in the live-state path.
*/
private static boolean disabled() {
return !SystemProperties.getBoolean(LiveGraphRegistry.class.getName() + ".enabled", true);
}
LiveGraphState getOrCreate(FlowExecution execution) {
if (disabled()) {
return null;
}
String key = keyFor(execution);
if (key == null) {
return null;
}
return states.get(key, k -> new LiveGraphState());
}
/**
* Returns a snapshot of the live state for this run, or {@code null} if none exists
* (feature disabled, state never populated, state poisoned). Callers must treat
* {@code null} as "fall back to the scanner path."
*/
public LiveGraphSnapshot snapshot(WorkflowRun run) {
if (disabled()) {
return null;
}
LiveGraphState state = states.getIfPresent(run.getExternalizableId());
return state == null ? null : state.snapshot();
}
void remove(FlowExecution execution) {
String key = keyFor(execution);
if (key != null) {
states.invalidate(key);
}
}
/**
* Returns a previously-cached {@link PipelineGraph} for this run if it was computed at
* or after {@code minVersion}, otherwise {@code null}. Use {@link LiveGraphSnapshot#version()}
* as the argument — cache entries older than the caller's snapshot are rejected.
*/
public PipelineGraph cachedGraph(WorkflowRun run, long minVersion) {
if (disabled()) {
return null;
}
LiveGraphState state = states.getIfPresent(run.getExternalizableId());
return state == null ? null : state.cachedGraph(minVersion);
}
public void cacheGraph(WorkflowRun run, long version, PipelineGraph graph) {
if (disabled()) {
return;
}
LiveGraphState state = states.getIfPresent(run.getExternalizableId());
if (state != null) {
state.cacheGraph(version, graph);
}
}
public PipelineStepList cachedAllSteps(WorkflowRun run, long minVersion) {
if (disabled()) {
return null;
}
LiveGraphState state = states.getIfPresent(run.getExternalizableId());
return state == null ? null : state.cachedAllSteps(minVersion);
}
public void cacheAllSteps(WorkflowRun run, long version, PipelineStepList steps) {
if (disabled()) {
return;
}
LiveGraphState state = states.getIfPresent(run.getExternalizableId());
if (state != null) {
state.cacheAllSteps(version, steps);
}
}
private static String keyFor(FlowExecution execution) {
try {
Object exec = execution.getOwner().getExecutable();
if (exec instanceof WorkflowRun run) {
return run.getExternalizableId();
}
return null;
} catch (Exception e) {
return null;
}
}
}