* Update docs references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update DOCS_README to reflect current reality Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update vuepress config with current versions and updated discussions link Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update generated docs versions Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update docs build to use temp folder instead of home Signed-off-by: Thane Thomson <connect@thanethomson.com> * Document build-docs Makefile target Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add serve-docs Makefile target to serve local build of docs Signed-off-by: Thane Thomson <connect@thanethomson.com> * Ensure 404 page is copied during docs build Signed-off-by: Thane Thomson <connect@thanethomson.com> * Redirect /master/ to /main/ Signed-off-by: Thane Thomson <connect@thanethomson.com> * Attempt to resolve #7908 Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update OpenAPI references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update CHANGELOG references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update Docker readme references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update UPGRADING references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update package-specific documentation references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update spec references from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update all code comment references to docs site from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Build v0.34.x as "latest" Signed-off-by: Thane Thomson <connect@thanethomson.com> * Explicitly mark v0.34 docs as latest in version selector Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update all links from docs.tendermint.com/main to docs.tendermint.com/latest Signed-off-by: Thane Thomson <connect@thanethomson.com> * ci: Redeploy docs on pushes to v0.34.x Signed-off-by: Thane Thomson <connect@thanethomson.com> * Temporarily copy spec directory into docs while building Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add nav link to main and clearly mark as unstable Signed-off-by: Thane Thomson <connect@thanethomson.com> * Revert to only publishing docs in nav for v0.34 and v0.33 with no latest Signed-off-by: Thane Thomson <connect@thanethomson.com> * Link to docs.tendermint.com/v0.34 from RFCs Signed-off-by: Thane Thomson <connect@thanethomson.com> * Rather just use main for all docs.tendermint.com references on main branch Signed-off-by: Thane Thomson <connect@thanethomson.com> * Rename GitHub tree links from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update link for ABCI Rust client Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update github links from master to main Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update badges in root readme Signed-off-by: Thane Thomson <connect@thanethomson.com> * Remove codecov badge since we do not use it any more Signed-off-by: Thane Thomson <connect@thanethomson.com> * Remove Java and Kotlin tutorials Signed-off-by: Thane Thomson <connect@thanethomson.com> * Remove specs from docs build Signed-off-by: Thane Thomson <connect@thanethomson.com> * Migrate spec links to GitHub repo from docs site Signed-off-by: Thane Thomson <connect@thanethomson.com> * Remove references to non-existent PEX reactor spec Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix linting badge in README Signed-off-by: Thane Thomson <connect@thanethomson.com> Signed-off-by: Thane Thomson <connect@thanethomson.com>
11 KiB
ADR 069: Flexible Node Initialization
Changlog
-
2021-06-09: Initial Draft (@tychoish)
-
2021-07-21: Major Revision (@tychoish)
Status
Proposed.
Context
In an effort to support Go-API-Stability,
during the 0.35 development cycle, we have attempted to reduce the the API
surface area by moving most of the interface of the node package into
unexported functions, as well as moving the reactors to an internal
package. Having this coincide with the 0.35 release made a lot of sense
because these interfaces were already changing as a result of the p2p
refactor, so it made sense to think a bit
more about how tendermint exposes this API.
While the interfaces of the P2P layer and most of the node package are already
internalized, this precludes some operational patterns that are important to
users who use tendermint as a library. Specifically, introspecting the
tendermint node service and replacing components is not supported in the latest
version of the code, and some of these use cases would require maintaining a
vendor copy of the code. Adding these features requires rather extensive
(internal/implementation) changes to the node and rpc packages, and this
ADR describes a model for changing the way that tendermint nodes initialize, in
service of providing this kind of functionality.
We consider node initialization, because the current implemention provides strong connections between all components, as well as between the components of the node and the RPC layer, and being able to think about the interactions of these components will help enable these features and help define the requirements of the node package.
Alternative Approaches
These alternatives are presented to frame the design space and to contextualize the decision in terms of product requirements. These ideas are not inherently bad, and may even be possible or desireable in the (distant) future, and merely provide additional context for how we, in the moment came to our decision(s).
Do Nothing
The current implementation is functional and sufficient for the vast
majority of use cases (e.g., all users of the Cosmos-SDK as well as
anyone who runs tendermint and the ABCI application in separate
processes). In the current implementation, and even previous versions,
modifying node initialization or injecting custom components required
copying most of the node package, which required such users
to maintain a vendored copy of tendermint.
While this is (likely) not tenable in the long term, as users do want
more modularity, and the current service implementation is brittle and
difficult to maintain, in the short term it may be possible to delay
implementation somewhat. Eventually, however, we will need to make the
node package easier to maintain and reason about.
Generic Service Pluggability
One possible system design would export interfaces (in the Golang sense) for all components of the system, to permit runtime dependency injection of all components in the system, so that users can compose tendermint nodes of arbitrary user-supplied components.
Although this level of customization would provide benefits, it would be a huge undertaking (particularly with regards to API design work) that we do not have scope for at the moment. Eventually providing support for some kinds of pluggability may be useful, so the current solution does not explicitly foreclose the possibility of this alternative.
Abstract Dependency Based Startup and Shutdown
The main proposal in this document makes tendermint node initialization simpler and more abstract, but the system lacks a number of features which daemon/service initialization could provide, such as a system allowing the authors of services to control initialization and shutdown order of components using dependency relationships.
Such a system could work by allowing services to declare initialization order dependencies to other reactors (by ID, perhaps) so that the node could decide the initialization based on the dependencies declared by services rather than requiring the node to encode this logic directly.
This level of configuration is probably more complicated than is needed. Given that the authors of components in the current implementation of tendermint already do need to know about other components, a dependency-based system would probably be overly-abstract at this stage.
Decisions
-
To the greatest extent possible, factor the code base so that packages are responsible for their own initialization, and minimize the amount of code in the
nodepackage itself. -
As a design goal, reduce direct coupling and dependencies between components in the implementation of
node. -
Begin iterating on a more-flexible internal framework for initializing tendermint nodes to make the initatilization process less hard-coded by the implementation of the node objects.
-
Reactors should not need to expose their interfaces within the implementation of the node type
-
This refactoring should be entirely opaque to users.
-
These node initialization changes should not require a reevaluation of the
service.Serviceor a generic initialization orchestration framework.
-
-
Do not proactively provide a system for injecting components/services within a tendtermint node, though make it possible to retrofit this kind of plugability in the future if needed.
-
Prioritize implementation of p2p-based statesync reactor to obviate need for users to inject a custom state-sync provider.
Detailed Design
The current
nodeImpl
includes direct references to the implementations of each of the
reactors, which should be replaced by references to service.Service
objects. This will require moving construction of the rpc
service
into the constructor of
makeNode. One
possible implementation of this would be to eliminate the current
ConfigureRPC method on the node package and instead configure it
here.
To avoid adding complexity to the node package, we will add a
composite service implementation to the service package
that implements service.Service and is composed of a sequence of
underlying service.Service objects and handles their
startup/shutdown in the specified sequential order.
Consensus, blocksync (née fast sync), and statesync all depend on
each other, and have significant initialization dependencies that are
presently encoded in the node package. As part of this change, a
new package/component (likely named blocks located at
internal/blocks) will encapsulate the initialization of these block
management areas of the code.
Injectable Component Option
This section briefly describes a possible implementation for user-supplied services running within a node. This should not be implemented unless user-supplied components are a hard requirement for a user.
In order to allow components to be replaced, a new public function
will be added to the public interface of node with a signature that
resembles the following:
func NewWithServices(conf *config.Config,
logger log.Logger,
cf proxy.ClientCreator,
gen *types.GenesisDoc,
srvs []service.Service,
) (service.Service, error) {
The service.Service objects will be initialized in the order supplied, after
all pre-configured/default services have started (and shut down in reverse
order). The given services may implement additional interfaces, allowing them
to replace specific default services. NewWithServices will validate input
service lists with the following rules:
- None of the services may already be running.
- The caller may not supply more than one replacement reactor for a given default service type.
If callers violate any of these rules, NewWithServices will return
an error. To retract support for this kind of operation in the future,
the function can be modified to always return an error.
Consequences
Positive
-
The node package will become easier to maintain.
-
It will become easier to add additional services within tendermint nodes.
-
It will become possible to replace default components in the node package without vendoring the tendermint repo and modifying internal code.
-
The current end-to-end (e2e) test suite will be able to prevent any regressions, and the new functionality can be thoroughly unit tested.
-
The scope of this project is very narrow, which minimizes risk.
Negative
-
This increases our reliance on the
service.Serviceinterface which is probably not an interface that we want to fully commit to. -
This proposal implements a fairly minimal set of functionality and leaves open the possibility for many additional features which are not included in the scope of this proposal.
Neutral
N/A
Open Questions
-
To what extent does this new initialization framework need to accommodate the legacy p2p stack? Would it be possible to delay a great deal of this work to the 0.36 cycle to avoid this complexity?
- Answer: depends on timing, and the requirement to ship pluggable reactors in 0.35.
-
Where should additional public types be exported for the 0.35 release?
Related to the general project of API stabilization we want to deprecate the
typespackage, and move its contents into a newpkghierarchy; however, the design of thepkginterface is currently underspecified. Iftypesis going to remain for the 0.35 release, then we should consider the impact of using multiple organizing modalities for this code within a single release.
Future Work
-
Improve or simplify the
service.Serviceinterface. There are some pretty clear limitations with this interface as written (there's no way to timeout slow startup or shut down, the cycle between theservice.BaseServiceandservice.Serviceimplementations is troubling, the default panic inOnResetseems troubling.) -
As part of the refactor of
service.Servicehave all services/nodes respect the lifetime of acontext.Contextobject, and avoid the current practice of creatingcontext.Contextobjects in p2p and reactor code. This would be required for in-process multi-tenancy. -
Support explicit dependencies between components and allow for parallel startup, so that different reactors can startup at the same time, where possible.
References
- the component graph as a framing for internal service construction.
Appendix
Dependencies
There's a relationship between the blockchain and consensus reactor described by the following dependency graph makes replacing some of these components more difficult relative to other reactors or components.
