A simple task management framework designed to queue and execute asynchronous tasks with support for database persistence and a user-friendly interface. It can be used to implement scheduling patterns or outbound patterns.
Focus is the usage with spring boot and JPA.
Secondary goal is to support Poor mans Workflow
- H2
- azure-sql-edge (MSSQL)
- PostgreSQL
- MariaDB
- MSSQL, as azure-sql-edge is tested
- mySQL: sequences are not supported
<dependency>
<groupId>org.sterl.spring</groupId>
<artifactId>spring-persistent-tasks-core</artifactId>
<version>1.x.x</version>
</dependency>
@SpringBootApplication
@EnableSpringPersistentTasks
public class ExampleApplication {
@Component(BuildVehicleTask.NAME)
@RequiredArgsConstructor
@Slf4j
public class BuildVehicleTask implements PersistentTask<Vehicle> {
private static final String NAME = "buildVehicleTask";
public static final TaskId<Vehicle> ID = new TaskId<>(NAME);
private final VehicleRepository vehicleRepository;
@Override
public void accept(Vehicle vehicle) {
// do stuff
// save
vehicleRepository.save(vehicle);
}
// OPTIONAL
@Override
public RetryStrategy retryStrategy() {
// run 5 times, multiply the execution count with 4, add the result in HOURS to now.
return new MultiplicativeRetryStrategy(5, ChronoUnit.HOURS, 4)
}
// OPTIONAL
// if the task in accept requires a DB transaction, join them together with the framework
// if true the TransactionTemplate is used. Set here any timeouts.
@Override
public boolean isTransactional() {
return true;
}
}
Consider setting a timeout to the TransactionTemplate
:
@Bean
TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.setTimeout(10);
return template;
}
Simple task will use defaults:
- Not a transactional task, e.g. HTTP calls
- 4 executions, one regular and 3 retries, linear
- using minutes with an offset of 1 which is added to now
@Bean
PersistentTask<Vehicle> task1(VehicleHttpConnector vehicleHttpConnector) {
return v -> vehicleHttpConnector.send(v);
}
private final TriggerService triggerService;
private final PersistentTaskService persistentTaskService;
public void buildVehicle() {
// Vehicle has to be Serializable
final var v = new Vehicle();
// set any data to v ...
// EITHER: queue it, will run later
triggerService.queue(BuildVehicleTask.ID.newUniqueTrigger(v));
// OR: will queue it and run it if possible.
// if the scheduler service is missing it is same as using the TriggerService
persistentTaskService.runOrQueue(BuildVehicleTask.ID.newUniqueTrigger(v));
}
private final PersistentTaskService persistentTaskService;
public void buildVehicle() {
var trigger = TaskTriggerBuilder
.<Vehicle>newTrigger("task2")
.id("my-id") // will overwrite existing triggers
.state(new Vehicle("funny"))
.runAfter(Duration.ofHours(2))
.build();
persistentTaskService.runOrQueue(trigger);
}
private final ApplicationEventPublisher eventPublisher;
public void buildVehicle() {
// Vehicle has to be Serializable
final var v = new Vehicle();
// send an event with the trigger inside - same as calling the PersistentTaskService
eventPublisher.publishEvent(TriggerTaskCommand.of(BuildVehicleTask.ID.newUniqueTrigger(v)));
}
Property | Type | Description | Default Value |
---|---|---|---|
spring.persistent-tasks.poll-rate |
java.lang.Integer |
The interval at which the scheduler checks for new tasks, in seconds. | 30 |
spring.persistent-tasks.max-threads |
java.lang.Integer |
The number of threads to use; set to 0 to disable task processing. | 10 |
spring.persistent-tasks.task-timeout |
java.time.Duration |
The maximum time allowed for a task and scheduler to complete a task. | PT5M (5 minutes) |
spring.persistent-tasks.poll-task-timeout |
java.lang.Integer |
The interval at which the system checks for abandoned tasks, in seconds. | 300 (5 minutes) |
spring.persistent-tasks.scheduler-enabled |
java.lang.Boolean |
Indicates whether this node should handle triggers. | true |
spring.persistent-tasks.history.delete-after |
java.time.Duration |
The max age for a trigger in the hstory. | PT72H (30 days) |
spring.persistent-tasks.history.delete-rate |
java.time.Integer |
The interval at which old triggers are deleted, in hours. | 24 (24 hours) |
Liquibase is supported. Either import all or just the required versions.
<include file="spring-persistent-tasks/db.changelog-master.xml" />
<include file="spring-persistent-tasks/db/pt-changelog-v1.xml" />
<dependency>
<groupId>org.sterl.spring</groupId>
<artifactId>spring-persistent-tasks-ui</artifactId>
<version>1.x.x</version>
</dependency>
@SpringBootApplication
@EnableSpringPersistentTasks
@EnableSpringPersistentTasksUI
public class ExampleApplication {
Axios should work with the following spring config out of the box with csrf:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.httpBasic(org.springframework.security.config.Customizer.withDefaults())
.csrf(c ->
c.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
);
return http.build();
}
more informations: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
- quartz
- db-scheduler
- jobrunr