Skip to content

Commit 2629e2f

Browse files
committed
showing trigger stats
1 parent e7ca753 commit 2629e2f

File tree

13 files changed

+215
-103
lines changed

13 files changed

+215
-103
lines changed

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

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.sterl.spring.persistent_tasks.api;
2+
3+
import java.time.OffsetDateTime;
4+
5+
public record TaskStatusHistoryOverview(
6+
String taskName,
7+
TriggerStatus status,
8+
Long executionCount,
9+
OffsetDateTime firstRun,
10+
OffsetDateTime lastRun,
11+
Number maxDurationMs,
12+
Number minDurationMs,
13+
Number avgDurationMs,
14+
Number avgExecutionCount
15+
) {
16+
}

core/src/main/java/org/sterl/spring/persistent_tasks/history/HistoryService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import org.springframework.data.domain.Sort;
1111
import org.springframework.data.domain.Sort.Direction;
1212
import org.springframework.lang.Nullable;
13-
import org.sterl.spring.persistent_tasks.api.TaskHistoryOverview;
13+
import org.sterl.spring.persistent_tasks.api.TaskStatusHistoryOverview;
1414
import org.sterl.spring.persistent_tasks.api.TriggerKey;
1515
import org.sterl.spring.persistent_tasks.api.TriggerStatus;
1616
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryDetailEntity;
@@ -118,7 +118,7 @@ private Pageable applyDefaultSortIfNeeded(Pageable page) {
118118
return page;
119119
}
120120

121-
public List<TaskHistoryOverview> taskHistory() {
122-
return triggerHistoryDetailRepository.listTaskHistoryOverview();
121+
public List<TaskStatusHistoryOverview> taskStatusHistory() {
122+
return triggerHistoryLastStateRepository.listTriggerStatus();
123123
}
124124
}

