I frequently use Awaitility
in my tests to handle asynchronous operations. However, each Awaitility
call spawns threads, which results in high memory consumption when running large tests. I’m searching for an alternative solution that provides similar functionality but is more memory-efficient.
Example Scenario:
Below is a simplified example where I need to find 500 random prime numbers between 0
and 10,000,000
.
Using Awaitility:
static void waitWithAwaitility() {
AtomicInteger primeCount = new AtomicInteger(0);
await()
.atMost(1, TimeUnit.HOURS)
.pollInterval(3, TimeUnit.SECONDS)
.until(() -> {
int number = supplierMethod().get();
if (isPrime(number)) {
System.out.println("Found prime: " + number);
primeCount.incrementAndGet();
}
return primeCount.get() >= 500;
});
}
Helper Methods:
public static Supplier<Integer> supplierMethod() {
int number = new Random().nextInt(10_000_000) + 1;
System.out.println("Generated number: " + number);
return () -> number;
}
public static boolean isPrime(int number) {
if (number <= 1) return false;
for (int i = 2; i <= Math.sqrt(number); i++) {
if (number % i == 0) return false;
}
return true;
}
When I profile the memory usage with tools like VisualVM, I notice a significant memory footprint.
Exploring Alternatives:
ChatGPT suggested using Reactive Programming (with Project Reactor) as an alternative. I tried implementing the solution below:
Reactive Programming Example:
public Mono<Integer> reactiveAwait(
Supplier<Integer> supplierMethod,
Predicate<Integer> condition,
Duration pollInterval,
Duration timeout
) {
return Mono.defer(() -> Mono.fromSupplier(supplierMethod))
.repeatWhen(repeat -> repeat.delayElements(pollInterval))
.takeUntil(condition::test)
.last()
.timeout(timeout, Mono.error(new RuntimeException("Timeout exceeded")))
.onErrorResume(throwable -> {
System.err.println(throwable.getMessage());
return Mono.empty();
})
.subscribeOn(Schedulers.boundedElastic());
}
static void waitWithJavaRx() {
AtomicInteger primeCount = new AtomicInteger(0);
Mono<Integer> result = new DifferentAwaitility().reactiveAwait(
() -> supplierMethod().get(),
number -> {
boolean primeStatus = isPrime(number);
System.out.println("Is prime? " + primeStatus);
if (primeStatus) {
primeCount.incrementAndGet();
}
return primeCount.get() >= 500;
},
Duration.ofSeconds(3),
Duration.ofHours(1)
);
Integer finalResult = result.block();
if (finalResult != null) {
System.out.println("Result: " + finalResult);
} else {
System.err.println("Error: Timeout or other failure occurred");
}
}
Issue with the Reactive Approach:
While this approach works, the memory consumption was three times higher than when using Awaitility
. Below are VisualVM screenshots comparing the memory usage:
- Awaitility Memory Usage
- Reactive Programming Memory Usage
Question:
Is there a better approach to achieving similar functionality as Awaitility
but with lower memory consumption? I’ve explored Reactive Programming, but it didn’t help in reducing memory usage. Any suggestions or optimizations would be greatly appreciated!
1