minor fixes in error handling and running exam update
This commit is contained in:
parent
a74d4c6e22
commit
e5ca068ccb
8 changed files with 40 additions and 34 deletions
|
@ -50,13 +50,11 @@ public final class CircuitBreaker<T> {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(CircuitBreaker.class);
|
private static final Logger log = LoggerFactory.getLogger(CircuitBreaker.class);
|
||||||
|
|
||||||
|
public static final String OPEN_CIRCUIT_BREAKER_EXCEPTION = "Open CircuitBreaker";
|
||||||
public static final int DEFAULT_MAX_FAILING_ATTEMPTS = 5;
|
public static final int DEFAULT_MAX_FAILING_ATTEMPTS = 5;
|
||||||
public static final long DEFAULT_MAX_BLOCKING_TIME = Constants.MINUTE_IN_MILLIS;
|
public static final long DEFAULT_MAX_BLOCKING_TIME = Constants.MINUTE_IN_MILLIS;
|
||||||
public static final long DEFAULT_TIME_TO_RECOVER = Constants.MINUTE_IN_MILLIS * 10;
|
public static final long DEFAULT_TIME_TO_RECOVER = Constants.MINUTE_IN_MILLIS * 10;
|
||||||
|
|
||||||
public static final RuntimeException OPEN_STATE_EXCEPTION =
|
|
||||||
new RuntimeException("Open CircuitBreaker");
|
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
CLOSED,
|
CLOSED,
|
||||||
HALF_OPEN,
|
HALF_OPEN,
|
||||||
|
@ -243,7 +241,7 @@ public final class CircuitBreaker<T> {
|
||||||
return protectedRun(supplier);
|
return protectedRun(supplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ofError(OPEN_STATE_EXCEPTION);
|
return Result.ofError(new RuntimeException(OPEN_CIRCUIT_BREAKER_EXCEPTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<T> attempt(final Supplier<T> supplier) {
|
private Result<T> attempt(final Supplier<T> supplier) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ public final class UpdateErrorHandler implements Function<Exception, Boolean> {
|
||||||
@Override
|
@Override
|
||||||
public Boolean apply(final Exception error) {
|
public Boolean apply(final Exception error) {
|
||||||
this.errors++;
|
this.errors++;
|
||||||
log.error("Failed to update server push: {}", error.getMessage());
|
log.error("Failed to update server push: {}", error.getMessage(), error);
|
||||||
if (this.errors > 5) {
|
if (this.errors > 5) {
|
||||||
checkUserSession();
|
checkUserSession();
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,6 +198,9 @@ public class ClientConnectionDetails {
|
||||||
this.connectionData.getIndicatorValues()
|
this.connectionData.getIndicatorValues()
|
||||||
.forEach(indValue -> {
|
.forEach(indValue -> {
|
||||||
final IndicatorData indData = this.indicatorMapping.get(indValue.getIndicatorId());
|
final IndicatorData indData = this.indicatorMapping.get(indValue.getIndicatorId());
|
||||||
|
if (indData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final double value = indValue.getValue();
|
final double value = indValue.getValue();
|
||||||
final String displayValue = IndicatorValue.getDisplayValue(indValue, indData.indicator.type);
|
final String displayValue = IndicatorValue.getDisplayValue(indValue, indData.indicator.type);
|
||||||
|
|
||||||
|
|
|
@ -496,8 +496,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
|
|
||||||
for (int i = 0; i < this.connectionData.indicatorValues.size(); i++) {
|
for (int i = 0; i < this.connectionData.indicatorValues.size(); i++) {
|
||||||
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
|
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
|
||||||
final IndicatorData indicatorData =
|
final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping
|
||||||
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId());
|
.get(indicatorValue.getIndicatorId());
|
||||||
|
|
||||||
if (indicatorData == null) {
|
if (indicatorData == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,11 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
final QuizData quizData = this.lmsAPIService
|
final QuizData quizData = this.lmsAPIService
|
||||||
.getLmsAPITemplate(record.getLmsSetupId())
|
.getLmsAPITemplate(record.getLmsSetupId())
|
||||||
.flatMap(template -> template.getQuiz(record.getExternalId()))
|
.flatMap(template -> template.getQuiz(record.getExternalId()))
|
||||||
.getOrThrow();
|
.onError(error -> log.error(
|
||||||
|
"Failed to load quiz data for exam: {} error: {}",
|
||||||
|
examId,
|
||||||
|
error.getMessage()))
|
||||||
|
.getOr(null);
|
||||||
return toDomainModel(record, quizData, null, true);
|
return toDomainModel(record, quizData, null, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -791,26 +795,11 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
|
|
||||||
log.info("Try to recover quiz data for Moodle quiz with internal identifier: {}", externalId);
|
log.info("Try to recover quiz data for Moodle quiz with internal identifier: {}", externalId);
|
||||||
|
|
||||||
// get additional quiz name attribute
|
// get former quiz name attribute
|
||||||
final AdditionalAttributeRecord additionalAttribute =
|
final String formerName = getFormerName(record.getId());
|
||||||
this.additionalAttributeRecordMapper.selectByExample()
|
if (formerName != null) {
|
||||||
.where(
|
|
||||||
AdditionalAttributeRecordDynamicSqlSupport.entityType,
|
|
||||||
SqlBuilder.isEqualTo(EntityType.EXAM.name()))
|
|
||||||
.and(
|
|
||||||
AdditionalAttributeRecordDynamicSqlSupport.entityId,
|
|
||||||
SqlBuilder.isEqualTo(record.getId()))
|
|
||||||
.and(
|
|
||||||
AdditionalAttributeRecordDynamicSqlSupport.name,
|
|
||||||
SqlBuilder.isEqualTo(QuizData.QUIZ_ATTR_NAME))
|
|
||||||
.build()
|
|
||||||
.execute()
|
|
||||||
.stream()
|
|
||||||
.findAny()
|
|
||||||
.orElse(null);
|
|
||||||
if (additionalAttribute != null) {
|
|
||||||
|
|
||||||
log.debug("Found additional quiz name attribute: {}", additionalAttribute);
|
log.debug("Found formerName quiz name: {}", formerName);
|
||||||
|
|
||||||
// get the course name identifier
|
// get the course name identifier
|
||||||
final String shortname = MoodleCourseAccess.getShortname(externalId);
|
final String shortname = MoodleCourseAccess.getShortname(externalId);
|
||||||
|
@ -827,7 +816,7 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
final String qShortName = MoodleCourseAccess.getShortname(quiz.id);
|
final String qShortName = MoodleCourseAccess.getShortname(quiz.id);
|
||||||
return qShortName != null && qShortName.equals(shortname);
|
return qShortName != null && qShortName.equals(shortname);
|
||||||
})
|
})
|
||||||
.filter(quiz -> additionalAttribute.getValue().equals(quiz.name))
|
.filter(quiz -> formerName.equals(quiz.name))
|
||||||
.findAny()
|
.findAny()
|
||||||
.get())
|
.get())
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
@ -851,7 +840,7 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.warn("Failed to try to recover from Moodle quiz restore: {}", e.getMessage());
|
log.debug("Failed to try to recover from Moodle quiz restore: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ public class ExamSessionControlTask implements DisposableBean {
|
||||||
final Map<Long, String> updated = this.examDAO.allForRunCheck()
|
final Map<Long, String> updated = this.examDAO.allForRunCheck()
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(exam -> exam.startTime.minus(this.examTimePrefix).isBefore(now))
|
.filter(exam -> exam.startTime != null && exam.startTime.minus(this.examTimePrefix).isBefore(now))
|
||||||
.filter(exam -> exam.endTime == null || exam.endTime.plus(this.examTimeSuffix).isAfter(now))
|
.filter(exam -> exam.endTime == null || exam.endTime.plus(this.examTimeSuffix).isAfter(now))
|
||||||
.flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId)))
|
.flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId)))
|
||||||
.collect(Collectors.toMap(Exam::getId, Exam::getName));
|
.collect(Collectors.toMap(Exam::getId, Exam::getName));
|
||||||
|
|
|
@ -245,12 +245,20 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
.putIfAbsent(Exam.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
.putIfAbsent(Exam.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||||
.putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name());
|
.putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name());
|
||||||
|
|
||||||
// NOTE: we evict the exam from the cache (if present) to ensure user is seeing always the current state of the Exam
|
|
||||||
return this.examDAO.allMatching(filterMap, predicate)
|
return this.examDAO.allMatching(filterMap, predicate)
|
||||||
.map(col -> col.stream()
|
.map(col -> col.stream()
|
||||||
.map(exam -> {
|
.map(exam -> {
|
||||||
this.examSessionCacheService.evict(exam);
|
final Exam runningExam = this.examSessionCacheService.getRunningExam(exam.id);
|
||||||
return this.examSessionCacheService.getRunningExam(exam.id);
|
if (runningExam == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isUpToDate(exam, runningExam)) {
|
||||||
|
// If the cached exam-quiz data differs from the one of the currently loaded exam, cache is updated
|
||||||
|
this.examSessionCacheService.evict(exam);
|
||||||
|
return this.examSessionCacheService.getRunningExam(exam.id);
|
||||||
|
} else {
|
||||||
|
return runningExam;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
@ -514,4 +522,11 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUpToDate(final Exam exam, final Exam runningExam) {
|
||||||
|
return Objects.equals(exam.lastModified, runningExam.lastModified)
|
||||||
|
&& Objects.equals(exam.startTime, runningExam.startTime)
|
||||||
|
&& Objects.equals(exam.endTime, runningExam.endTime)
|
||||||
|
&& Objects.equals(exam.name, runningExam.name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class CircuitBreakerTest {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
result = circuitBreaker.protectedRun(tester); // 10. call...
|
result = circuitBreaker.protectedRun(tester); // 10. call...
|
||||||
assertEquals(State.OPEN, circuitBreaker.getState());
|
assertEquals(State.OPEN, circuitBreaker.getState());
|
||||||
assertEquals(CircuitBreaker.OPEN_STATE_EXCEPTION, result.getError());
|
assertEquals(CircuitBreaker.OPEN_CIRCUIT_BREAKER_EXCEPTION, result.getError().getMessage());
|
||||||
|
|
||||||
// wait time to recover
|
// wait time to recover
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
|
Loading…
Reference in a new issue