Thanks for the well-written proposal. If we were to implement this, I think you have described the way we would do it.
My main objection to implementing streaming RPC is a fear of feature creep. Keeping Thrift small(ish) makes it a lot easier to make sure that all of the language implementations have good support for all features. What to other committers think? --David Jeff Brown wrote: > Today I encountered a case where it would be useful if Thrift provided > native support for streaming RPC. To be sure, everything I needed to do I > could implement (albeit with difficult) using the existing infrastructure > but I feel it could be made much better by extending the IDL. > > Please forgive me if this idea has already been discussed here. > > *Background: > * > Sometimes a service needs to perform a long-running operation that > periodically yields output. Rather than waiting for the full result to be > processed, it would be nice if clients can get a head start on the > intermediate output. This is particularly important when the results are > intended to be presented to an end user. > > A simple way to accomplish this trick is to allow services to write multiple > messages to the protocol. > > > *Proposed IDL extensions: > * > * Option 1: New *stream* keyword admitted as a method return type. > > *struct Response > { > 1: required string item; > } > > service Example > { > stream<Response> Method() > } > * > With this approach, the method can be conceived of as returning a stream of > zero or more responses. This is particularly appealing because it leverages > familiar patterns of iteration. > > The server side implementation can be conveniently written as an iterator > method. Here's a C# example. Similar syntax exists in many popular > languages but it is not essential for the API. > > public IEnumerable<Response> Method() > { > yield return new Response() { Item = "a" }; > // expensive work > yield return new Response() { Item = "b" }; > } > > Likewise the client side can be modeled as a consumer of an iterator. > > public void Consumer() > { > IEnumerable<Response> responses = client.Method(); > foreach (Response response in responses) > { > Console.WriteLine(response.item); > } > } > > * Option 2: New *streams* keyword admitted as an auxiliary clause similar to > throws. > > *struct Item > { > 1: required string name > } > > struct Response > { > 1: required string summary > } > > service Example > { > Response Method() streams(1: Item item) > } > * > With this approach, the method has both a standard return value and can > stream items on the side. It could potentially be set up to allow a method > to provide multiple streams. > > The server side implementation is straightforward. The method just needs to > be provided with an object that can publish to the stream in addition to > whatever arguments it receives. > > public Response Method(TStream<Item> stream) > { > stream.Send(new Item() { name = "a" }); > // expensive work > stream.Send(new Item() { name = "b" }); > return new Reponse() { summary = "2 items" }); > } > > The client side is not so nice since we can't leverage the iterator pattern > because we could have multiple distinct streams. Here's a synchronous > implementation using a callback. > > public void Consumer() > { > TStreamCallback<Item> callback = (item) => Console.WriteLine(item.name); > Response response = client.Method(callback); > } > > > *Proposed protocol extension:* > > To make this work, the server needs to be able to send multiple stream > messages before the final reply. > > One way to do that is to add a new TMessageType value called Stream that > indicates that the TMessage contains a streaming response. When the client > receives a message of type Stream, it makes the content available to the > consumer and then (conceptually) loops again until it sees a final message > of type Reply. > > The final reply message might only indicate success / failure if the stream > is empty or if everything of interest has already been streamed by the time > the method returns. > > Adding a new TMessageType is a non-invasive change that won't break existing > clients or servers unless they use streaming methods. > > Jeff.