Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clarification]: A ManagedExecutorService.submit is called before the application is deployed (e.g. in @Initialized(ApplicationScoped.class) observer #299

Open
OndroMih opened this issue Jul 7, 2023 · 2 comments
Labels
question Further information is requested

Comments

@OndroMih
Copy link
Contributor

OndroMih commented Jul 7, 2023

Specification

ManagedExecutorService

I need clarification on ...

What happens and when a task is executed if the task is submitted before the application is fully deployed?

For example, this code is in the application:

@ApplicationScoped
public class LogAppStarted {

    @Inject
    ManagedExecutorService executor;

    public void log(@Observes @Initialized(ApplicationScoped.class) Object event) {
        executor.submit(() -> {
            logger.log(Level.INFO, "app started");
        }        
    }
}

The same is for the @Asynchronous annotation:

@ApplicationScoped
public class LogAppStarted {

    @Asynchronous
    public void log(@Observes @Initialized(ApplicationScoped.class) Object event) {
        executor.submit(() -> {
            logger.log(Level.INFO, "app started");
        }        
    }
}

I would expect that the message will be logged in a different thread immediately, as soon as the executor can run the task after submitting it, although this could still happen before application is fully loaded and thus not ideal for most cases. Alternatively, the executor could wait until the application is fully deployed and execute the task right after.

I tried both approaches (with executor and @Asynchrononous annotation) in GlassFish, and the message was never logged. Probably because the executor isn't ready to take submissions before the app is fully deployed and it lost the submitted task. So I expected the Concurrency TCK doesn't cover this usecase. I also didn't find anything about this case in the specification text.

Additional information

I propose that the specification clarifies that the executor should execute tasks submitted during deployment and when. I suggest that the tasks are executed ASAP. We could add another method submitAfterDeployed to ensure that the executor waits until the application is deployed before it executes the task.

This should also be covered by tests in the TCK to ensure that using an executor during deployment is a valid and portable usecase.

@OndroMih OndroMih added the question Further information is requested label Jul 7, 2023
@njr-11
Copy link
Contributor

njr-11 commented Jul 10, 2023

Yes, this needs clarification. I tried both examples, as well as a third example which I think was the intent of the second example

    @Asynchronous
    public void log(@Observes @Initialized(ApplicationScoped.class) Object event) {
        logger.log(Level.INFO, "app started");        
    }

in Open Liberty and observed that it runs the tasks in all 3 cases, but only after the application is deployed, although I don't think this behavior was necessarily intentional.

I could see the possibility of some providers choosing to reject execution of these tasks on the grounds that section 3.3.4 of the Jakarta Concurrency specification states of ContextService that "All invocations to any of the proxied interface methods will fail with a java.lang.IllegalStateException exception if the application component is not started or deployed". ContextService is not ManagedExecutorService, but some sections of the spec, such as the fact that ManagedExecutorDefinition configuration includes a ContextServiceDefinition, suggest that ManagedExecutorService uses a ContextService to contextualize tasks, so Runnable.run might be considered the proxied interface method to which the requirement applies, whether inadvertently through code reuse or intentionally if interpreting the spec that way.

As far as what behavior it ought to have, I wouldn't be comfortable requiring tasks from an application to run prior to the completion of deployment of the application. That seems risky and likely to have unforeseen consequences with respect to the architectures of other specification components for which context is propagated. Is there a use case for why tasks need to run this early in the life cycle? I can understand the convenience of wanting to be able to submit them at this point, but it isn't clear why they should need to run prior to application deployment.

@OndroMih
Copy link
Contributor Author

Actually, my first gut feeling was that a task submitted during deployment will be scheduled right after the deployment is finished. That would allow triggering some action as the first thing after everything is initialized, compared to @startup on a singleton EJB or @initialized(ApplicationScoped.class) observer, which are triggered before the application is deployed and don't guarantee that everything is deployed and initialized.

But now I also have a real usecase for using threads during deployment. We're trying to decrease the startup time of a huge application, that runs several expensive processes during deployment, each takes around 30 seconds. If we can run them in parallel, we could significantly reduce the startup time. I realized that there's probably no standard way how to use multiple threads during deployment. If managed executors started a task immediately, even during deployment, we could easily parallelize the startup processes.

Anyway, as I said, my gut feeling was that the tasks would be started after deployment. So OpenLiberty behaves as I expected. But GlassFish doesn't run the tasks at all, which made me realize that this usecase is probably not clarified in the spec, and definitely not covered by the TCK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants