Returning one results from futures in loop

155 views
Skip to first unread message

Vasu Sethia

unread,
Sep 1, 2022, 12:41:50 PM9/1/22
to vert.x
              

Hi All,

I have one question, I am trying to fetch people profile for a given mobile number from redis. There could be multiple profile for a mobile number and at a time only one could be the active or none. I need to return the optional of empty or option of profile but I always getting failure due to async nature, what could be the possible solution


I have written something like this


public Future<Optional<Profile>> returnProfile(Set<String> profileNumber){


Promise<LinkageResponseDto> linkageResponseDtoPromise = Promise.promise();

AtomicBoolean isActive = new AtomicBoolean();

int indexOfProfileNumber = 0;


while(indexOfProfileNumber < profileNumber.size()){


redisApi

.hgetall(key)
.onSuccess(peopleProfile-> {
var peopleProfile = convertToPeopleProfile(peopleProfile.toString());


    if(peopleProfile.isActive()){

       linkageResponseDtoPromise.complete(peopleProfile)

       isActive.set(true);

   }

});

   

}


if(!isActive.get()){

   //complete or fail

   linkageResponseDtoPromise.fail(“Not active profile”);

}

return linkageResponseDtoPromise.future();

}


       

Julien Viet

unread,
Sep 1, 2022, 4:18:54 PM9/1/22
to ve...@googlegroups.com
Hi,

could you reformat your code to be more readable by the community ?

cheers

Julien
> --
> You received this message because you are subscribed to the Google Groups "vert.x" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/5a00525e-597e-42f3-8545-1fe188d8c23bn%40googlegroups.com.

Vasu Sethia

unread,
Sep 2, 2022, 1:27:55 AM9/2/22
to vert.x
public Future<Optional<Profile>> returnProfile(Set<String> profileNumber){

           Promise<LinkageResponseDto> linkageResponseDtoPromise = Promise.promise();
           AtomicBoolean isActive = new AtomicBoolean();
           int indexOfProfileNumber = 0;

         while(indexOfProfileNumber < profileNumber.size()){

                 redisApi
                             .hgetall(key)
.                            .onSuccess(peopleProfile -> {
                  
                                            var peopleProfile = convertToPeopleProfile(peopleProfile.toString());
                                            if(peopleProfile.isActive()){
                                                    linkageResponseDtoPromise.complete(peopleProfile)
                                                    isActive.set(true);
                                             }
                             });
           }


        if(!isActive.get()) {
//complete or fail
                   linkageResponseDtoPromise.fail(“Not active profile”);
        }

       return linkageResponseDtoPromise.future();
}


Vasu Sethia

unread,
Sep 2, 2022, 1:31:37 AM9/2/22
to vert.x
Issue I am facing is  that before all the profile numbers are queried from the redis the function is returning so I am not able return the profile which is active from the set of the profile numbers, what can I be done, please help?

Julien Viet

unread,
Sep 2, 2022, 10:38:55 AM9/2/22
to ve...@googlegroups.com
Hi,

it is not clear where "key" is obtained from, can you elaborate ?

could you also provide a synchronous version (i.e blocking) that would
help to fully understand.

Julien
> To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/578b4f1a-541e-4726-b0ea-45d9e82d050dn%40googlegroups.com.

Rúnar Sveinsson

unread,
Sep 2, 2022, 10:58:37 AM9/2/22
to vert.x
Hi, 

this is actually quite interesting question since I have not seen many examples how to deal with features in loops. Until now this is how I have solved it, would be great to get some comments if there is a better way (I have made few assumptions since the example is missing some details).

This is one way to solve this I think (Have not tested this)

public Future<Optional<Profile>> returnProfile(Set<String> profileNumbers)
{
  //Create list of features to be completed
  List<Future> hgetallRedis = profileNumbers.stream()
    .map(this::getActiveProfileOrFail)
    .collect(Collectors.toList());

  //List of features using CompositeFuture.any in this case since we only need one success feature
  return CompositeFuture.any(hgetallRedis)
    .map(ar -> ar.list().stream()
      .filter(Objects::nonNull)
      .map(Profile.class::cast)
      .findAny())
    .otherwise(Optional.empty());
}

//Return Failed future in case if the profile is not active
private Future<Profile> getActiveProfileOrFail(String profileNumber)
{
  return redisApi.hgetall(profileNumber)
    .map(res -> convertToPeopleProfile(res.toString()))
    .compose(profile -> {
      if(profile.isActive())
        return Future.succeededFuture(profile);
      else
        return Future.failedFuture("Not active");
    });
}


/Rúnar

Vasu Sethia

