@Override public <T extends Schedule> void updateSchedules( int projId, List<T> schedules, ScheduleUpdateAction<T> func) throws ResourceConflictException { Map<String, Integer> oldScheduleNames = idNameListToHashMap(dao.getScheduleNames(projId)); // Concurrent call of updateSchedules doesn't happen because having // ProjectControlStore means that the project is locked. // // However, ScheduleExecutor modifies schedules without locking the // project. Instead, ScheduleExecutor locks schedules. To avoid // concurrent update of schedules, here needs to lock schedules // before UPDATE. for (T schedule : schedules) { Integer matchedSchedId = oldScheduleNames.get(schedule.getWorkflowName()); if (matchedSchedId != null) { // found the same name. lock it and update ScheduleStatus status = dao.lockScheduleById(matchedSchedId); if (status != null) { ScheduleTime newSchedule = func.apply(status, schedule); dao.updateScheduleById( matchedSchedId, schedule.getWorkflowDefinitionId(), newSchedule.getRunTime().getEpochSecond(), newSchedule.getTime().getEpochSecond()); oldScheduleNames.remove(schedule.getWorkflowName()); } } else { // not found this name. inserting a new entry. catchConflict( () -> dao.insertSchedule( projId, schedule.getWorkflowDefinitionId(), schedule.getNextRunTime().getEpochSecond(), schedule.getNextScheduleTime().getEpochSecond()), "workflow_definition_id=%d", schedule.getWorkflowDefinitionId()); } } // delete unused schedules if (!oldScheduleNames.isEmpty()) { // those names don exist any more. handle .createStatement( "delete from schedules" + " where id " + inLargeIdListExpression(oldScheduleNames.values())) .execute(); } }
@Override public ScheduleStatus map(int index, ResultSet r, StatementContext ctx) throws SQLException { return ScheduleStatus.of( ScheduleTime.of( Instant.ofEpochSecond(r.getLong("next_schedule_time")), Instant.ofEpochSecond(r.getLong("next_run_time"))), getOptionalLong(r, "last_session_time").transform(it -> Instant.ofEpochSecond(it))); }