1

I am writing a little polling mechanism using Mutiny, part of me learning the library and i am kinda stuck in cancelling the polling when result is found. I tried using the tick() and what i came up with looks like

Multi.createFrom().ticks().every(Duration.ofSeconds(5))
    .onItem().transformToMultiAndMerge(tick -> {
      System.out.println("Tick:" + tick);
      return Multi.createFrom()
          .<Transaction>emitter(
              emitter -> {
                service.getTransactions().toMulti()
                    .onItem().transformToMultiAndMerge(
                        transactions -> Multi.createFrom().iterable(transactions))
                    .subscribe().with(transaction -> {
                      if (!verification.isOngoing()) {
                        emitter.fail(new TransactionVerificationException());
                      } else {
                        boolean transactionFound = transaction.getAmount().stream().anyMatch(
                            amount -> amount.getQuantity()
                                .equals("test"));
                        if (transactionFound) {
                          emitter.emit(transaction);
                          emitter.complete();
                        } 
                      }
                    });
              });
    })
    .subscribe()
    .with(transaction -> log.info(transaction),
        x -> x.printStackTrace());

Problem here is that the Multi from ticks() is running forever and the only way i think of to cancel it would be to propagate somehow that the emitter has completed. The case here is that i want to emit, and process only if certain conditions are met.

tmarwen
  • 15,750
  • 5
  • 43
  • 62
Panos Karampis
  • 557
  • 6
  • 18
  • Could you elaborate more on your use case? Would you want the upstream `service.getTransactions()` first valid item to be emitted then cancel all the polling altogether (i.e. emit one item and cancel all next ticks)? Do you expect one `transaction` item to be valid (matching your `"test"` predicate)? – tmarwen Jul 05 '22 at 15:08
  • 1
    My use case is to poll an API for a max amount of time (= while `isOngoing == true`) or until the first transaction that matches the predicate is found. If one of the two happens, cancel the ticking and process further the one emitted transaction. `service.getTransactions()` then is completed, parent is cancelled, i can start another ticking when needed without abusing resources. – Panos Karampis Jul 05 '22 at 17:49

1 Answers1

2

You approach is almost correct, though,

  • there is no need to create a custom MultiEmitter out of an existing Multi (or transformed Uni) as you can leverage the different Multi operators on top of your source service#getTransaction result
  • you missed the EmptyMulti source which will automatically complete downstream subscriber chain and which you can use to signal an absence of valid item (i.e. Transaction)
  • you need to select the first valid item (being non-null) then transform your Multi to Uni which will result in the upstream subscription being cancelled automatically once an item is received

Here down what the stream pipeline would look like:

Multi.createFrom()
        .ticks()
        .every(Duration.ofSeconds(5))
        .onItem()
        // flat map the ticks to the `service#getTransactions` result
        .transformToMultiAndMerge(tick -> service.getTransactions()
                .toMulti()
                .onItem()
                // flatten Collection<Transaction> to Multi<Transaction>
                .transformToIterable(Function.identity())
                .onItem()
                .transformToMultiAndMerge(transaction -> {
                    if (!verification.isOngoing()) {
                        return Multi.createFrom().failure(new TransactionVerificationException());
                    } else {
                        boolean transactionFound = transaction.getAmount()
                                .stream()
                                .anyMatch(amount -> amount.getQuantity().equals("test"));
                        if (transactionFound) {
                            return Multi.createFrom().item(transaction);
                        } else {
                            return Multi.createFrom().empty();
                        }
                    }
                })
        )
        .select()
        .first(Objects::nonNull)
        .toUni()
        .subscribe()
        .with(transaction -> log.info(transaction), x -> x.printStackTrace());
tmarwen
  • 15,750
  • 5
  • 43
  • 62