Reputation: 8219
I have issue with scheduler that I start when the play application server start, but once application got shutdown, its hit the following part of code :
// firstDay something like 1 = monday
private void startScheduler(final ImageService imageService,
final ActorSystem system) {
startImagesCleanupScheduler(imageService, system);
Logger.info("Schedulers started");
}
My problem that the Runnable
block start to execute immediately instead of just cancel the task.
To clarify code:
The following method to start the Scheduler
:
private void startImagesCleanupScheduler(ImageService imageService, ActorSystem system) {
system.scheduler().schedule(
Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
Duration.create(1, TimeUnit.DAYS), //Frequency 1 days
() -> {
int rows = imageService.cleanupInactiveImages();
Logger.info(String.format("%d inactive unused images cleaned from db", rows));
},
system.dispatcher()
);
}
The log when I shutdown note the first line here :
[info] - application - 1 inactive unused images cleaned from db
[info] - application - Shutting down connection pool.
[info] - application - Creating Pool for datasource 'default'
...
[info] - application - Schedulers started
[info] - play.api.Play - Application started (Prod)
You can see it executed the Scheduler ignoring its original execution time, then got shutting down, and then started, and "Schedulers started" after.
What the problem, how I cancel the scheduler or prevent the play to run it before shutdown? is it a bug for Akka?
I'm calling startScheduler
inside OnStartup
like the answer of the following question :
java Playframework GlobalSettings deprecation for onStart
Edit: The following the minimal code to reproduce issue:
First Create OnStartup
class:
@Singleton
public class OnStartup {
@Inject
public OnStartup(final ActorSystem system) {
startScheduler(system);
}
private void startScheduler(final ActorSystem system) {
startImagesCleanupScheduler(system);
Logger.info("Schedulers started");
}
private void startImagesCleanupScheduler(ActorSystem system) {
system.scheduler().schedule(
Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
Duration.create(1, TimeUnit.DAYS), //Frequency 1 days
() -> {
//int rows = imageService.cleanupInactiveImages();
rows = 1;
Logger.info(String.format("%d inactive unused images cleaned from db", rows ));
},
system.dispatcher()
);
}
}
Then create module:
public class OnStartupModule extends AbstractModule {
@Override
public void configure() {
bind(OnStartup.class).asEagerSingleton();
}
}
Finally enable module in application.conf :
play.modules.enabled += "modules.OnStartupModule"
Upvotes: 3
Views: 1130
Reputation: 5049
This is a bit too long for a comment, but I did not test this approach, you might need to tweak it. The documentation says that the components are destroyed in the reverse order they are created. That means you could potentially cancel your scheduler before the ActorSystem
shuts down, since this class is registered after it and depends on it. Create a shutdown hook like this in your OnStartup
class in which you will cancel your schedule. That means at the point of the ActorSystem
shutdown, there will be no schedule to execute:
@Singleton
public class OnStartup {
private final Cancellable cancellableSchedule;
@Inject
public OnStartup(final ActorSystem system, final ApplicationLifecycle l) {
cancellableSchedule = startScheduler(system);
initStopHook(l);
}
private Cancellable startScheduler(final ActorSystem system) {
return startImagesCleanupScheduler(system);
Logger.info("Schedulers started");
}
private Cancellable startImagesCleanupScheduler(ActorSystem system) {
return system.scheduler().schedule(
Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
Duration.create(1, TimeUnit.DAYS), //Frequency 1 days
() -> {
//int rows = imageService.cleanupInactiveImages();
rows = 1;
Logger.info(String.format("%d inactive unused images cleaned from db", rows ));
},
system.dispatcher()
);
}
private void initStopHook(ApplicationLifecycle lifecycle) {
lifecycle.addStopHook(() -> {
cancellableSchedule.cancel();
return CompletableFuture.completedFuture(null);
});
}
}
Upvotes: 5