4

In one class I want to call a method, but not have to wait until the method finishes. Normally in a spring application I would use @Async, but what is the way to go about in a Quarkus application?

Beneath is a simple example to get started. In the 'StartWork' class the 'Work' gets started. (I left out the Work-interface, but you can see one of its implementations: WorkA). After calling 'work.do()' the startWork() method should proceed without waiting for the work.do() to finish.

@ApplicationScoped
public class WorkA implements Work {
    public void do() {
        System.out.println("Starting work A.");
        try {
            Thread.sleep(1000l);
            System.out.println("Finished work A.");
        } catch(InterruptedException ex) {
            System.out.println("My work got interrupted.");
        }
    }
}

@ApplicationScoped
public class StartWork {

@Inject
Work work;

    public void startWork() {
        work.do();
        System.out.println("I dont' care when and if the work finished, but it has started.");
    }
}

Here is the same example, but now I've tried to use Mutiny:

@ApplicationScoped
public class WorkA implements Work {
    public void do() {
        Uni.createFrom().voidItem().invoke(Runnable -> {
            System.out.println("Starting work A.");
            try {
                Thread.sleep(1000l);
                System.out.println("Finished work A.");
            } catch(InterruptedException ex) {
                System.out.println("My work got interrupted.");
            }
        }
    });
}

@ApplicationScoped
public class StartWork {

@Inject
Work work;

    public void startWork() {
        work.do();
        System.out.println("I dont' care when and if the work finished, but it has started.");
    }
}

When running this example I do not see the lines being printed. So I guess the anonymous runnable is not invoked?

Minimal reproducible product: https://gitlab.com/rmvanderspek/quarkus-multithreading

Robert van der Spek
  • 1,017
  • 3
  • 16
  • 37
  • I am not sure whether quarkus supports [`@Asynchronous`](https://jakarta.ee/specifications/platform/8/apidocs/javax/ejb/asynchronous) (I suspect not). The "quarkus"-way would be to use [Mutiny](https://quarkus.io/guides/getting-started-reactive). – Turing85 Dec 21 '20 at 11:50
  • Could you show how that would work in this example? I have tried, but it doesn't work as I suspect it to. All examples I find are in combination with Rest. – Robert van der Spek Dec 21 '20 at 13:19
  • I have added my non-working example with Mutiny to the original question. – Robert van der Spek Dec 21 '20 at 13:26
  • What exactly does "*doesn't work as expected*" mean? I am not an expert on mutiny, but the setup looks fine to me. – Turing85 Dec 21 '20 at 13:31
  • Also, providing a [MRE] (e.g. in form of a git repo) might help. – Turing85 Dec 21 '20 at 13:35
  • Her is an example project: https://gitlab.com/rmvanderspek/quarkus-multithreading When you go to the rest endpoint, the doWork should be invoked twice. It is, but you don't see any logging that's inside the Runnable. – Robert van der Spek Dec 21 '20 at 14:17
  • "*The repository for this project is empty*" (possibly forgot to `git push`?). Also, I would suggest to use an actual logger. I am not quite sure how quarkus handles logging to `System.out`, especially in the present of asynchronous processng. – Turing85 Dec 21 '20 at 14:32
  • Lol, sorry about that, I did forget to push. – Robert van der Spek Dec 21 '20 at 14:36

1 Answers1

7

Kudo's to Turing85 for finding the answer.

As it turns out Quarkus works with an EventBus for asynchronous actions. A producer is created, but works lazily and thus won't be invoked until a consumer is subscribed to this producer.

A working example: https://gitlab.com/rmvanderspek/quarkus-multithreading

In short:

@ApplicationScoped
public class WorkA implements Work {

    @Override
    public void doWork() {
        log.info("Do work");

        Uni.createFrom()
        .item(UUID::randomUUID)
        .emitOn(Infrastructure.getDefaultWorkerPool())
        .subscribe()
        .with(this::worker, Throwable::printStackTrace);
    }

    private Uni<Void> worker(UUID uuid) {
        log.info("Starting work: " + uuid);
        try {
            Thread.sleep((long) Math.random() * 1000);
        } catch (InterruptedException ex) {
            log.info("Could not finish work: " + uuid);
            throw new RuntimeException(ex);
        }
        log.info("Finish work: {}.", uuid);
        return Uni.createFrom().voidItem();
    }
}

@ApplicationScoped
public class StartWork {

    @Inject
    Work work;

    public void startWork() {
        work.do();
        System.out.println("I dont' care when and if the work finished, but it has started.");
    }
}
Robert van der Spek
  • 1,017
  • 3
  • 16
  • 37