locked
Pattern for waiting on multiple service responses? RRS feed

  • Question

  • Can somebody please give me a pattern that can be used to wait on several responses, possibly from different services? There are two scenarios I am interested in:

    1. Issuing several SetJointTargetPose requests to a robotic arm and then waiting for them all to complete.

    2. Sending commands to multiple robots using DriveDistance or RotateDegrees and waiting for them to complete.

     

    In either case, the requests can take different amounts of time and I do not want to continue until ALL of the requests have finished.

     

    The problem I have with the MultipleItemReceiver and MultiplePortReceiver is that most of the overloads will not take a PortSet. The response from most services is PortSet<DefaultUpdateResponseType, Fault>. It is quite possible that there will be a fault, so this has to be taking into account.

     

    The current solution I have is to activate a series of Choice receivers, one per request, where the delegates update a global counter. When the counter reaches the total number of requests a message is posted to a completion port and I can wait on that port.

     

    Most of the examples I have seen deal with individual ports, not portsets.

     

    Thanks,

    Trevor

     

    Friday, February 8, 2008 7:04 AM

Answers

  • Will Arbiter.MultipleItemGather meet the requirement? Assuming that all your target services respond through a PortSet<T0, T1>, then you can create one of these at the start, assign it as the response port to each outbound request, then yield to a MultipleItemGather that takes this PortSet, a count of number of expected responses, and a handler that receives a pair of collections, the first a collection of the T0 responses, the second a collection of T1 responses.

    I can't say I've ever used this Arbiter before, so I'd be interested to hear if it works for you.

    Nick.
    Friday, February 8, 2008 9:40 AM

