diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index a82d2941ccf3..41ef2be91a0a 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -1819,7 +1819,7 @@ public void poll(final Date timestamp) { } @DB - private Date scheduleNextBackupJob(final BackupScheduleVO backupSchedule) { + protected Date scheduleNextBackupJob(final BackupScheduleVO backupSchedule) { final Date nextTimestamp = DateUtil.getNextRunTime(backupSchedule.getScheduleType(), backupSchedule.getSchedule(), backupSchedule.getTimezone(), currentTimestamp); return Transaction.execute(new TransactionCallback() { @@ -1833,13 +1833,19 @@ public Date doInTransaction(TransactionStatus status) { }); } - private void checkStatusOfCurrentlyExecutingBackups() { + protected void checkStatusOfCurrentlyExecutingBackups() { final SearchCriteria sc = backupScheduleDao.createSearchCriteria(); sc.addAnd("asyncJobId", SearchCriteria.Op.NNULL); final List backupSchedules = backupScheduleDao.search(sc, null); for (final BackupScheduleVO backupSchedule : backupSchedules) { final Long asyncJobId = backupSchedule.getAsyncJobId(); final AsyncJobVO asyncJob = asyncJobManager.getAsyncJob(asyncJobId); + if (asyncJob == null) { + logger.debug("The async job [{}] of backup schedule [{}] does not exist anymore. Considering it as finished and scheduling the next backup job.", + asyncJobId, backupSchedule); + scheduleNextBackupJob(backupSchedule); + continue; + } switch (asyncJob.getStatus()) { case SUCCEEDED: case FAILED: diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java index 0eaa18ab39df..03e539fe6265 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -2432,4 +2432,18 @@ public void testRestoreBackupVolumeMismatch() { verify(vmInstanceDao, times(1)).findByIdIncludingRemoved(vmId); verify(volumeDao, times(1)).findByInstance(vmId); } + + @Test + public void checkStatusOfCurrentlyExecutingBackupsTestMissingAsyncJobSchedulesNextBackup() { + BackupScheduleVO backupSchedule = mock(BackupScheduleVO.class); + doReturn(1L).when(backupSchedule).getAsyncJobId(); + doReturn(mock(SearchCriteria.class)).when(backupScheduleDao).createSearchCriteria(); + doReturn(List.of(backupSchedule)).when(backupScheduleDao).search(any(), any()); + doReturn(null).when(asyncJobManager).getAsyncJob(1L); + doReturn(new Date()).when(backupManager).scheduleNextBackupJob(backupSchedule); + + backupManager.checkStatusOfCurrentlyExecutingBackups(); + + verify(backupManager).scheduleNextBackupJob(backupSchedule); + } }