os.failsafe.executor.TaskRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of failsafe-executor Show documentation
Show all versions of failsafe-executor Show documentation
Persistent executor service for Java
package os.failsafe.executor;
import os.failsafe.executor.utils.Database;
import os.failsafe.executor.utils.SystemClock;
import os.failsafe.executor.utils.WhereBuilder;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
class TaskRepository {
private final Database database;
private final String tableName;
private final SystemClock systemClock;
private final String insertStmtMysqlOrMariaDb;
private final String insertStmtPostgres;
private final String insertStmtOracle;
private final String lockStmt;
private final String unlockStmt;
private final String saveFailureStmt;
private final String deleteFailureStmt;
private final String deleteStmt;
private final String findOneStmt;
private final String countAllStmt;
private final String findAllNotLockedOrderedByCreatedDateStmt;
private final String findAllPagingStmt;
private final String updatelockTimeStmt;
public TaskRepository(Database database, String tableName, SystemClock systemClock) {
this.database = database;
this.tableName = tableName;
this.systemClock = systemClock;
this.insertStmtMysqlOrMariaDb = String.format(//language=SQL
"INSERT IGNORE INTO %s" +
" (ID, NAME, PARAMETER, PLANNED_EXECUTION_TIME, CREATED_DATE, LOCK_TIME, FAIL_TIME, EXCEPTION_MESSAGE, STACK_TRACE, RETRY_COUNT, VERSION)" +
" VALUES" +
" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
tableName);
this.insertStmtPostgres = String.format(//language=SQL
"INSERT INTO %s" +
" (ID, NAME, PARAMETER, PLANNED_EXECUTION_TIME, CREATED_DATE, LOCK_TIME, FAIL_TIME, EXCEPTION_MESSAGE, STACK_TRACE, RETRY_COUNT, VERSION)" +
" VALUES" +
" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +
" ON CONFLICT DO NOTHING",
tableName);
this.insertStmtOracle = String.format(//language=SQL
"INSERT INTO %s" +
" (ID, NAME, PARAMETER, PLANNED_EXECUTION_TIME, CREATED_DATE, LOCK_TIME, FAIL_TIME, EXCEPTION_MESSAGE, STACK_TRACE, RETRY_COUNT, VERSION)" +
" SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FROM DUAL" +
" WHERE NOT EXISTS" +
" (SELECT ID FROM %s WHERE ID = ?)",
tableName, tableName);
this.lockStmt = String.format(//language=SQL
"UPDATE %s" +
" SET" +
" LOCK_TIME=?, VERSION=?" +
" WHERE ID=? AND VERSION=?",
this.tableName);
this.unlockStmt = String.format(//language=SQL
"UPDATE %s" +
" SET" +
" LOCK_TIME=NULL, PLANNED_EXECUTION_TIME=?, VERSION=?" +
" WHERE ID=? AND VERSION=?",
tableName);
this.saveFailureStmt = String.format(//language=SQL
"UPDATE %s" +
" SET" +
" LOCK_TIME=null, FAIL_TIME=?, EXCEPTION_MESSAGE=?, STACK_TRACE=?, VERSION=?" +
" WHERE ID=?",
tableName);
this.deleteFailureStmt = String.format(//language=SQL
"UPDATE %s" +
" SET" +
" FAIL_TIME=null, EXCEPTION_MESSAGE=null, STACK_TRACE=null, RETRY_COUNT=?, VERSION=?" +
" WHERE ID=? AND VERSION=?",
tableName);
this.deleteStmt = String.format(//language=SQL
"DELETE FROM %s WHERE ID = ? AND VERSION = ?", tableName);
this.findAllPagingStmt = String.format(//language=SQL
"SELECT * FROM %s %s", tableName, "%s");
this.countAllStmt = String.format(//language=SQL
"SELECT COUNT(*) FROM %s %s", tableName, "%s");
this.findOneStmt = String.format(//language=SQL
"SELECT * FROM %s WHERE ID = ?", tableName);
this.findAllNotLockedOrderedByCreatedDateStmt = String.format(//language=SQL
"SELECT * FROM %s WHERE" +
" FAIL_TIME IS NULL AND (LOCK_TIME IS NULL OR LOCK_TIME <= ?)" +
" AND PLANNED_EXECUTION_TIME <= ? AND NAME IN (%s)" +
" ORDER BY CREATED_DATE %s",
tableName, "%s", database.isMysqlOrMariaDb() ? "LIMIT ?" : "FETCH FIRST ? ROWS ONLY");
this.updatelockTimeStmt = String.format(//language=SQL
"UPDATE %s" +
" SET" +
" LOCK_TIME=?" +
" WHERE ID=? AND LOCK_TIME IS NOT NULL AND VERSION=?",
this.tableName);
}
String add(Task task) {
return database.connect(connection -> this.add(connection, task));
}
String add(Connection connection, Task task) {
LocalDateTime creationTime = systemClock.now();
int count = 0;
if (database.isOracle() || database.isH2()) {
count = addTaskInOracle(connection, task, creationTime);
} else if (database.isMysqlOrMariaDb()) {
count = addTaskInMysqlOrMariaDb(connection, task, creationTime);
} else if (database.isPostgres()) {
count = addTaskInPostgres(connection, task, creationTime);
}
return count == 1 ? task.getId() : null;
}
private int addTaskInMysqlOrMariaDb(Connection connection, Task task, LocalDateTime creationTime) {
ExecutionFailure executionFailure = task.getExecutionFailure();
return database.insert(connection, insertStmtMysqlOrMariaDb,
task.getId(),
task.getName(),
task.getParameter(),
Timestamp.valueOf(task.getPlannedExecutionTime()),
Timestamp.valueOf(creationTime),
null,
executionFailure != null ? Timestamp.valueOf(executionFailure.getFailTime()) : null,
executionFailure != null ? executionFailure.getExceptionMessage() : null,
executionFailure != null ? executionFailure.getStackTrace() : null,
task.getRetryCount(),
task.getVersion());
}
private int addTaskInPostgres(Connection connection, Task task, LocalDateTime creationTime) {
ExecutionFailure executionFailure = task.getExecutionFailure();
return database.insert(connection, insertStmtPostgres,
task.getId(),
task.getName(),
task.getParameter(),
Timestamp.valueOf(task.getPlannedExecutionTime()),
Timestamp.valueOf(creationTime),
null,
executionFailure != null ? Timestamp.valueOf(executionFailure.getFailTime()) : null,
executionFailure != null ? executionFailure.getExceptionMessage() : null,
executionFailure != null ? executionFailure.getStackTrace() : null,
task.getRetryCount(),
task.getVersion());
}
private int addTaskInOracle(Connection connection, Task task, LocalDateTime creationTime) {
ExecutionFailure executionFailure = task.getExecutionFailure();
return database.insert(connection, insertStmtOracle,
task.getId(),
task.getName(),
task.getParameter(),
Timestamp.valueOf(task.getPlannedExecutionTime()),
Timestamp.valueOf(creationTime),
null,
executionFailure != null ? Timestamp.valueOf(executionFailure.getFailTime()) : null,
executionFailure != null ? executionFailure.getExceptionMessage() : null,
executionFailure != null ? executionFailure.getStackTrace() : null,
task.getRetryCount(),
task.getVersion(),
task.getId());
}
Task findOne(String id) {
return database.selectOne(findOneStmt, this::mapToPersistentTask, id);
}
List findAll(String taskName, String parameter, Boolean failed, int offset, int limit, Sort... sorts) {
if (offset < 0) {
throw new IllegalArgumentException("Offset must be greater or equal 0");
}
if (limit <= 0) {
throw new IllegalArgumentException("Limit must be greater than 0");
}
if (sorts.length == 0) {
sorts = new Sort[]{new Sort(Sort.Field.CREATED_DATE, Sort.Direction.DESC), new Sort(Sort.Field.ID, Sort.Direction.DESC)};
}
WhereBuilder.WhereClause whereClause = new WhereBuilder(database)
.where(taskName, "NAME")
.where(parameter, "PARAMETER")
.isNullOrNotNull(failed, "FAIL_TIME")
.orderBy(sorts)
.limit(offset, limit)
.build();
String sql = String.format(findAllPagingStmt, whereClause.where);
return database.selectAll(sql, this::mapToPersistentTask, whereClause.params);
}
int count(String taskName, String parameter, Boolean failed) {
WhereBuilder.WhereClause whereClause = new WhereBuilder(database)
.where(taskName, "NAME")
.where(parameter, "PARAMETER")
.isNullOrNotNull(failed, "FAIL_TIME")
.build();
String sql = String.format(countAllStmt, whereClause.where);
return database.selectOne(sql, rs -> rs.getInt(1),
whereClause.params);
}
List lock(Connection connection, List toLock) {
LocalDateTime lockTime = systemClock.now();
Timestamp timestamp = Timestamp.valueOf(lockTime);
Object[][] params = new Object[toLock.size()][4];
for (int i = 0; i < params.length; i++) {
Task task = toLock.get(i);
params[i] = new Object[]{timestamp, task.getVersion() + 1, task.getId(), task.getVersion()};
params[i][0] = timestamp;
params[i][1] = task.getVersion() + 1;
params[i][2] = task.getId();
params[i][3] = task.getVersion();
}
int[] updateCount = database.executeBatchUpdate(connection, lockStmt, params);
List result = new ArrayList<>();
for (int i = 0; i < updateCount.length; i++) {
int executionResult = updateCount[i];
if (executionResult == Statement.SUCCESS_NO_INFO) {
throw new IllegalStateException("CAUTION! JDBC driver returns SUCCESS_NO_INFO. FailsafeExecutor depends on JDBC drivers ability to return the effected row count. For more details go to the FAQs!");
}
if (executionResult == 1) {
Task task = toLock.get(i);
result.add(new Task(task.getId(), task.getName(), task.getParameter(), task.getCreationTime(), task.getPlannedExecutionTime(), lockTime, null, task.getRetryCount(), task.getVersion() + 1));
}
}
return result;
}
void unlock(Task toUnLock, LocalDateTime nextPlannedExecutionTime) {
int effectedRows = database.update(unlockStmt,
Timestamp.valueOf(nextPlannedExecutionTime),
toUnLock.getVersion() + 1,
toUnLock.getId(),
toUnLock.getVersion());
if (effectedRows != 1) {
throw new RuntimeException(String.format("Could not unlock task %s", toUnLock.getId()));
}
}
List findAllNotLockedOrderedByCreatedDate(Connection connection, Set processableTasks, LocalDateTime plannedExecutionDateLessOrEquals, LocalDateTime lockTimeLessOrEqual, int limit) {
if (processableTasks.isEmpty()) {
return Collections.emptyList();
}
String selectStmt = String.format(findAllNotLockedOrderedByCreatedDateStmt, processableTasks.stream().map(s -> "?").collect(Collectors.joining(",")));
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy