Skip to content

Commit 4307509

Browse files
authored
Transactional events (#7)
* events now in transactions * update * adjusted transaction handling during the flow * write history only if the trigger is done * adjusted UI that it displays running on * showing trigger stats * failing task may succeed * update changelog * adjusted test
1 parent 4bd36b3 commit 4307509

File tree

57 files changed

+793
-415
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+793
-415
lines changed

CHANGELOG.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,48 @@
11
# Changelog
22

3+
## v1.5.0
4+
5+
- Adjusted transaction handling for trigger life cycle events
6+
- Base event entry is only written for done/finished trigger
7+
- Base statistics added for a task
8+
39
## v1.4.6 - (2025-01-08)
410

5-
- Trigger history with more details - not waiting for the transaction
11+
- Trigger history with more details - not waiting for the transaction
612

713
## v1.4.5 - (2025-01-08)
814

9-
- Adjusted path matching to support sub routes for an SPA web app
15+
- Adjusted path matching to support sub routes for an SPA web app
1016

1117
## v1.4.4 - (2025-01-08)
1218

13-
- Fixed UI routing
14-
- added support for thymeleaf - adding index.html to template folder
19+
- Fixed UI routing
20+
- added support for thymeleaf - adding index.html to template folder
1521

1622
## v1.4.3 - (2025-01-08)
1723

18-
- Scheduler service leaves current transaction before executing task
24+
- Scheduler service leaves current transaction before executing task
1925

2026
## v1.4.2 - (2025-01-06)
2127

22-
- Fixed count by TaskId
23-
- added search by ID to the UI
24-
- added search by task to history
28+
- Fixed count by TaskId
29+
- added search by ID to the UI
30+
- added search by task to history
2531

2632
## v1.4.1 - (2025-01-06)
2733

28-
- Added state to the TriggerLifeCycleEvent
29-
- @Transactional annotation is taken from the method first
34+
- Added state to the TriggerLifeCycleEvent
35+
- @Transactional annotation is taken from the method first
3036

3137
## v1.4.0 - (2025-01-05)
3238

33-
- @Transactional Annotation support
34-
- PersistentTask instead of Task or SpringBeanTask
35-
39+
- @Transactional Annotation support
40+
- PersistentTask instead of Task or SpringBeanTask
3641

3742
## v1.3.1 - (2025-01-02)
3843

39-
- Bugfixes
40-
- Sprign Transaction Template support
44+
- Bugfixes
45+
- Sprign Transaction Template support
4146

4247
## v1.3.0 - (2025-01-01)
4348

RUN_AND_BUILD.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
mvn versions:display-dependency-updates
2-
mvn versions:set -DnewVersion=1.4.6 -DgenerateBackupPoms=false
3-
git tag -a v1.4.6 -m "v1.4.6 release"
4-
mvn versions:set -DnewVersion=1.4.7-SNAPSHOT -DgenerateBackupPoms=false
2+
mvn versions:set -DnewVersion=1.5.0 -DgenerateBackupPoms=false
3+
git tag -a v1.5.0 -m "v1.5.0 release"
4+
mvn versions:set -DnewVersion=1.5.1-SNAPSHOT -DgenerateBackupPoms=false
55

66
## postgres
7+
78
docker run --name pg-container -e POSTGRES_USER=sa -e POSTGRES_PASSWORD=veryStrong123 -p 5432:5432 -d postgres
89

910
## azure-sql-edge
11+
1012
docker run --cap-add SYS_PTRACE -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=veryStrong123' -p 1433:1433 --name azuresqledge -d mcr.microsoft.com/azure-sql-edge
1113

1214
## MariaDB
15+
1316
docker run -e MYSQL_ROOT_PASSWORD=veryStrong123 -e MYSQL_DATABASE=testdb -e MYSQL_USER=sa -e MYSQL_PASSWORD=veryStrong123 -p 3306:3306 -d mariadb:latest
1417

1518
## MySQL
19+
1620
docker run -e MYSQL_ROOT_PASSWORD=veryStrong123 -e MYSQL_DATABASE=testdb -e MYSQL_USER=sa -e MYSQL_PASSWORD=veryStrong123 -p 3306:3306 -d mysql

core/src/main/java/org/sterl/spring/persistent_tasks/PersistentTaskService.java

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

1111
import org.springframework.context.event.EventListener;
12+
import org.springframework.data.domain.Pageable;
1213
import org.springframework.lang.NonNull;
1314
import org.springframework.stereotype.Service;
1415
import org.springframework.transaction.annotation.Transactional;
@@ -55,30 +56,48 @@ public Optional<TriggerData> getLastTriggerData(TriggerKey key) {
5556
}
5657
}
5758

