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

Update ControllerApiMqttImpl.java to reconnect on Connection Failure #2525

Closed
wants to merge 14 commits into from

Conversation

Sn0w3y
Copy link
Contributor

@Sn0w3y Sn0w3y commented Feb 6, 2024

After the initial connection attempt in the activate method, we schedule the attemptConnect method to be called periodically every 60 seconds. This ensures that if the MQTT client becomes disconnected for any reason (e.g., network issues, broker restarts), the system will automatically attempt to re-establish the connection at regular intervals.

See Issue here

After the initial connection attempt in the activate method, we schedule the attemptConnect method to be called periodically every 60 seconds. This ensures that if the MQTT client becomes disconnected for any reason (e.g., network issues, broker restarts), the system will automatically attempt to re-establish the connection at regular intervals.
@sfeilmeier
Copy link
Contributor

It's surprising to me, that we have to go such a "hacky" approach. MqttConnector already sets the setAutomaticReconnect option, so maybe we are using something else in a wrong way. I found this, but could not find a quick solution: spring-projects/spring-integration#3822

If we have to go that way, I would make private ScheduledExecutorService scheduledExecutorService; final. There is no benefit in creating it only during activate() and you can avoid the null-checks.

@Sn0w3y
Copy link
Contributor Author

Sn0w3y commented Feb 7, 2024

It's surprising to me, that we have to go such a "hacky" approach. MqttConnector already sets the setAutomaticReconnect option, so maybe we are using something else in a wrong way. I found this, but could not find a quick solution: spring-projects/spring-integration#3822

Yes, thats true. The Connection is retried once and not contiously.

If we have to go that way, I would make private ScheduledExecutorService scheduledExecutorService; final. There is no benefit in creating it only during activate() and you can avoid the null-checks.

I understand where you're coming from, @sfeilmeier, but I'd like to offer a different perspective:

By initializing the ScheduledExecutorService in the activate() method, we gain the flexibility to adjust to configuration changes dynamically. This approach allows for finer control over how resources are allocated, which is essential given the inherently dynamic nature of OSGi services.
Opting to instantiate the ScheduledExecutorService only when the component is actually activated helps us use system resources more judiciously. This ensures that threads are allocated only when there's a genuine need for them, which is a more efficient use of resources.
Handling the ScheduledExecutorService lifecycle within the activate() and deactivate() methods enhances our ability to manage errors and recover from them. This strategy is closely integrated with the component's lifecycle, allowing for more context-aware error handling and potentially smoother recovery processes.
Adhering to the OSGi guidelines for resource management—specifically, the practice of initializing and disposing of resources like the ScheduledExecutorService in sync with the component's activation and deactivation—leads to cleaner and more maintainable code. It ensures that components behave in a predictable manner within the OSGi framework, which is beneficial for long-term maintenance and reliability.
To sum up, while setting the ScheduledExecutorService as final and initializing it outside the activate() method might seem to streamline the code by eliminating the need for null checks and minimizing the risk of encountering uninitialized fields, this approach overlooks the benefits of flexibility, efficient resource management, and lifecycle alignment with OSGi principles. These aspects are indispensable for crafting robust, efficient, and easily maintainable OSGi components.

@sfeilmeier
Copy link
Contributor

Thank you for the detailed explanation.

By initializing the ScheduledExecutorService in the activate() method, we gain the flexibility to adjust to configuration changes dynamically

This would only be true if we implemented a @Modified() method, right? Otherwise there is technically no difference between constructor and @activate, apart of the downside of not having the executor final. I might be too picky sometimes, but I have just seen too many NullPointerExceptions that were only possible because of such programming decisions. In my opinion and experience I would generally favour code quality over performance optimizations - with exceptions only for really performance critical operations (like our optimized websocket handling in the Backend)

Maybe we can agree on the approach that we took for "DebugCycleExecutor", combining constructor and activate() method: https://github.com/OpenEMS/openems/blob/develop/io.openems.backend.core/src/io/openems/backend/core/debugcycle/DebugCycleExecutor.java#L48-L53

For terminating, you can use the existing ThreadPoolUtils method: ThreadPoolUtils.shutdownAndAwaitTermination(); (https://github.com/OpenEMS/openems/blob/develop/io.openems.backend.core/src/io/openems/backend/core/debugcycle/DebugCycleExecutor.java#L60)

Enhanced Lifecycle Management: Modified the component's initialization and shutdown processes. The ScheduledExecutorService, responsible for managing reconnection attempts, is now initialized in the component's constructor and configured in the @activate method. This ensures that the executor is ready for use immediately and adapts to configuration updates.

Graceful Shutdown: Implemented a more graceful shutdown process using ThreadPoolUtils.shutdownAndAwaitTermination in the @deactivate method. This approach ensures that ongoing reconnection attempts are properly completed before the component is deactivated, preventing potential resource leaks.

Configuration Update Handling: Introduced a @Modified method to dynamically handle configuration changes. This method allows the component to adjust its behavior, such as changing the reconnection attempt interval, without needing to be fully deactivated and reactivated.
Dynamic Reconnection Interval: Added a new configuration parameter (reconnectionAttemptInterval) to the Config interface. This parameter allows users to specify the interval between reconnection attempts to the MQTT broker, enhancing the component's adaptability to network conditions or broker availability.
@Sn0w3y
Copy link
Contributor Author

Sn0w3y commented Feb 7, 2024

@sfeilmeier should be applied now ?

Dynamic Reconnection Interval: Added a new configuration parameter (reconnectionAttemptInterval) to the Config interface. This parameter allows users to specify the interval between reconnection attempts to the MQTT broker, enhancing the component's adaptability to network conditions or broker availability.

Enhanced Lifecycle Management: Modified the component's initialization and shutdown processes. The ScheduledExecutorService, responsible for managing reconnection attempts, is now initialized in the component's constructor and configured in the @activate method. This ensures that the executor is ready for use immediately and adapts to configuration updates.

Graceful Shutdown: Implemented a more graceful shutdown process using ThreadPoolUtils.shutdownAndAwaitTermination in the @deactivate method. This approach ensures that ongoing reconnection attempts are properly completed before the component is deactivated, preventing potential resource leaks.

Configuration Update Handling: Introduced a @Modified method to dynamically handle configuration changes. This method allows the component to adjust its behavior, such as changing the reconnection attempt interval, without needing to be fully deactivated and reactivated.

What do you think about my approach?

@Sn0w3y
Copy link
Contributor Author

Sn0w3y commented Feb 7, 2024

@sfeilmeier maybe now everything is fixed i guess - if i forgot something just hit me up :)

@Sn0w3y
Copy link
Contributor Author

Sn0w3y commented Feb 12, 2024

Some news on this topic?

@Sn0w3y
Copy link
Contributor Author

Sn0w3y commented Feb 23, 2024

up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants