Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MonoThen creates deep stack traces leading to StackOverflowError #3095

Closed
nelapsi13 opened this issue Jun 29, 2022 · 7 comments
Closed

MonoThen creates deep stack traces leading to StackOverflowError #3095

nelapsi13 opened this issue Jun 29, 2022 · 7 comments
Assignees
Labels
status/declined We feel we shouldn't currently apply this change/suggestion
Milestone

Comments

@nelapsi13
Copy link

Expected behavior

some kind of error?

Actual behavior

block() never finishes

Steps to reproduce

following code

  1. causes java.lang.StackOverflowError (which is I guess expected according to Map and flatMap operator chaining is not stack-safe #1441)
  2. then blocks forever (which doesn't seem expected)
package org.example;

import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;

public class App {
    static AtomicInteger depth = new AtomicInteger(100000);

    private static Mono<Integer> source() {
        return Mono.fromSupplier(() -> {
            var i = depth.decrementAndGet();
            return  i > 0 ? null : 1;
        });
    }

    static Mono<Integer> waitForData() {
        return source()
            .switchIfEmpty(Mono.defer(() ->
//                Mono.just(1).delayElement(Duration.ofNanos(1))
                Mono.delay(Duration.ofNanos(1))
                    .then(Mono.defer(App::waitForData))));
    }

    public static void main(String[] args) {
        waitForData().block();
    }
}

@reactorbot reactorbot added the ❓need-triage This issue needs triage, hasn't been looked at by a team member yet label Jun 29, 2022
@OlegDokuka OlegDokuka added type/bug A general bug status/need-decision This needs a decision from the team and removed ❓need-triage This issue needs triage, hasn't been looked at by a team member yet type/bug A general bug status/need-decision This needs a decision from the team labels Jul 4, 2022
@OlegDokuka
Copy link
Contributor

MonoThen recursion-related bug. Can have a fix similar to recursion protection in reactor.core.publisher.FluxConcatArray#ConcatArraySubscriber

@nelapsi13
Copy link
Author

not quite sure about " Can have a fix similar to recursion protection in reactor.core.publisher.FluxConcatArray#ConcatArraySubscriber".

wil it fix never-finishing block() call?

@OlegDokuka
Copy link
Contributor

@nelapsi13. Yes, basically your code creates too deep stack trace so at some point any call that the reactor tries to do ends with Overflowexception which can not be handled. However, if we apply a trick, we can have that stack trace be in constant size, so an exception will not throw and the result wich unblock a lock will be delivered

@simonbasle simonbasle removed the status/need-decision This needs a decision from the team label Jul 18, 2022
@simonbasle simonbasle added this to the 3.4.x Backlog milestone Jul 18, 2022
@simonbasle simonbasle changed the title java.lang.StackOverflowError and endless block() MonoThen creates deep stack traces leading to StackOverflowError Jul 18, 2022
@OlegDokuka OlegDokuka removed the type/bug A general bug label Aug 18, 2022
@chemicL
Copy link
Member

chemicL commented Aug 18, 2022

As a matter of fact, the case is different from reactor.core.publisher.FluxConcatArray. The code is creating a recursion, for which we don't have a way to overcome. The subscription flow is forming a recursive object hierarchy:

block (0) -> waitForData (1) -> switchIfEmpty (2) -> defer (3) -> then (4) -> defer (5) -> waitForData (1') -> ...

As (1) and (1') are different objects, there is no way of determining the recursion and thinking about any internal optimisation. Consider structuring the reactive chain differently, as this type of generator is doomed to cause StackOverflowError.

Perhaps, instead of:

waitForData().block();

this could do the trick instead:

Flux.from(source()).single().retry().block();

@chemicL chemicL closed this as not planned Won't fix, can't repro, duplicate, stale Aug 18, 2022
@OlegDokuka OlegDokuka added the status/declined We feel we shouldn't currently apply this change/suggestion label Aug 18, 2022
@nelapsi13
Copy link
Author

StackOverflowError is not a problem (more or less)
the problem is that block() never finishes (i.e. blocks forever)

@chemicL
Copy link
Member

chemicL commented Aug 18, 2022

The reactive chain is unable to terminate block on such an occasion, as when a SOE happens, the terminal signal can't be delivered to block's subscriber. All in all, JVM VirtualMachineErrors (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/VirtualMachineError.html) are not considered recoverable.
Also, please note in the latest version the logging of such erroneous states has been added: #3122

@nelapsi13
Copy link
Author

ok, thanks for clarification

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/declined We feel we shouldn't currently apply this change/suggestion
Projects
None yet
Development

No branches or pull requests

5 participants