Skip to content

Commit 471ea70

Browse files
committed
adjusted UI to view more data of a running trigger
1 parent 3e091e2 commit 471ea70

File tree

10 files changed

+73
-30
lines changed

10 files changed

+73
-30
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## v1.6.1
44
- simpler RetryStrategy - as function
5+
- showing last ping
6+
- showing execution time or still running triggers
7+
- saver way to keep track of running triggers
58

69
## v1.6.0 - (2025-03-11)
710

core/src/main/java/org/sterl/spring/persistent_tasks/api/Trigger.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public class Trigger {
2323

2424
private OffsetDateTime runAt = OffsetDateTime.now();
2525

26+
private OffsetDateTime lastPing;
27+
2628
private OffsetDateTime start;
2729

2830
private OffsetDateTime end;

core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Optional;
99
import java.util.concurrent.Future;
1010

11+
import org.slf4j.event.Level;
1112
import org.springframework.lang.NonNull;
1213
import org.springframework.transaction.annotation.Propagation;
1314
import org.springframework.transaction.annotation.Transactional;
@@ -80,7 +81,9 @@ public SchedulerEntity getScheduler() {
8081
}
8182

8283
public Optional<SchedulerEntity> findStatus(String name) {
83-
return editSchedulerStatus.find(name);
84+
if (name == null) return Optional.empty();
85+
else if (name.equals(this.name)) return Optional.of(getScheduler());
86+
else return editSchedulerStatus.find(name);
8487
}
8588

8689
/**
@@ -111,7 +114,7 @@ public List<Future<TriggerKey>> triggerNextTasks(OffsetDateTime timeDue) {
111114

112115
return taskExecutor.submit(result);
113116
} else {
114-
log.debug("No free threads {}/{} right now to run jobs due for: {}",
117+
log.info("No free threads {}/{} right now to run jobs due for: {}",
115118
taskExecutor.getFreeThreads(),
116119
taskExecutor.getMaxThreads(),
117120
timeDue);
@@ -151,7 +154,7 @@ public List<TriggerEntity> rescheduleAbandonedTasks(OffsetDateTime timeout) {
151154
.toList();
152155

153156
int running = triggerService.markTriggersAsRunning(runningKeys, name);
154-
log.debug("({}) - {} trigger(s) are running on {} schedulers",
157+
log.atLevel(running > 0 ? Level.INFO : Level.DEBUG).log("({}) - {} trigger(s) are running on {} schedulers",
155158
running, runningKeys, schedulers);
156159
return triggerService.rescheduleAbandonedTasks(timeout);
157160
}

core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/component/TaskExecutorComponent.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ public class TaskExecutorComponent implements Closeable {
4242
private Duration maxShutdownWaitTime = Duration.ofSeconds(10);
4343
@Nullable
4444
private ExecutorService executor;
45+
// also the LOCK object ...
4546
private final ConcurrentHashMap<TriggerEntity, Future<TriggerKey>> runningTasks = new ConcurrentHashMap<>();
4647
private final AtomicBoolean stopped = new AtomicBoolean(true);
47-
48+
4849
public TaskExecutorComponent(String schedulerName, TriggerService triggerService, int maxThreads) {
4950
super();
5051
this.schedulerName = schedulerName;
@@ -54,7 +55,8 @@ public TaskExecutorComponent(String schedulerName, TriggerService triggerService
5455

5556
@NonNull
5657
public List<Future<TriggerKey>> submit(List<TriggerEntity> trigger) {
57-
if (trigger == null || trigger.isEmpty()) return Collections.emptyList();
58+
if (trigger == null || trigger.isEmpty())
59+
return Collections.emptyList();
5860

5961
final List<Future<TriggerKey>> result = new ArrayList<>(trigger.size());
6062
for (TriggerEntity triggerEntity : trigger) {
@@ -71,9 +73,13 @@ public Future<TriggerKey> submit(@Nullable TriggerEntity trigger) {
7173
if (stopped.get() || executor == null) {
7274
throw new IllegalStateException("Executor of " + schedulerName + " is already stopped");
7375
}
74-
75-
final var result = executor.submit(() -> runTrigger(trigger));
76-
runningTasks.put(trigger, result);
76+
77+
Future<TriggerKey> result;
78+
synchronized (runningTasks) {
79+
result = executor.submit(() -> runTrigger(trigger));
80+
runningTasks.put(trigger, result);
81+
}
82+
7783
return result;
7884
}
7985

@@ -88,7 +94,7 @@ private TriggerKey runTrigger(TriggerEntity trigger) {
8894

8995
public void start() {
9096
if (stopped.compareAndExchange(true, false)) {
91-
synchronized (stopped) {
97+
synchronized (runningTasks) {
9298
runningTasks.clear();
9399
executor = Executors.newFixedThreadPool(maxThreads.get());
94100
log.info("Started {} with {} threads.", schedulerName, maxThreads.get());
@@ -99,7 +105,7 @@ public void start() {
99105
@Override
100106
public void close() {
101107
if (stopped.compareAndExchange(false, true)) {
102-
synchronized (stopped) {
108+
synchronized (runningTasks) {
103109
doShutdown();
104110
}
105111
}
@@ -109,8 +115,8 @@ private void doShutdown() {
109115
if (executor != null) {
110116
executor.shutdown();
111117
if (runningTasks.size() > 0) {
112-
log.info("Shutdown {} with {} running tasks, waiting for {}.",
113-
schedulerName, runningTasks.size(), maxShutdownWaitTime);
118+
log.info("Shutdown {} with {} running tasks, waiting for {}.", schedulerName, runningTasks.size(),
119+
maxShutdownWaitTime);
114120

115121
try {
116122
executor.awaitTermination(maxShutdownWaitTime.getSeconds(), TimeUnit.SECONDS);
@@ -129,8 +135,8 @@ private void doShutdown() {
129135

130136
public void shutdownNow() {
131137
if (stopped.compareAndExchange(false, true)) {
132-
if (executor != null) {
133-
synchronized (executor) {
138+
synchronized (runningTasks) {
139+
if (executor != null) {
134140
executor.shutdownNow();
135141
log.info("Force stop {} with {} running tasks", schedulerName, runningTasks.size());
136142
runningTasks.clear();
@@ -146,7 +152,7 @@ public int getFreeThreads() {
146152
}
147153
return Math.max(maxThreads.get() - runningTasks.size(), 0);
148154
}
149-
155+
150156
public int countRunning() {
151157
return runningTasks.size();
152158
}
@@ -158,14 +164,15 @@ public Collection<Future<TriggerKey>> getRunningTasks() {
158164
public boolean isStopped() {
159165
return stopped.get() || maxThreads.get() <= 0;
160166
}
161-
167+
162168
public List<TriggerEntity> getRunningTriggers() {
163169
return Collections.list(this.runningTasks.keys());
164170
}
165171

166172
public void setMaxThreads(int value) {
167173
this.maxThreads.set(value);
168174
}
175+
169176
public int getMaxThreads() {
170177
return isStopped() ? 0 : this.maxThreads.get();
171178
}

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerConverter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public Trigger convert(TriggerEntity source) {
1616
result.setId(source.getId());
1717
result.setInstanceId(source.getId());
1818
result.setRunningOn(source.getRunningOn());
19+
result.setLastPing(source.getLastPing());
1920
return result;
2021
}
2122
}

example/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<spt.version>1.5.6</spt.version>
1818
-->
1919
<properties>
20-
<spt.version>1.6.0-SNAPSHOT</spt.version>
20+
<spt.version>1.6.1-SNAPSHOT</spt.version>
2121
</properties>
2222

2323
<dependencies>

example/src/main/java/org/sterl/spring/example_app/ExampleApplication.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.sterl.spring.example_app;
22

33
import java.net.UnknownHostException;
4+
import java.time.Duration;
45

56
import org.springdoc.core.models.GroupedOpenApi;
67
import org.springframework.boot.SpringApplication;
@@ -22,6 +23,7 @@
2223
import org.sterl.spring.persistent_tasks.scheduler.SchedulerService;
2324
import org.sterl.spring.persistent_tasks.scheduler.component.EditSchedulerStatusComponent;
2425
import org.sterl.spring.persistent_tasks.scheduler.component.TaskExecutorComponent;
26+
import org.sterl.spring.persistent_tasks.scheduler.config.SchedulerConfig;
2527
import org.sterl.spring.persistent_tasks.scheduler.config.SchedulerConfig.SchedulerCustomizer;
2628
import org.sterl.spring.persistent_tasks.trigger.TriggerService;
2729
import org.sterl.spring.persistent_tasks_ui.EnableSpringPersistentTasksUI;
@@ -65,14 +67,14 @@ GroupedOpenApi springPersistentTasksApi() {
6567

6668
// just one more for demonstration
6769
@Bean(name = "schedulerB", initMethod = "start", destroyMethod = "stop")
68-
@SuppressWarnings("resource")
6970
SchedulerService schedulerB(
7071
TriggerService triggerService,
7172
EditSchedulerStatusComponent editSchedulerStatus,
7273
TransactionTemplate trx) throws UnknownHostException {
7374

74-
return new SchedulerService("schedulerB", triggerService,
75-
new TaskExecutorComponent(triggerService, 7), editSchedulerStatus, trx);
75+
return SchedulerConfig.newSchedulerService("schedulerB",
76+
triggerService,
77+
editSchedulerStatus, 7, Duration.ofSeconds(1), trx);
7678
}
7779

7880
@Bean

ui/src/scheduler/views/scheduler.view.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,28 @@ const SchedulerStatusView = ({ scheduler }: Props) => {
2020
Last Ping: {durationSince(new Date(scheduler.lastPing))}
2121
</Col>
2222
<Col>
23-
<Form.Label htmlFor={"slot-" + name}>
23+
<Form.Label htmlFor={"slot-" + scheduler.id}>
2424
{"Running " +
25-
scheduler.runnungTasks +
25+
scheduler.runningTasks +
2626
" of " +
2727
scheduler.tasksSlotCount}
2828
</Form.Label>
2929
<ProgressBar
30-
id={"slot-" + name}
30+
id={"slot-" + scheduler.id}
3131
animated={true}
3232
min={0}
33-
now={scheduler.runnungTasks}
33+
now={scheduler.runningTasks}
3434
max={scheduler.tasksSlotCount}
3535
></ProgressBar>
3636
</Col>
3737
</Row>
3838
<Row>
3939
<Col>
40-
<Form.Label htmlFor={"cpu-" + name}>CPU</Form.Label>
40+
<Form.Label htmlFor={"cpu-" + scheduler.id}>
41+
CPU
42+
</Form.Label>
4143
<ProgressBar
42-
id={"cpu-" + name}
44+
id={"cpu-" + scheduler.id}
4345
animated={true}
4446
min={0}
4547
now={scheduler.systemLoadAverage}
@@ -52,14 +54,14 @@ const SchedulerStatusView = ({ scheduler }: Props) => {
5254
></ProgressBar>
5355
</Col>
5456
<Col>
55-
<Form.Label htmlFor={"memory-" + name}>
57+
<Form.Label htmlFor={"memory-" + scheduler.id}>
5658
Memory{" "}
5759
{formatMemory(scheduler.usedHeap) +
5860
" of " +
5961
formatMemory(scheduler.maxHeap)}
6062
</Form.Label>
6163
<ProgressBar
62-
id={"memory-" + name}
64+
id={"memory-" + scheduler.id}
6365
animated={true}
6466
min={0}
6567
now={scheduler.usedHeap}

ui/src/server-api.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export interface Trigger {
6363
runningOn: string;
6464
createdTime: string;
6565
runAt: string;
66+
lastPing: string;
6667
start: string;
6768
end: string;
6869
executionCount: number;

ui/src/shared/view/trigger-list-item.view.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ const TriggerItemView = ({
117117

118118
export default TriggerItemView;
119119

120+
function runningSince(value?: string) {
121+
if (!value) return "";
122+
const msRuntime = new Date().getTime() - new Date(value).getTime();
123+
return `since ${formatMs(msRuntime)}`;
124+
}
125+
120126
const TriggerCompactView = ({ trigger }: { trigger: Trigger }) => (
121127
<Row className="align-items-center">
122128
<Col className="col-2">
@@ -138,7 +144,9 @@ const TriggerCompactView = ({ trigger }: { trigger: Trigger }) => (
138144
{trigger.runningOn ? (
139145
<LabeledText
140146
label={`Running on (${trigger.executionCount})`}
141-
value={trigger.runningOn}
147+
value={
148+
trigger.runningOn + " " + runningSince(trigger.start)
149+
}
142150
/>
143151
) : (
144152
<LabeledText
@@ -200,10 +208,24 @@ const TriggerDetailsView = ({
200208
<Col md="6" xl="4">
201209
<LabeledText
202210
label="Duration MS"
203-
value={formatMs(trigger.runningDurationInMs)}
211+
value={
212+
trigger.runningDurationInMs
213+
? formatMs(trigger.runningDurationInMs)
214+
: runningSince(trigger.start)
215+
}
204216
/>
205217
</Col>
206218
</Row>
219+
{trigger.lastPing ? (
220+
<Row>
221+
<Col md="12">
222+
<LabeledText
223+
label="Last keep alive ping"
224+
value={formatShortDateTime(trigger.lastPing)}
225+
/>
226+
</Col>
227+
</Row>
228+
) : undefined}
207229
<Row className="mt-2">
208230
<Col>
209231
<TriggerHistoryListView triggers={history} />

0 commit comments

Comments
 (0)