All replies

  • I already had a similar issue.

    I used an iterator handler to deal with it.

    I first send all my requests, without waiting for the response, but keeping each request response port in a list, or array.

    Once all the request are sent, I use a foreach loop to iterate over my response port list, yielding to a Choice receiver on each port. The foreach loop exits only when all responses have been received.

    The drawback is that a response received on a port will not be handled until all the previous responses have been received.
    Also it does not deal with the case of different types of responses.
     

    Hope this helps.
    Friday, February 8, 2008 9:22 AM
  • Will Arbiter.MultipleItemGather meet the requirement? Assuming that all your target services respond through a PortSet<T0, T1>, then you can create one of these at the start, assign it as the response port to each outbound request, then yield to a MultipleItemGather that takes this PortSet, a count of number of expected responses, and a handler that receives a pair of collections, the first a collection of the T0 responses, the second a collection of T1 responses.

    I can't say I've ever used this Arbiter before, so I'd be interested to hear if it works for you.

    Nick.
    Friday, February 8, 2008 9:40 AM
  • Hi guys,

     

    Thanks for the prompt suggestions.

     

    Marc, I had a solution something like yours and in fact the final solution I came up with uses an array of requests.

     

    However, Nick, yours wins the prize as the most elegant. I "could not see the wood for the trees" as they say. I was too focused on using multiple requests with multiple portsets for the responses, when in fact you only need one portset.

     

    Here is what I am using now:

     

    Code Snippet

     

    PortSet<DefaultUpdateResponseType, Fault> p = new PortSet<DefaultUpdateResponseType, Fault>();

    drive.DriveDistance[] requests = new drive.DriveDistance[_drivePorts.Count];

     

    for (i = 0; i < _drivePorts.Count; i++)

    {

        requests[i] = new drive.DriveDistance();

        requests[i].Body.Distance = _state.DriveDistance;

        requests[i].Body.Power = _state.DrivePower;

        requests[i].ResponsePort = p;

        _drivePorts[i].Post(requests[i]);

    }

     

    yield return Arbiter.MultipleItemReceive(p, _drivePorts.Count, driveHandler);

     

     

    The _drivePorts array contains a list of operation ports for the various robots. I posted about that earlier today.

     

    Using a single portset to collect all of the responses allows you to use the MultipleItemReceive, as Nick suggested.

     

    Works like a charm

     

    Thanks again,

    Trevor

     

    Friday, February 8, 2008 12:25 PM
  • I Love it when the community finds the optimal answer on its own. Indeed using the same response port for all outbound operations is the key. Then you can use MultipleItem receive, or even better, multiple item gather so you get N success + K faults out of M total. Its bullet proof

     

    Friday, February 8, 2008 4:31 PM
  • But if we use the same port for all the responses, how can we identify who returned a fault and who returned a success?
    Friday, February 8, 2008 4:40 PM
  • I'm glad we got a good solution :-)

     

    I forgot to mention in my example that the handler gets called with two collections with the responses split between the collections as default update responses and faults. You can easily check the count on the fault collection to see if there were any problems. However, unless the Fault itself identifies its origin, I don't see how to tell which request failed. I have found that in a lot of cases most of the Fault fields are empty.

     

    Trevor

     

     

    Saturday, February 9, 2008 7:34 AM
  • Trevor, happy to help and think you raise an interesting point about the fault sources. From a RESTful perspective, you'd might expect all DSS Faults to indicate the URI that raised it, in the sense that it is part of the state of the Fault.

    The 1.5 Fault object model has a Node property and the relevant part of the SOAP spec says this:

    >>
    The value of the Node element information item is the URI that identifies the SOAP node that generated the fault. SOAP nodes that do not act as the ultimate SOAP receiver MUST include this element information item. An ultimate SOAP receiver MAY include this element information item to indicate explicitly that it generated the fault.
    <<

    In DSS, the Node doesn't seem to be automatically populated, although my test (point a browser at a service whose Get throws an exception) could well be inconclusive.

    I think that you would want this property automatically populated because (a) it's cumbersome to explicitly do this yourself everytime, (b) exceptions (thrown and not posted) caught by the DSS runtime can only have this property assigned by the runtime, and (c) it's generally useful to have this information for diagnostic purposes.
    Saturday, February 9, 2008 10:44 AM
  • Thanks Nick. That's what a community is all about :-)

     

    Yes, I agree with you that the fault information does not seem to be automatically populated. If you look in Debug and Trace Messages (which is where I assume you were looking), then often you will see a full trace back to the source code line where the error occured, but not always.

     

    Part of the problem as I see it is that some of the faults are created by the code itself to indicate a problem. I know I am guilty of just creating an empty Fault and sending it back because "this can never happen" or just laziness.

     

    It would be good if there were another helper method that DSS provided which would do something like pre-populating a Fault with the current service and operation so that all you had to do was add a meaningful error message and maybe a code. There definitely needs to be more consistency so that automated parsing of faults is possible.

     

    The other concern I have is about error levels. Some faults are fatal. Others are just "so what?" and continue regardless. There are several fields in a fault (Code, Detail, Reason) but I think we need a standard way to specify the type of error and its severity that applies in a DSS environment.

     

    By the way, Faults are messy to handle if you use XSLT transforms on HttpGet and HttpPost operations. I have defined my own "Fault transform" to use when a Fault occurs, but I think there should be a standard default transform (embedded in RoboticsCommon) that can be applied for consistency.

     

    Maybe I am talking through my hat. Does anyone from the MSRS team want to comment?

     

    Trevor

     

     

    Sunday, February 10, 2008 3:57 AM
  • There are two issues being raised that i will address:

    1) when you use a single port to receive multiple responses plus the MultipleItemReceive primitive, how to you know which fault came from who?

    2) A standard way to forumalte faults

     

    1) When creating a fault, as Nick suggested, place the uri of the source of the fault, in the fault.

    2) We do have Fault.FromCode, FromCodeSubcodeReason, Fault.FromException static methods you should use today. We also have two layers of predifined fault codes.

    • The W3C.Soap.Fault static cass has qualified names of errors you should use for the fault code.
    • The DSSP.DsspFaults static class has predefined fault codes you can use for the sub code

    i will open a bug so we put the URI of the service where the fault occured, in the fault itself, as nick suggests.

    Monday, February 11, 2008 6:09 PM
  • Thanks George.

     

    I know about Fault.Fromxxx and I use Fault.FromException quite often. However, there is a lot of code already out there that does not do a good job of filling in the details of a Fault. If DSS can automatically insert the URI that will be great!

     

    The issue is still a little more subtle.

     

    If I send a set of SetJointTargetPose requests to a robotic arm and one of the joints has a problem, I still will not be able to tell them apart because Faults will have the same URI for all of the joints. Of course, if I wrote the arm service, then I could insert appropriate error messages that identify the particular joint. But what if I did not write the arm service?

     

    (As an aside, I have suggested in the past that there should be an extra operation in the generic robotic arm contract that allows you to set multiple joints instead of using several separate requests. That's a different issue.)

     

    Adding a user-specified tag, or id, to a request might help, provided that all services copied the tag into the response. However, this is a serious architectural change and it won't help for existing services.

     

    Do you have any other ideas on how to associate a response with the corresponding request?

     

    Trevor

     

    Tuesday, February 12, 2008 2:28 AM
  • This is the risk with services that do too much. If you want to know which joint failed, then the service must indicate this, on its own, in the fault. DSS cant help you much there. DSS however does convert exceptions to faults, so a service could just throw an exception with an information message, and we will preserve this in the fault, allowing to parse where the error occured in the sub-operation within the service.

     

    I wil definately look into populating the URI in the fault for sure

     

    Wednesday, February 13, 2008 9:39 PM
  • Nick is absolutely right that the SOAP Fault Node element is good to use for this. I can't help laughing but I was the one pushing this model into the SOAP 1.2 spec should Smile We should fill this out automatically.

     

    Henrik

    Thursday, February 14, 2008 8:46 PM