-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Specification
Currently we have 2 connection abstractions in PK:
network-ConnectionandConnectionForwardandConnectionReverse- usesStartStopgrpc&nodes-GRPCClient,GRPCClientAgent,GRPCClientClientandNodeConnection- usesCreateDestroy
As you can see the network is using the StartStop pattern and the grpc & nodes is using CreateDestroy.
The CreateDestroy is fundamentally more limiting than StartStop, as it enforces a sort of logical order, where asynchronous side-effects must occur first before you are given the actual object instance. Thus tying the object-lifetime to asynchronous setup and shutdown.
Originally I wanted to apply CreateDestroy to network domain, but was impacted by some cyclic dependencies that existed in network domain. However now there are cyclic dependencies in the grpc and nodes domain, and it appears that CreateDestroy is too limiting, and instead we need to use StartStop.
Now what are these cyclic dependencies? They all boil down to needing to do some asynchronous side-effects while having access to the instance itself.
For example in ConnectionForward, the start call makes use of this.utpSocketandthis.tlsSocket, which are all properties of the instance. Some complex refactoring could move this all into async creation, and then passed in as properties of the ConnectionForward` instance, but this is unnecessary complexity when all these properties have to be part of the instance.
In the case of NodeConnection, we have a problem where it causes us to construct the instance first new NodeConnection() and then setup the GRPCClient and then inject the client object into the NodeConnection instance.
const nodeConnection = new NodeConnection<T>({
host: targetHost,
port: targetPort,
hostname: targetHostname,
fwdProxy: fwdProxy,
destroyCallback,
logger,
});
// ... setting up client
// TODO: This is due to chicken or egg problem
// see if we can move to CreateDestroyStartStop to resolve this
nodeConnection.client = client;The reason for this is because the creation of the GRPCClient requires the destroyCallback to be set, and that requires a reference to the nodeConnection instance.
This round-about way of doing things makes things a bit strange/convoluted for no reason.
If the creation logic of GRPCClient and NodeConnection were instead changed to StartStop pattern, then we can proceed to make this logic much more cleaner.
It does mean you may have NodeConnection instances that are not started. But that's ok, since our network proxies handle this by removing themselves from the connection proxies when they are stopped. The nodes connection make use of a generic destroyCallback to do this same behaviour. The main difference is that network connections hardcode knowledge about the connection state from the proxies, and require it during construction. See the connections parameter in ConnectionForward and ConnectionReverse. With GRPCClient, this is more difficult since nodes and grpc are separate domains, so we don't want to hardcode knowledge about nodes connection management into the grpc classes.
Additional context
- Extracting Node Connection Management out of
NodeManagertoNodeConnectionManager#310 - PR that extracted node connection management intoNodeConnectionManager - Allow generic extensions to
Idjs-id#13 (comment) - Some discussion about polymorphicthis, this may not be relevant if we useStartStop
Tasks
- Change
GRPCClientandGRPCClientClient,GRPCClientAgentandGRPCClientTesttoStartStop - Change
NodeConnectiontoStartStop - Adapt the
NodeConnectionManager, and base it on howForwardProxyandReverseProxyhandles it - Update all tests