Refactor/service framework method shadowing#776
Open
renatomaia wants to merge 9 commits intonext/2.0from
Open
Conversation
538cd95 to
352353b
Compare
… in 'Tick' functions
352353b to
f3b971e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issues Addressed
Still Open Issues (left for a future PR)
Stop(force bool)or by cancelling the context explicitly provided to be used by the service. The former is used to synchronize the shutdown of multiple services running together.Overview of Changes
Package
pkg/serviceprovides the service runtime used across the node. It owns lifecycle concerns such as logging, signal handling, telemetry, and shutdown coordination.The solution seems to try to follow the Template Method design pattern where a base class implements methods that provide those basic features (template methods defined by interface
IService) but also calls additional methods that are provided by the subclass to implement some service specific behavior (abstract methods defined by interfaceServiceImpl).In Go, it is common to use struct embedding to emulate class inheritance of other languages. However, Go does not provide a standard way for a embedded struct (base class) like
service.Serviceto access its embedding struct (subclass)custom_service.Service. Therefore it is always necessary to explicitly provide a reference that allows the template method to access the actual implementation of the abstract method it needs to call (see the description of one approach here). This explicit reference is provided as fieldservice.Service.Impl.Such approach for Template Methods using struct embedding with explicit auto-reference has the following advantages:
Loggerfrom the base class or callCancelto terminate the service's context and cause the service to stop (it would be better to be able to callStopbut it is shadowed due to a particular problem of the current implementation).IServicefor the clients, the embedding struct (subclass) can override any of its public method by shadowing the "inherited" implementation. For instance, some services redefine theServermethod to do continuous processing instead of the tick-based periodic processing supported by the service framework.Problems
Using the same method name for a template method and the abstract method that it invokes causes some confusion when adopting the approach of struct embedding because the abstract method shadows the template method. Therefore clients using the objects returned by factory function
custom_service.Create()will execute the stripped down abstract method instead of the full-featured template method. In principle, we could solve this by returning the embeddedservice.Serviceinstead of thecustom_service.Serviceobject, but this would prevent shadowing altogether, which is an issue as described by the next problem.Since
service.Serviceonly supports services that perform smalls processing batches periodically (ticks), some custom services define an implementation ofServethat shadows the one fromservice.Serviceto start Goroutines for continuous tasks. Therefore, we currently must returncustom_service.Serviceto allow to shadowing of some methods.Solution
Adopt different method names for abstract methods to avoid unintentional method shadowing. It also makes clear that calling
Stoponcustom_service.Servicewill not executecustom_service.Service.Stopas the former will be named differently.Unlike methods
Reload,Stop, andServe, methodsAliveandReadydo not implement a common basic behavior and only delegate entirely to the custom service. For this reason, we will not implement them as a pair of template and abstract methods, and just let the concrete services to shadow them when necessary to define a custom behavior.We will also implement a standard behavior for most abstract methods which are currently implemented by most services.
We will also provide two alternative service templates:
ServiceTemplatefor services that do continuous processing.TickServiceTemplatefor services that do processing in periodic batches (tick-based).Finally, since the
implself-reference is a specific hack necessary in Go to implement the Template Method design pattern, we removed this field from the service initialization structure (currentConfigInfo) to avoid making them too visible in the code, and try to confine this setup of the self-references to the factory functions that create and initialize services.