unread,
Sep 2, 2022, 1:51:18 PM9/2/22
to vert.x
I have the following situation, I will be given a list of profile Id, I need to query all the profile Id from  Redis and fetch the one which is active, there could be possiblity that there is no active profile. I have written the following the code for async api but the problem which I am thinking is that function is returning while async requests are still being processed.

public Future<Optional<Profile>> returnProfile(Set<String> profileNumber){

           Promise<LinkageResponseDto> linkageResponseDtoPromise = Promise.promise();
           AtomicBoolean isActive = new AtomicBoolean();
           int indexOfProfileNumber = 0;

           while(indexOfProfileNumber < profileNumber.size()){

                 redisApi
                             .hgetall(profileNumber.get(indexOfProfileNumber);
.                            .onSuccess(peopleProfile -> {
                   
                                            var peopleProfile = convertToPeopleProfile(peopleProfile.toString());
                                            if(peopleProfile.isActive()){
                                                    linkageResponseDtoPromise.complete(peopleProfile)
                                                    isActive.set(true);
                                             }
                             });
                 indexOfProfileNumber++;

           }


        if(!isActive.get()) {
                   //complete or fail
                   linkageResponseDtoPromise.fail(“Not active profile”);
        }

       return linkageResponseDtoPromise.future();
}

Following is the blocking version


public Optional<Profile> returnProfile(Set<String> profileNumber){

           boolean isActive = false;
           int indexOfProfileNumber = 0;
           var peopleProfile;
           while(indexOfProfileNumber < profileNumber.size()){

                // use some Redis api to get the profile and convert it to profile class
                peopleProfile = someRedisApi.hgetall(profileNumber.get(indexOfProfileNumber);
 
              // check if peopleProfile obtained is active
              if(peopleProfile.isActive()){
                     isActive = true;
                     break;
               }

                indexOfProfileNumber++;

           }
        
      if(isActive){
         return Optional.of(peopleProfile);
     }
    
     return Optional.empty()
}

Vasu Sethia

unread,
Sep 2, 2022, 1:52:58 PM9/2/22
to vert.x
Let me look through it, I was thinking to use recursion

Bruno F

unread,
Sep 3, 2022, 7:14:58 AM9/3/22
to vert.x
You can try this (not idiomatic with flatMap and stuff but it's working):

// the function
Future<Optional<Person>> getFirstActive(Redis redis, Set<String> names) {
    Promise<Optional<Person>> promise = Promise.promise();
    findNextActive(redis, names.iterator(), promise);
    return promise.future();
}

// utility function
void findNextActive(Redis redis, Iterator<String> names, Promise<Optional<Person>> promise) {
    if (!names.hasNext())
        promise.complete(Optional.empty());
    String name = names.next();
    System.out.println("Trying: " + name);
    redis.getPeople(name)
        .onSuccess(result -> {
            if (result.isActive())
                promise.complete(Optional.of(result));
            else
                findNextActive(redis, names, promise);
           
});
}

// using the function
Set<String> people = new HashSet<>(Arrays.asList("bob", "joe", "jane", "will"));
getFirstActive(redis, people)
     .onFailure(Throwable::printStackTrace)
     .onSuccess(result -> {
          if (result.isPresent())
               System.out.println("Found: " + result.get());
          else
               System.out.println("Not found");
})
;

Julien Viet

unread,
Sep 6, 2022, 9:32:47 AM9/6/22
to ve...@googlegroups.com
I think your version is doing concurrent calls to get each profile.

Another way to do it, could be to do each profile one by one until one
valid is found.

In that case it would use a recursive way, e.g a function like:

static <T> Future<T> until(int idx, List<Supplier<Future<T>>> futures) {
return Future.future(p -> until(0, futures, p));
}

static <T> void until(int idx, List<Supplier<Future<T>>> futures,
Promise<T> promise) {
if (idx == futures.size()) {
promise.fail("Not found");
} else {
futures.get(idx).get().onComplete(ar -> {
if (ar.succeeded()) {
promise.complete(ar.result());
} else {
until(idx + 1, futures, promise);
}
});
}
}









Julien
> To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/8aac5e9a-7d00-4471-9d9d-59f28ade135fn%40googlegroups.com.

Vasu Sethia

unread,
Sep 8, 2022, 8:24:24 AM9/8/22
to vert.x
Hi Julien,
I was able to implement the recursive solution to it, how about the iterative approach?
Vasu

Bruno F

unread,
Sep 8, 2022, 8:34:04 AM9/8/22
to vert.x
My version was not doing any concurrent call: I tested it and it calls getPeople one after the other, and stops when the first active has been found.
I think our versions are strictly equivalent :-).

I know it looks like it, but it is not a recursion per-se (and your favorite IDE will show you that): there is no risk of stack overflow.
Reply all
Reply to author
Forward
0 new messages