The deep difference between request/response and fire-and-forget

(long note warning)

On the call today we got into an interesting discussion of req/resp vs. 
fire-and-forget, which I'll abbreviate here as FAF.  What I'm about to say 
is pretty close to some things that David Hall and Mark Baker have said, 
so I don't claim total originality, but I think this is deep and 
important, and needs to be set down in detail. 

I claim that req/resp is deeply different from FAF.  In particular, for 
reasons explained below: 

a) you cannot safely support true FAF on an underlying protocol that is 
req/resp (regardless, by the way, of whether that underlying protocol is 
transport-like or application-like)

b) you cannot in general simulate request/response out of two FAF 
messages, unless you are willing to essentially build your own req/resp 
transport protocol.

===============analysis of point (a) ===================================
The main point I'd like to focus on is (a), because it's quite subtle. Why 
can't we support true FAF on a request/response protocol like HTTP?

First, let me make some definitions (I'll ignore streaming in all this):

FAF:  a model in which the sending application prepares a message and a 
destination address, hands them to its local software for transmission, 
and is then free to disappear (I.e. "forget").  In the absence of errors, 
failures, unusual congestion, etc., the message is delivered intact to the 
intended receiver.   The receiver has no further obligation to the 
protocol after reading the message.  Typical application pseudo code for 
this looks like:

Sender:

        s = socket.new(destinationURI);
        s.send(Message);
        // note that client does not wait here
        s.close();

Reciever:

        s = socket.new(listen);  // I know, this simplifies the real code 
a lot
        msg = s.read(); 
        s.close();
        // process the message if you like


Req/Resp:  a model in which the requestor prepares a request message and 
destination as for FAF, hands them to the local software for transmission, 
> and waits to receive either a response or else confirmation that none 
will be coming<.  The responder receives the request message, processes it 
an any desired manner and >MUST either transmit a response message (with 
no explicit addressing) or must explicitly signal to its local software 
that no response will be forthcoming.<

Client:

        s = socket.new(destinationURI);
        s.send(requestMessage);
        // we hang on following line until the responder takes
        // explicit action, either sending a response or
        // an indication that none is coming
        r = s.read(); 
        s.close();
        if (r != null) 
                processResponse(r)
        else
                println("No response received\n");

Responder:

        s = socket.new(listen);  // I know, this simplifies the real code 
a lot
        req = s.read(); 
        responseMessage = processRequest(req);
        if (responseMessage != null)
                // note: the following response carries no address
                // we assume there's an open connection left from
                // the request...this will be important when
                // we analyse point (b) later
                s.reply(responseMessage);
        // note, if no response message is sent, then the
        // the following will "unexpectedly" close the socket,
        // causing the client to fall through its read, but
        // with no response message.
        s.close();

Crucially, after sending the request, the client is waiting.  For any 
reasonable underlying request/response protocol, even the decision by the 
responding application that no explict response message is forthcoming 
(e.g. the s.close by the responder above) will trigger some sort of 
signaling to the client.  In the case of closing a socket in TCP/IP, I 
believe that IP packets are sent to explicitly alert the other end that 
the connection is being closed (I'm leaving out discussions of lost 
packets and timeouts, which are very important in practice).  Thus, with 
request/response:

* The responder has an obligation to do >something< protocol-related after 
receiving the request message.
* The client is waiting until it does.

A subtle problem occurs if you try to implement true FAF on a req/resp 
protocol like HTTP.  Repeating the sender code from above:

        s = socket.new(destinationURI);
        s.send(Message);
        // note that client does not wait here
        s.close();

Depending on the local implementation, I don't think the above obligates 
the messaging system to even send the message.  At the time of the close, 
the Message might still be in a local buffer or conceivably the connection 
might not yet have completed, depending on how synchronous the 
implementation of the API is.   The point is that typical request/response 
protocols set up connections at some level.  Doing so implies that client 
has some state that has to be torn down >after the far end decides that 
nothing more is to be done<, and that usually involves round trip 
communication at some level.  Round trip usually involves waiting, and FAF 
is specifically the model in which the original sender does not wait. 

I noticed this in DaveO's draft [1] where he failed to explicitly include 
in the HTTP binding an indication of what was to happen at the point that 
the server decided that it was in fact not sending back a message of any 
sort.  Is it to close the connection?  I'm not sure that's good HTTP 
practice, but it does work.  Note, however, that this would NOT be the 
same as fire and forget.  In the req/resp model, the client MUST wait 
until the connection closes or the response comes back.  Fire and wait is 
NOT the same as Fire and Forget.

===============analysis of point (b) ===================================

Conversely, you can't support a classic req/resp API with two FAFs, in 
part because you don't know how to address the response.  The responder 
has an address, and the requestor uses it to address the request.  The 
requestor need not in general have an address.  We see this all the time 
in NAT'd HTTP environments.  You can't model req/resp in the NAT world 
very well with two FAFs, but we all routinely do request/response through 
NATs.  Q.E.D., I think.
 ================================================================

>From this I conclude that we cannot do what DaveO originally suggested, 
I.e. we cannot make the response in our existing MEP truly optional.  I 
believe I tentatively convinced Dave of this on the phone.  The plan we 
agreed to explore today is indeed to make the response message mandatory, 
but the response envelope optional, which is what Anish and I also 
drafted. 

I hope I've convinced you that true FAF would need to be a separate MEP, 
as David Hull has been suggesting. 

The point of this note has been to:

a) explain why making the response optional doesn't work over HTTP, and 
thus why we can't or at least shouldn't try to support true FAF over HTTP.
b) explain why, if we want to support true FAF underlying transports like 
UDP, then we will need a separate one-way MEP for use with them, because 
they cannot do a proper job of req/resp.

I hope the above all makes sense.   I do think it's important that we have 
the detailed reasoning set down. 

I hope this also explains why I strongly disagree with DaveO's suggestion 
that SOAP should be mainly about formats and less about protocols.  I 
agree that separating the two is good, and we've done that.  The 
definition and processing of application/soap+xml is indeed quite separate 
from its use in MEPs and bindings.  Still for interop to work, the nodes 
involved MUST know when to wait for responses, and MUST know where faults 
will be delivered.  That's protocols, not just formats.

Sorry the note turned out to be a bit long.

Noah

[1] http://lists.w3.org/Archives/Public/xml-dist-app/2006Jan/0007.html

--------------------------------------
Noah Mendelsohn 
IBM Corporation
One Rogers Street
Cambridge, MA 02142
1-617-693-4036
--------------------------------------

Received on Wednesday, 11 January 2006 23:15:53 UTC