On Dec 17, 2013, at 3:27 AM, Antoine Pitrou <[email protected]> wrote:
> On Mon, 16 Dec 2013 22:21:14 -0800 > Glyph <[email protected]> wrote: >> >> This is another case where pushing more convenience functionality into a >> primitive just means more re-implementation of that convenience >> functionality in different primitive implementations. All of this >> reconnecting logic can be higher-level, defined in terms of wrappers around >> protocols, without any additional complexity on the part of the caller. > > I don't understand how reconnection can be a "wrapper around protocols". > To reconnect you have to interact with a given transport. Worse, you > must *decide* whether to reconnect or not (because a normal connection > close should usually not lead you to reconnect). You create a protocol factory wrapper (PFW) around an application protocol factory (PF) and some instructions for how to create an outbound connection that takes a protocol factory. When PFW is asked to create a protocol, it asks PF to create a protocol P, and then creates a wrapped protocol PW, which delegates everything except connection_lost to P. PW.connection_lost then executes the instructions to create a new outgoing connection with PF, and you get a new protocol for that new connection. > So it *is* transport-specific already. No, it's protocol-specific. If it were "transport-specific" that would mean that it would make sense to reconnect all TCP connections, for example. > Actually, the general pattern may be to call self.transport.reconnect() > from the protocol's connection_lost(). If you need to do something in connection_lost anyway, then why not just call create_connection again yourself? The whole point of having transport-level support for reconnection, it seems to me, would be to implement application-independent reconnection policies. (Surely you're not suggesting that you *re-use* the protocol instance? That would be like re-using a stack frame for a subsequent call.) In the simple case of TCP with happy eyeballs, and your original connection was to a host name and not an IP address, there are a multitude of things that "reconnect()" could mean. Some examples: start doing exactly what create_connection did in the first place, i.e. talk to your DNS servers, issue some parallel connections, return the first one. do the second part of what create_connection does; assume the hostname resolution is going to return the same thing (perhaps you're within the TTL window on the records you'd previously resolved, in which case it's very nearly obligated to) but issue a bunch of connections do only the TCP-level reconnecting, assuming the exact same IP address and port number. Then if you imagine you had a connection to an SSH server, you could do all of those things with the host name and then also make some decisions about whether to make a new connection at all, or to simply re-issue the command over the existing SSH channel. 'transport.reconnect' is not a broad enough interface to communicate all of this stuff. As the SSH example illustrates, you can't just add some flags, because every new transport implementation would add a slew of its own. Plus, you kinda want to communicate these instructions to the application in some way in advance, and you don't want to do all this work for *re*connection in a way that can't be used for connecting in the first place. To sum up: you cannot "re-connect" a transport. Once the stream has ended, it's ended, and you have to make a whole ton of decisions about how to create one that is similar but will always, ultimately, be distinct. -glyph
