3

Here's the setup: There is a federal remote service which returns whether a particular value is correct or not correct. We can send requests as we like, up to 50 per request to the remote service.

Since we need to only use the correct value, and the set of possible values is small (~700), we can just send 15 or so batch requests of 50 and the correct value will be part of the result set. As such, I've used the following code:

Observable
    .Range(0, requests.Count)
    .Select(i => Observable.FromAsync(async () =>
    {
        responses.Add(await client.FederalService.VerifyAsync(requests[i]));
        Console.Write(".");
    }))
    .Merge(8)
    .Wait();

But - what I don't like about this is that if one of the earlier requests has the correct value, I still run all the possibilities through the service wasting time. I'm trying to make this run as fast as possible. I know the exit condition (response code is from 1 to 99, any response code within 50-59 indicates the value is "correct").

Is there a way to make this code a little smarter, so we minimize the number of requests? Unfortunately, the value we are verifying is distributed evenly so sorting the requests does nothing (that I'm aware of).

VMAtm
  • 27,943
  • 17
  • 79
  • 125
djbyter
  • 763
  • 8
  • 28

2 Answers2

3

You should consider usage of the FirstAsync method here:

The secret in our example is the FirstAsync method. We are actually awaiting the first result returned by our observable and don’t care about any further results.

So your code could be like this:

await Observable
    .Range(0, requests.Count)
    .Select(i => Observable.FromAsync(async () =>
    {
        responses.Add(await client.FederalService.VerifyAsync(requests[i]));
        Console.Write(".");
    }))
    .FirstAsync()
    .Subscribe(Console.WriteLine);

> System.Reactive.Linq.ObservableImpl.Defer`1[System.Reactive.Unit]

Rx and Await: Some Notes article provides some tricks with similar methods. For example, you have an overload for FirstAsync, which can be filtered, as the LINQ' method First:

await Observable
    .Range(0, requests.Count)
    .Select(i => Observable.FromAsync(async () =>
    {
        responses.Add(await client.FederalService.VerifyAsync(requests[i]));
        Console.Write(".");
    }))
    .FirstAsync(r => /* do the check here */)
    .Subscribe(Console.WriteLine);
VMAtm
  • 27,943
  • 17
  • 79
  • 125
2

You're pretty close. Change your observable to this:

Observable
    .Range(0, requests.Count)
    .Select(i => Observable.FromAsync(async () =>
    {
        var response = await Task.FromResult(i); //replace with client.FederalService.VerifyAsync(requests[i])
        responses.Add(response);
        Console.Write($"{i}.");
        var responseCode = response; //replace with however you get the response code.
        return responseCode >= 50 && responseCode <= 59;
    }))
    .Merge(8)
    .Where(b => b)
    .Take(1)
    .Wait();

This way your observable continues to emit values, so you can continue acting on it.

Shlomo
  • 14,102
  • 3
  • 28
  • 43