Skip to content

Improvements for Quarkus extension #1043

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

Merged
merged 10 commits into from
Mar 18, 2022
Merged

Improvements for Quarkus extension #1043

merged 10 commits into from
Mar 18, 2022

Conversation

metacosm
Copy link
Collaborator

@metacosm metacosm commented Mar 16, 2022

  • Extract DependentResource instantiation to ConfigurationService so that it could be overridden by a different mechanism (in particular, CDI beans)
  • Make resource type explicit for DependentResource implementations that need it via ResourceTypeAware interface.

@metacosm metacosm self-assigned this Mar 16, 2022
@metacosm metacosm requested a review from csviri March 16, 2022 13:51
@metacosm metacosm changed the title WIP: refactor to enable CDI-injected dependents Improvements for Quarkus extension Mar 17, 2022
@metacosm metacosm marked this pull request as ready for review March 17, 2022 17:44
@@ -85,25 +85,19 @@ private void initContextIfNeeded(P resource, Context context) {

private DependentResource createAndConfigureFrom(DependentResourceSpec dependentResourceSpec,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it be move to controller ctor and passed to DependentResourceManager ctor ? It seems weird that dependent are created during prepareEventSources especially when it is not an EventSourceProvider and it feels like an inversion of control should better. This may avoid the call to ConfigurationServiceProvider deep in the stack.

Copy link
Collaborator Author

@metacosm metacosm Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I'm following. It makes sense to initialise the DependentResource implementations in prepareEventSources because DependentResources are more or less abstractions over EventSources. This also avoids iterating over the dependents several times (once to create them, another to initialize the event sources for the EventSourceProvider implementations).

That said, I'm open to alternatives.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I'm following. It makes sense to initialise the DependentResource implementations in prepareEventSources because DependentResources are more or less abstractions over EventSources. This also avoids iterating over the dependents several times (once to create them, another to initialize the event sources for the EventSourceProvider implementations).

That said, I'm open to alternatives.

Looping over a very small list at startup time doesn't worry me that much.

Looking at https://github.com/java-operator-sdk/java-operator-sdk/blob/4b3178f6546494faff048b9dcef1e00244586fe3/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconcilerDependentResources.java#L40-L42, the natural choice to instanciate the dependent resources in standalone mode is in the Reconciler ctor. I would expect that the framework is consistent and that the depedent are created with the nature of a dependent resource, they should have been be instantiated in the prepareEventSource too.

My POV is that DependentResourceManager is about managing DependentResource not DependentResourceSpec. The latter is a concern of the configuration of the Controller.
DependentManager receive a Controller instance in the ctor and has access to controller configuration and reconciler while all it needs is a list of DependentResource instance. The initContextIfNeeded could be moved to Controller to avoid the need of reconciler. BTW, there is a side effect of calling initContext from DependentResourceManager that make reconciler implementing ContextInitializer without dependent annotation works as it should. Not sure if ContextInitializer was introduce with depedents.
I think that when it will start dealing with dependent of dependents, the complexity of DependentResourceManager will raise and it will no more be much SRP compliant.

That being said, I checked the code and there is something fishy regarding valid lifetime of DependentResource instance and this will drive when and where it should be instanciated.

Operator::start will prepareEventSource and start the DependentResource event source. Then, Operator::stop will stop the event source. It means that if DependentResource implementing EventSourceProvider were Singleton, their event soure must be restartable. But k8s informer are not restartable, thus KubernetesDependentResource can not be a Singleton. Until now, this was not an issue because prepareEventSource called during start is actually creating new instance of DependentResource each time in createAndConfigureFrom. But it feels natural to have @ApplicationScoped/@Singleton dependent resource in Quarkus. It also told us that sample WebPageReconciler will fail if it is start, stop and start (in a test suite for example).
I think that it should be documented that EventSourceInitializer::prepareEventSource must return either restartable EventSource (should support start/stop/start/... sequence) or return transient instance.
Or cleaner imho, find a way to make KubernetesDependentResource event source restartable but this seems not that simple.

As Controller is LifecycleAware, it make sense to me that it take the ownership of DependentResource instances. For eg, if the DependentResourceSpec is not empty it can create them in the start method and then new DependentResourceManager(dependents). And if the DependentResourceSpec is empty, it can new a noop manager to optimize calls.

The last thing is that I don't know how it can ensure EventSource are restartable for DependentResource singleton. Should the Quarkus extension check and forbid @ApplicationScoped/@Singleton ?

sorry for the long story

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator::start will prepareEventSource and start the DependentResource event source. Then, Operator::stop will stop the event source. It means that if DependentResource implementing EventSourceProvider were Singleton, their event soure must be restartable.

I don't understand this, why would you restart the operator / event source, or in what scenario event source needs to be restartable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot to unpack there. I'm not sure we should worry about sources being restartable at this point. The main use case is still an operator that is started once when it is starting up and then stop when it's about to be shutdown. I don't think it makes sense to start/stop the operator multiple times during normal operations.

Regarding where DependentResource instances should be created, I'm going to look at the implications of moving that part to Controller but at this point, I'd rather leave things as-is so that we can release and get some user feedback (unless there's a blocking issue that we need to deal with now). As these changes seem to be related to the internal implementation, that's probably a change we can make in a later iteration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense to start/stop the operator multiple times during normal operations.

In normal operation, I totally agree. In test suite, this is exactly what does the JUnit Operator extension. It start/stop the same operator instance for each test. I just wanted to warn about that because sample operator are not representative of fully tested operators.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Let's revisit if that becomes an issue.

@@ -126,4 +130,14 @@ default boolean closeClientOnStop() {
default ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}

default <T extends DependentResource<?, ?>> T createFrom(DependentResourceSpec<T, ?> spec) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a little weird, that a configuration service is responsible for instantiating a Dependent Resource.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, would be better to add a factory if we want to go that way. Putting it directly on ConfigurationService was a shortcut.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 61 Code Smells

39.1% 39.1% Coverage
0.3% 0.3% Duplication

@metacosm metacosm requested a review from csviri March 18, 2022 12:48
Copy link
Collaborator

@csviri csviri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@metacosm metacosm merged commit f408a14 into next Mar 18, 2022
@metacosm metacosm deleted the cdi-dependents branch March 18, 2022 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants