S16 - some transactional hardening
All checks were successful
Build, test and publish / verify (push) Successful in 7m32s

This commit is contained in:
2026-11-04 21:54:20 +01:00
parent 694bf3fa86
commit 2d8822c1a4
5 changed files with 130 additions and 5 deletions

View File

@@ -136,6 +136,71 @@ class SchedulingRunsTest {
schools.endScheduling(schools.beginScheduling(school, show.getId()).showId());
}
public @Test void databaseRecoveryPersistsPendingFailureWithoutRestart() throws Exception {
final Show show = show();
final var entered = new CountDownLatch(1);
final var release = new CountDownLatch(1);
final var persistence = new OutagePersistence();
final var runs = new SchedulingRuns(schools, blocking(entered, release));
final java.lang.reflect.Field field = SchedulingRuns.class.getDeclaredField("persistence");
field.setAccessible(true);
field.set(runs, persistence);
try {
final SchedulingRun started = runs.start(school, show.getId());
assertTrue(entered.await(10, TimeUnit.SECONDS));
persistence.available.set(false);
release.countDown();
assertTrue(persistence.unavailable.await(10, TimeUnit.SECONDS));
assertEquals(started, persistence.stored.get());
assertEquals(started, runs.status(school, show.getId()));
assertThrows(ManagementException.class, () -> runs.start(school, show.getId()));
persistence.available.set(true);
final SchedulingRun completed = await(runs, show);
assertEquals(SchedulingState.SOLVER_FAILED, completed.state());
assertEquals(started.runId(), completed.runId());
assertEquals(completed, persistence.stored.get());
schools.endScheduling(schools.beginScheduling(school, show.getId()).showId());
} finally {
persistence.available.set(true);
release.countDown();
runs.close();
}
}
private static class OutagePersistence extends SchedulingPersistence {
private final CountDownLatch unavailable = new CountDownLatch(2);
private final java.util.concurrent.atomic.AtomicBoolean available =
new java.util.concurrent.atomic.AtomicBoolean(true);
private final AtomicReference<SchedulingRun> stored = new AtomicReference<>();
private OutagePersistence() {
super(null, null, null);
}
public @Override <T> T work(final java.util.concurrent.Callable<T> action) {
if (!available.get()) {
unavailable.countDown();
throw new nl.agilecc.agiledancers.repository.StorageException(new IllegalStateException("offline"));
}
try {
return action.call();
} catch (final RuntimeException failure) {
throw failure;
} catch (final Exception failure) {
throw new IllegalStateException(failure);
}
}
public @Override void accepted(final SchedulingRun run, final Runnable launch, final Runnable rollback) {
save(run, launch);
}
public @Override void save(final SchedulingRun run, final Runnable publication) {
stored.set(run);
publication.run();
}
}
private Show show() {
final long group = schools.createGroup(
school, new GroupDetails("Group", DanceGroupType.REGULAR, 0, 99, Set.of()))