Skip to content

Commit 31c45c5

Browse files
authored
added interceptor to write task name and id to MDC logging context (#9)
* added interceptor to write task name and id to MDC logging context * added release script * update to changelog * include tests in release * cleanup the doc * added event tests
1 parent cddbcc2 commit 31c45c5

File tree

15 files changed

+234
-198
lines changed

15 files changed

+234
-198
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v1.5.5 - (2025-01-19)
4+
5+
- Added MdcTriggerInterceptor
6+
37
## v1.5.4 - (2025-01-14)
48

59
- adjusted trigger cols that the UI does not break

README.md

Lines changed: 17 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Focus is the usage with spring boot and JPA.
88

99
Secondary goal is to support [Poor mans Workflow](https://github.com/sterlp/pmw)
1010

11+
# Documentation
12+
13+
Use for more advanced doc the [WIKI](https://github.com/sterlp/spring-persistent-tasks/wiki).
14+
The README contains a shorter how to use.
15+
1116
# DBs for storage
1217

1318
## Tested in the pipeline
@@ -27,14 +32,17 @@ Secondary goal is to support [Poor mans Workflow](https://github.com/sterlp/pmw)
2732

2833
- mySQL: sequences are not supported
2934

30-
# Setup and Run a Task
35+
# JavaDoc
3136

3237
- [JavaDoc](https://sterlp.github.io/spring-persistent-tasks/javadoc-core/org/sterl/spring/persistent_tasks/PersistentTaskService.html)
3338

34-
## Maven
39+
# Quickstart
3540

3641
- [Maven Central spring-persistent-tasks-core](https://central.sonatype.com/artifact/org.sterl.spring/spring-persistent-tasks-core/versions)
3742

43+
## Setup with Maven
44+
45+
3846
```xml
3947
<dependency>
4048
<groupId>org.sterl.spring</groupId>
@@ -51,61 +59,7 @@ Secondary goal is to support [Poor mans Workflow](https://github.com/sterlp/pmw)
5159
public class ExampleApplication {
5260
```
5361

54-
## Setup a spring persistent task
55-
56-
### As a class
57-
58-
```java
59-
@Component(BuildVehicleTask.NAME)
60-
@RequiredArgsConstructor
61-
@Slf4j
62-
public class BuildVehicleTask implements PersistentTask<Vehicle> {
63-
64-
private static final String NAME = "buildVehicleTask";
65-
public static final TaskId<Vehicle> ID = new TaskId<>(NAME);
66-
67-
private final VehicleRepository vehicleRepository;
68-
69-
@Override
70-
public void accept(Vehicle vehicle) {
71-
// do stuff
72-
// save
73-
vehicleRepository.save(vehicle);
74-
}
75-
// OPTIONAL
76-
@Override
77-
public RetryStrategy retryStrategy() {
78-
// run 5 times, multiply the execution count with 4, add the result in HOURS to now.
79-
return new MultiplicativeRetryStrategy(5, ChronoUnit.HOURS, 4)
80-
}
81-
// OPTIONAL
82-
// if the task in accept requires a DB transaction, join them together with the framework
83-
// if true the TransactionTemplate is used. Set here any timeouts.
84-
@Override
85-
public boolean isTransactional() {
86-
return true;
87-
}
88-
}
89-
```
90-
91-
Consider setting a timeout to the `TransactionTemplate`:
92-
93-
```java
94-
@Bean
95-
TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
96-
TransactionTemplate template = new TransactionTemplate(transactionManager);
97-
template.setTimeout(10);
98-
return template;
99-
}
100-
```
101-
102-
### As a closure
103-
104-
Simple task will use defaults:
105-
106-
- Not a transactional task, e.g. HTTP calls
107-
- 4 executions, one regular and 3 retries, linear
108-
- using minutes with an offset of 1 which is added to now
62+
## Create a Task
10963

11064
```java
11165
@Bean
@@ -114,79 +68,22 @@ PersistentTask<Vehicle> task1(VehicleHttpConnector vehicleHttpConnector) {
11468
}
11569
```
11670

117-
### Task Transaction Management
118-
119-
[Transaction-Management Task](https://github.com/sterlp/spring-persistent-tasks/wiki/Transaction-Management)
120-
121-
## Queue a task execution
122-
123-
### Direct usage of the `TriggerService` or `PersistentTaskService`.
124-
125-
```java
126-
private final TriggerService triggerService;
127-
private final PersistentTaskService persistentTaskService;
128-
129-
public void buildVehicle() {
130-
// Vehicle has to be Serializable
131-
final var v = new Vehicle();
132-
// set any data to v ...
133-
134-
// EITHER: queue it, will run later
135-
triggerService.queue(BuildVehicleTask.ID.newUniqueTrigger(v));
136-
137-
// OR: will queue it and run it if possible.
138-
// if the scheduler service is missing it is same as using the TriggerService
139-
persistentTaskService.runOrQueue(BuildVehicleTask.ID.newUniqueTrigger(v));
140-
}
141-
```
142-
143-
### Build complex Trigger
144-
145-
```java
146-
private final PersistentTaskService persistentTaskService;
147-
148-
public void buildVehicle() {
149-
var trigger = TaskTriggerBuilder
150-
.<Vehicle>newTrigger("task2")
151-
.id("my-id") // will overwrite existing triggers
152-
.state(new Vehicle("funny"))
153-
.runAfter(Duration.ofHours(2))
154-
.build();
155-
156-
persistentTaskService.runOrQueue(trigger);
157-
}
158-
```
159-
160-
### Use a Spring Event
71+
## Trigger a task
16172

16273
```java
163-
private final ApplicationEventPublisher eventPublisher;
74+
@Autowired
75+
PersistentTaskService persistentTaskService;
16476

165-
public void buildVehicle() {
166-
// Vehicle has to be Serializable
167-
final var v = new Vehicle();
168-
// send an event with the trigger inside - same as calling the PersistentTaskService
169-
eventPublisher.publishEvent(TriggerTaskCommand.of(BuildVehicleTask.ID.newUniqueTrigger(v)));
77+
public void triggerTask1(Vehicle vehicle) {
78+
persistentTaskService.runOrQueue(
79+
TaskTriggerBuilder.newTrigger("task1").state(vehicle).build());
17080
}
17181
```
17282

17383
### JUnit Tests
17484

17585
- [Persistent Task and Testing](https://github.com/sterlp/spring-persistent-tasks/wiki/Triggers-and-Tasks-in-JUnit-Tests)
17686

177-
178-
### Spring configuration options
179-
180-
| Property | Type | Description | Default Value |
181-
| ---------------------------------------------- | -------------------- | ------------------------------------------------------------------------ | ------------------ |
182-
| `spring.persistent-tasks.poll-rate` | `java.lang.Integer` | The interval at which the scheduler checks for new tasks, in seconds. | `30` |
183-
| `spring.persistent-tasks.max-threads` | `java.lang.Integer` | The number of threads to use; set to 0 to disable task processing. | `10` |
184-
| `spring.persistent-tasks.task-timeout` | `java.time.Duration` | The maximum time allowed for a task and scheduler to complete a task. | `PT5M` (5 minutes) |
185-
| `spring.persistent-tasks.poll-task-timeout` | `java.lang.Integer` | The interval at which the system checks for abandoned tasks, in seconds. | `300` (5 minutes) |
186-
| `spring.persistent-tasks.scheduler-enabled` | `java.lang.Boolean` | Indicates whether this node should handle triggers. | `true` |
187-
| `spring.persistent-tasks.history.delete-after` | `java.time.Duration` | The max age for a trigger in the hstory. | `PT72H` (30 days) |
188-
| `spring.persistent-tasks.history.delete-rate` | `java.time.Integer` | The interval at which old triggers are deleted, in hours. | `24` (24 hours) |
189-
19087
# Setup DB with Liquibase
19188

19289
Liquibase is supported. Either import all or just the required versions.
@@ -246,25 +143,6 @@ public class ExampleApplication {
246143

247144
![History](screenshots/history-screen.png)
248145

249-
## Spring Boot CSRF config for the UI
250-
251-
Axios should work with the following spring config out of the box with csrf:
252-
253-
```java
254-
@Bean
255-
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
256-
http
257-
.httpBasic(org.springframework.security.config.Customizer.withDefaults())
258-
.csrf(c ->
259-
c.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
260-
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
261-
);
262-
return http.build();
263-
}
264-
```
265-
266-
more informations: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
267-
268146
# Alternatives
269147

270148
- quartz

RUN_AND_BUILD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mvn versions:display-dependency-updates
2-
mvn versions:set -DnewVersion=1.5.3 -DgenerateBackupPoms=false
2+
mvn versions:set -DnewVersion=1.5.5 -DgenerateBackupPoms=false
33
git tag -a v1.5.3 -m "v1.5.3 release"
44
mvn versions:set -DnewVersion=1.5.4-SNAPSHOT -DgenerateBackupPoms=false
55

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
import lombok.Builder;
1111
import lombok.Data;
1212
import lombok.NoArgsConstructor;
13+
import lombok.ToString;
1314

1415
/**
1516
* Unique key of a trigger during it's execution. But it after that the same key
1617
* can be added if needed. Ensures that only one trigger with the same key
1718
* is currently scheduled for execution.
1819
*/
1920
@Data
20-
@Builder(toBuilder = true)
21+
@ToString
22+
@Builder
2123
@NoArgsConstructor
2224
@AllArgsConstructor
2325
public class TriggerKey implements Serializable {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.sterl.spring.persistent_tasks.exception;
2+
3+
import lombok.Getter;
4+
5+
public class SpringPersistentTaskException extends RuntimeException {
6+
private static final long serialVersionUID = 1L;
7+
@Getter
8+
protected final Object state;
9+
10+
public SpringPersistentTaskException(String message, Object state, Throwable cause) {
11+
super(message, cause);
12+
this.state = state;
13+
}
14+
15+
public SpringPersistentTaskException(String message, Object state) {
16+
super(message);
17+
this.state = state;
18+
}
19+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.concurrent.Future;
1111

1212
import org.springframework.lang.NonNull;
13+
import org.springframework.transaction.annotation.Propagation;
1314
import org.springframework.transaction.annotation.Transactional;
1415
import org.springframework.transaction.event.TransactionPhase;
1516
import org.springframework.transaction.event.TransactionalEventListener;
@@ -105,6 +106,7 @@ public List<Future<TriggerKey>> triggerNextTasks() {
105106
* This method should not be called in a transaction!
106107
* </p>
107108
*/
109+
@Transactional(propagation = Propagation.NEVER)
108110
@NonNull
109111
public List<Future<TriggerKey>> triggerNextTasks(OffsetDateTime timeDue) {
110112
if (taskExecutor.getFreeThreads() > 0) {

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/EditTriggerComponent.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.springframework.context.ApplicationEventPublisher;
1111
import org.springframework.lang.NonNull;
1212
import org.springframework.stereotype.Component;
13+
import org.springframework.transaction.annotation.Propagation;
1314
import org.springframework.transaction.annotation.Transactional;
1415
import org.sterl.spring.persistent_tasks.api.AddTriggerRequest;
1516
import org.sterl.spring.persistent_tasks.api.TriggerKey;
@@ -18,6 +19,7 @@
1819
import org.sterl.spring.persistent_tasks.trigger.event.TriggerAddedEvent;
1920
import org.sterl.spring.persistent_tasks.trigger.event.TriggerCanceledEvent;
2021
import org.sterl.spring.persistent_tasks.trigger.event.TriggerFailedEvent;
22+
import org.sterl.spring.persistent_tasks.trigger.event.TriggerRunningEvent;
2123
import org.sterl.spring.persistent_tasks.trigger.event.TriggerSuccessEvent;
2224
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
2325
import org.sterl.spring.persistent_tasks.trigger.repository.TriggerRepository;
@@ -40,9 +42,9 @@ public Optional<TriggerEntity> completeTaskWithSuccess(TriggerKey key, Serializa
4042

4143
result.ifPresent(t -> {
4244
t.complete(null);
45+
log.debug("{} set to status={}", key, t.getData().getStatus());
4346
publisher.publishEvent(new TriggerSuccessEvent(
4447
t.getId(), t.copyData(), state));
45-
log.debug("Setting {} to status={}", key, t.getData().getStatus());
4648
triggerRepository.delete(t);
4749
});
4850
return result;
@@ -146,4 +148,11 @@ public int markTriggersAsRunning(Collection<TriggerKey> keys, String runOn) {
146148
return triggerRepository.markTriggersAsRunning(keys, runOn,
147149
OffsetDateTime.now(), TriggerStatus.RUNNING);
148150
}
151+
152+
@Transactional(propagation = Propagation.SUPPORTS)
153+
public void triggerIsNowRunning(TriggerEntity trigger, Serializable state) {
154+
if (!trigger.isRunning()) trigger.runOn(trigger.getRunningOn());
155+
publisher.publishEvent(new TriggerRunningEvent(
156+
trigger.getId(), trigger.copyData(), state, trigger.getRunningOn()));
157+
}
149158
}

0 commit comments

Comments
 (0)