59+
public Optional<TriggerData> getLastDetailData(TriggerKey key) {
60+
var data = historyService.findAllDetailsForKey(key, Pageable.ofSize(1));
61+
if (data.isEmpty()) return Optional.empty();
62+
return Optional.of(data.getContent().get(0).getData());
63+
}
64+
5865
@EventListener
5966
void queue(TriggerTaskCommand<? extends Serializable> event) {
6067
if (event.triggers().size() == 1) {
6168
runOrQueue(event.triggers().iterator().next());
6269
} else {
63-
queueAll(event.triggers());
70+
queue(event.triggers());
6471
}
6572
}
6673

6774
/**
68-
* Queues the given triggers.
75+
* Queues/updates the given triggers, if the {@link TriggerKey} is already present
6976
*
7077
* @param <T> the state type
7178
* @param triggers the triggers to add
7279
* @return the {@link TriggerKey}
7380
*/
7481
@Transactional(timeout = 10)
7582
@NonNull
76-
public <T extends Serializable> List<TriggerKey> queueAll(Collection<AddTriggerRequest<T>> triggers) {
83+
public <T extends Serializable> List<TriggerKey> queue(Collection<AddTriggerRequest<T>> triggers) {
7784
return triggers.stream() //
7885
.map(t -> triggerService.queue(t)) //
7986
.map(TriggerEntity::getKey) //
8087
.toList();
8188
}
89+
/**
90+
* Queues/updates the given trigger, if the {@link TriggerKey} is already present.
91+
*
92+
* @param <T> the state type
93+
* @param trigger the trigger to add
94+
* @return the {@link TriggerKey}
95+
*/
96+
@Transactional(timeout = 5)
97+
@NonNull
98+
public <T extends Serializable> TriggerKey queue(AddTriggerRequest<T> trigger) {
99+
return triggerService.queue(trigger).getKey();
100+
}
82101

83102
/**
84103
* Runs the given trigger if a free threads are available

core/src/main/java/org/sterl/spring/persistent_tasks/SpringPersistentTasksConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
44
import org.springframework.context.annotation.ComponentScan;
55
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.scheduling.annotation.EnableAsync;
67
import org.springframework.scheduling.annotation.EnableScheduling;
78

89
@Configuration
910
@EnableScheduling
11+
@EnableAsync
1012
@AutoConfigurationPackage(basePackageClasses = EnableSpringPersistentTasks.class)
1113
@ComponentScan(basePackageClasses = EnableSpringPersistentTasks.class)
1214
public class SpringPersistentTasksConfig {

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

Lines changed: 0 additions & 15 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/api/Trigger.java

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

33
import java.time.OffsetDateTime;
44

5-
import org.sterl.spring.persistent_tasks.shared.model.TriggerStatus;
6-
75
import lombok.Data;
86

97
@Data
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.sterl.spring.persistent_tasks.shared.model;
1+
package org.sterl.spring.persistent_tasks.api;
22

33
import java.util.EnumSet;
44
import java.util.Set;

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
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.TaskStatusHistoryOverview;
1314
import org.sterl.spring.persistent_tasks.api.TriggerKey;
15+
import org.sterl.spring.persistent_tasks.api.TriggerStatus;
1416
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryDetailEntity;
1517
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryLastStateEntity;
1618
import org.sterl.spring.persistent_tasks.history.repository.TriggerHistoryDetailRepository;
1719
import org.sterl.spring.persistent_tasks.history.repository.TriggerHistoryLastStateRepository;
18-
import org.sterl.spring.persistent_tasks.shared.model.TriggerStatus;
1920
import org.sterl.spring.persistent_tasks.shared.stereotype.TransactionalService;
2021
import org.sterl.spring.persistent_tasks.trigger.TriggerService;
2122
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
@@ -64,7 +65,7 @@ public Page<TriggerHistoryDetailEntity> findAllDetailsForKey(TriggerKey key) {
6465
return findAllDetailsForKey(key, PageRequest.of(0, 100));
6566
}
6667
public Page<TriggerHistoryDetailEntity> findAllDetailsForKey(TriggerKey key, Pageable page) {
67-
page = sortByIdIfNeeded(page);
68+
page = applyDefaultSortIfNeeded(page);
6869
return triggerHistoryDetailRepository.listKnownStatusFor(key, page);
6970
}
7071

@@ -97,7 +98,7 @@ public long countTriggers(TriggerKey key) {
9798
public Page<TriggerHistoryLastStateEntity> findTriggerState(
9899
@Nullable TriggerKey key, Pageable page) {
99100

100-
page = sortByIdIfNeeded(page);
101+
page = applyDefaultSortIfNeeded(page);
101102
if (key == null) return triggerHistoryLastStateRepository.findAll(page);
102103
if (key.getId() == null && key.getTaskName() == null) return triggerHistoryLastStateRepository.findAll(page);
103104
if (key.getId() == null && key.getTaskName() != null) {
@@ -109,11 +110,15 @@ public Page<TriggerHistoryLastStateEntity> findTriggerState(
109110
page);
110111
}
111112

112-
private Pageable sortByIdIfNeeded(Pageable page) {
113+
private Pageable applyDefaultSortIfNeeded(Pageable page) {
113114
if (page.getSort() == Sort.unsorted()) {
114115
return PageRequest.of(page.getPageNumber(), page.getPageSize(),
115-
Sort.by(Direction.DESC, "id"));
116+
Sort.by(Direction.DESC, "data.createdTime", "id"));
116117
}
117118
return page;
118119
}
120+
121+
public List<TaskStatusHistoryOverview> taskStatusHistory() {
122+
return triggerHistoryLastStateRepository.listTriggerStatus();
123+
}
119124
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
import org.apache.commons.lang3.StringUtils;
66
import org.springframework.data.domain.Pageable;
7-
import org.springframework.data.domain.Sort.Direction;
87
import org.springframework.data.web.PageableDefault;
98
import org.springframework.data.web.PagedModel;
109
import org.springframework.web.bind.annotation.GetMapping;
1110
import org.springframework.web.bind.annotation.PathVariable;
1211
import org.springframework.web.bind.annotation.RequestMapping;
1312
import org.springframework.web.bind.annotation.RequestParam;
1413
import org.springframework.web.bind.annotation.RestController;
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,12 +32,16 @@ public List<Trigger> listInstances(@PathVariable("instanceId") long instanceId)
3232
return FromTriggerStateDetailEntity.INSTANCE.convert( //
3333
historyService.findAllDetailsForInstance(instanceId));
3434
}
35+
@GetMapping("task-status-history")
36+
public List<TaskStatusHistoryOverview> taskStatusHistory() {
37+
return historyService.taskStatusHistory();
38+
}
3539

3640
@GetMapping("history")
3741
public PagedModel<Trigger> list(
3842
@RequestParam(name = "id", required = false) String id,
3943
@RequestParam(name = "taskName", required = false) String taskName,
40-
@PageableDefault(size = 100, direction = Direction.DESC, sort = "id") Pageable pageable) {
44+
@PageableDefault(size = 100) Pageable pageable) {
4145

4246
return FromLastTriggerStateEntity.INSTANCE.toPage( //
4347
historyService.findTriggerState(

0 commit comments

Comments
 (0)