-
Notifications
You must be signed in to change notification settings - Fork 139
Description
Currently, all RPCs flow through the same internal abstraction: duplexHTTPCall. This abstraction can accommodate all manner of calls, including full-duplex bidirectional calls, but it is overkill for unary RPC. Some of the machinery needed to support bidirectional streaming (synchronization mechanisms and an extra goroutine and io.Pipe per call) is pure overhead and unnecessary for simple, unary RPCs.
We can eliminate this overhead by providing a dedicated flow for unary RPCs via a new method on the protocolClient interface. Something like so:
// Invoke invokes a unary RPC. The given req is sent to the server and the reply
// is unmarshalled into resp. Returns the HTTP method used, response headers,
// trailers, and optional error.
Invoke(ctx context.Context, spec Spec, reqHdr http.Header, req, resp any) (method string hdr, tlr http.Header, error)A very naive implementation of this new method might look like so (just to demonstrate what it conceptually does):
func (m *myClient) Invoke(ctx context.Context, spec Spec, reqHdr http.Header, req, resp any) (http.Header, http.Header, error) {
conn := client.protocolClient.NewConn(ctx, spec, reqHdr)
if err := conn.Send(req); err != nil && !errors.Is(err, io.EOF) {
_ = conn.CloseRequest()
_ = conn.CloseResponse()
return nil, err
}
if err := conn.CloseRequest(); err != nil {
_ = conn.CloseResponse()
return nil, err
}
if err := conn.Receive(resp); err != nil {
return nil, err
}
// Make sure there's no extra message and also
// make sure we've received any trailers.
if err := conn.Receive(resp); err == nil {
return nil, NewError(CodeUnknown, errors.New("unary stream has multiple messages"))
} else if err != nil && !errors.Is(err, io.EOF) {
return nil, NewError(CodeUnknown, err)
}
return http.MethodPost, conn.ResponseHeader(), conn.ResponseTrailer(), conn.CloseResponse()
}But the value of it is that it enables a much more bespoke flow that eschews the under-the-hood complexity of the StreamingClientConn (which is implemented by way of duplexHTTPConn).
Having this in place would also make it trivial to resolve #541 for unary RPCs in a way that doesn't further complicate duplexHTTPConn.