core/src/main/java/org/sterl/spring/persistent_tasks/history/api/TriggerHistoryResource.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import org.springframework.web.bind.annotation.RequestMapping;
1212
import org.springframework.web.bind.annotation.RequestParam;
1313
import org.springframework.web.bind.annotation.RestController;
14-
import org.sterl.spring.persistent_tasks.api.TaskHistoryOverview;
14+
import org.sterl.spring.persistent_tasks.api.TaskStatusHistoryOverview;
1515
import org.sterl.spring.persistent_tasks.api.Trigger;
1616
import org.sterl.spring.persistent_tasks.api.TriggerKey;
1717
import org.sterl.spring.persistent_tasks.history.HistoryService;
@@ -32,9 +32,9 @@ public List<Trigger> listInstances(@PathVariable("instanceId") long instanceId)
3232
return FromTriggerStateDetailEntity.INSTANCE.convert( //
3333
historyService.findAllDetailsForInstance(instanceId));
3434
}
35-
@GetMapping("task-history")
36-
public List<TaskHistoryOverview> taskHistory() {
37-
return historyService.taskHistory();
35+
@GetMapping("task-status-history")
36+
public List<TaskStatusHistoryOverview> taskStatusHistory() {
37+
return historyService.taskStatusHistory();
3838
}
3939

4040
@GetMapping("history")

core/src/main/java/org/sterl/spring/persistent_tasks/history/repository/TriggerHistoryDetailRepository.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,10 @@
44

55
import org.springframework.data.jpa.repository.Query;
66
import org.springframework.data.repository.query.Param;
7-
import org.sterl.spring.persistent_tasks.api.TaskHistoryOverview;
87
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryDetailEntity;
98

109
public interface TriggerHistoryDetailRepository extends HistoryTriggerRepository<TriggerHistoryDetailEntity> {
1110

12-
@Query("""
13-
SELECT new org.sterl.spring.persistent_tasks.api.TaskHistoryOverview(
14-
e.data.key.taskName,
15-
count(1) as entryCount,
16-
MIN(e.data.runAt) as firstRun,
17-
MAX(e.data.runAt) as lastRun,
18-
MAX(e.data.runningDurationInMs) as maxDuration,
19-
MIN(e.data.runningDurationInMs) as minDuration,
20-
AVG(e.data.runningDurationInMs) as avgDuration
21-
)
22-
FROM #{#entityName} e
23-
WHERE e.data.end IS NOT NULL
24-
GROUP BY e.data.key.taskName
25-
ORDER BY e.data.key.taskName ASC
26-
""")
27-
List<TaskHistoryOverview> listTaskHistoryOverview();
28-
2911
@Query("""
3012
SELECT e
3113
FROM #{#entityName} e
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
package org.sterl.spring.persistent_tasks.history.repository;
22

3+
import java.util.List;
4+
5+
import org.springframework.data.jpa.repository.Query;
6+
import org.sterl.spring.persistent_tasks.api.TaskStatusHistoryOverview;
37
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryLastStateEntity;
48

59
public interface TriggerHistoryLastStateRepository extends HistoryTriggerRepository<TriggerHistoryLastStateEntity> {
610

11+
@Query("""
12+
SELECT new org.sterl.spring.persistent_tasks.api.TaskStatusHistoryOverview(
13+
e.data.key.taskName,
14+
e.data.status,
15+
count(1),
16+
MIN(e.data.runAt) as firstRun,
17+
MAX(e.data.runAt) as lastRun,
18+
MAX(e.data.runningDurationInMs) as maxDuration,
19+
MIN(e.data.runningDurationInMs) as minDuration,
20+
AVG(e.data.runningDurationInMs) as avgDuration,
21+
AVG(e.data.executionCount) as avgExecutionCount
22+
)
23+
FROM #{#entityName} e
24+
GROUP BY e.data.key.taskName, e.data.status
25+
ORDER BY e.data.key.taskName ASC, e.data.status ASC
26+
""")
27+
List<TaskStatusHistoryOverview> listTriggerStatus();
728
}

core/src/test/java/org/sterl/spring/persistent_tasks/history/repository/TriggerHistoryDetailRepositoryTest.java

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.sterl.spring.persistent_tasks.history.repository;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.time.OffsetDateTime;
6+
import java.util.concurrent.atomic.AtomicLong;
7+
8+
import org.junit.jupiter.api.Test;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.sterl.spring.persistent_tasks.AbstractSpringTest;
11+
import org.sterl.spring.persistent_tasks.api.TriggerKey;
12+
import org.sterl.spring.persistent_tasks.api.TriggerStatus;
13+
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryLastStateEntity;
14+
import org.sterl.spring.persistent_tasks.shared.model.TriggerData;
15+
16+
class TriggerHistoryLastStateRepositoryTest extends AbstractSpringTest {
17+
18+
final AtomicLong idGenerator = new AtomicLong(0);
19+
@Autowired
20+
private TriggerHistoryLastStateRepository subject;
21+
22+
@Test
23+
void testListTriggerStatus() {
24+
// GIVEN
25+
subject.deleteAllInBatch();
26+
createStatus(new TriggerKey("1", "task1"), TriggerStatus.SUCCESS);
27+
createStatus(new TriggerKey("2", "task1"), TriggerStatus.SUCCESS);
28+
createStatus(new TriggerKey("3", "task1"), TriggerStatus.FAILED);
29+
createStatus(new TriggerKey("4", "task2"), TriggerStatus.SUCCESS);
30+
createStatus(new TriggerKey("5", "task2"), TriggerStatus.CANCELED);
31+
assertThat(subject.count()).isEqualTo(5);
32+
33+
// THEN
34+
var result = subject.listTriggerStatus();
35+
36+
// WHEN
37+
assertThat(result.size()).isEqualTo(4);
38+
// AND
39+
var i = 0;
40+
assertThat(result.get(i).taskName()).isEqualTo("task1");
41+
assertThat(result.get(i).status()).isEqualTo(TriggerStatus.FAILED);
42+
assertThat(result.get(i).executionCount()).isEqualTo(1L);
43+
// AND
44+
i = 1;
45+
assertThat(result.get(i).taskName()).isEqualTo("task1");
46+
assertThat(result.get(i).status()).isEqualTo(TriggerStatus.SUCCESS);
47+
assertThat(result.get(i).executionCount()).isEqualTo(2L);
48+
// AND
49+
i = 2;
50+
assertThat(result.get(i).taskName()).isEqualTo("task2");
51+
assertThat(result.get(i).status()).isEqualTo(TriggerStatus.CANCELED);
52+
assertThat(result.get(i).executionCount()).isEqualTo(1L);
53+
}
54+
55+
private TriggerHistoryLastStateEntity createStatus(TriggerKey key, TriggerStatus status) {
56+
final var now = OffsetDateTime.now();
57+
final var isCancel = status == TriggerStatus.CANCELED;
58+
59+
TriggerHistoryLastStateEntity result = new TriggerHistoryLastStateEntity();
60+
result.setId(idGenerator.incrementAndGet());
61+
result.setData(TriggerData
62+
.builder()
63+
.start(isCancel ? null : now.minusMinutes(1))
64+
.end(isCancel ? null : now)
65+
.createdTime(now)
66+
.key(key)
67+
.status(status)
68+
.runningDurationInMs(isCancel ? null : 600L)
69+
.build()
70+
);
71+
72+
return subject.save(result);
73+
}
74+
75+
}

ui/src/scheduler/scheduler.page.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import SchedulerStatusView from "@src/scheduler/views/scheduler.view";
2-
import HttpErrorView from "@src/shared/view/http-error.view";
2+
import { TaskStatusHistoryOverview } from "@src/server-api";
3+
import { formatMs } from "@src/shared/date.util";
34
import { useServerObject } from "@src/shared/http-request";
45
import useAutoRefresh from "@src/shared/use-auto-refresh";
6+
import HttpErrorView from "@src/shared/view/http-error.view";
7+
import StatusView from "@src/task/view/staus.view";
58
import { useEffect } from "react";
6-
import { Col, Row } from "react-bootstrap";
9+
import { Card, Col, ListGroup, Row } from "react-bootstrap";
710

811
const SchedulersPage = () => {
912
const schedulers = useServerObject<string[]>(
1013
"/spring-tasks-api/schedulers"
1114
);
1215
const tasks = useServerObject<string[]>("/spring-tasks-api/tasks");
16+
const taskHistory = useServerObject<TaskStatusHistoryOverview[]>(
17+
"/spring-tasks-api/task-status-history"
18+
);
1319

14-
useEffect(tasks.doGet, []);
15-
useAutoRefresh(10000, schedulers.doGet, []);
20+
useEffect(() => tasks.doGet(), []);
21+
useAutoRefresh(10000, () => schedulers.doGet(), []);
22+
useAutoRefresh(10000, () => taskHistory.doGet(), []);
1623

1724
return (
1825
<>
@@ -27,9 +34,58 @@ const SchedulersPage = () => {
2734
<SchedulerStatusView name={i} />
2835
</Col>
2936
))}
37+
38+
{tasks.data?.map((i) => (
39+
<Col key={i} xl="6" md="12" className="mb-2">
40+
<TaskStatusHistoryOverviewView
41+
name={i}
42+
status={taskHistory.data || []}
43+
/>
44+
</Col>
45+
))}
3046
</Row>
3147
</>
3248
);
3349
};
34-
3550
export default SchedulersPage;
51+
52+
const TaskStatusHistoryOverviewView = ({
53+
name,
54+
status,
55+
}: {
56+
name: string;
57+
status: TaskStatusHistoryOverview[];
58+
}) => (
59+
<Card>
60+
<Card.Header
61+
as="h5"
62+
className="d-flex justify-content-between align-items-center"
63+
>
64+
{name}
65+
</Card.Header>
66+
<ListGroup variant="flush">
67+
{status
68+
.filter((s) => s.taskName == name)
69+
.map((s) => (
70+
<ListGroup.Item key={s.taskName + s.status}>
71+
<Row className="align-items-center">
72+
<Col>
73+
<StatusView
74+
status={s.status}
75+
suffix={`: ${s.executionCount}`}
76+
/>
77+
</Col>
78+
<Col>avg: {formatMs(s.avgDurationMs)}</Col>
79+
<Col>max: {formatMs(s.maxDurationMs)}</Col>
80+
<Col>
81+
avg retry:{" "}
82+
{Math.round(
83+
Math.max(0, s.avgExecutionCount - 1) * 100
84+
) / 100}
85+
</Col>
86+
</Row>
87+
</ListGroup.Item>
88+
))}
89+
</ListGroup>
90+
</Card>
91+
);

ui/src/server-api.d.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,23 @@ export interface MultiplicativeRetryStrategy extends RetryStrategy {
4242
export interface SpringBeanTask<T> extends PersistentTask<T> {
4343
}
4444

45-
export interface TaskHistoryOverview {
45+
export interface TaskId<T> extends Serializable {
46+
name: string;
47+
}
48+
49+
export interface TaskTriggerBuilder<T> {
50+
}
51+
52+
export interface TaskStatusHistoryOverview {
4653
taskName: string;
54+
status: TriggerStatus;
4755
executionCount: number;
4856
firstRun: string;
4957
lastRun: string;
5058
maxDurationMs: number;
5159
minDurationMs: number;
5260
avgDurationMs: number;
53-
}
54-
55-
export interface TaskId<T> extends Serializable {
56-
name: string;
57-
}
58-
59-
export interface TaskTriggerBuilder<T> {
61+
avgExecutionCount: number;
6062
}
6163

6264
export interface TransactionalTask<T> extends PersistentTask<T> {

0 commit comments

Comments
 (0)