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

Reply via email to