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

When high qps, async operation code stack may too deep #5977

Closed
wants to merge 1 commit into from

Conversation

semistone
Copy link

No description provided.

@mrniko
Copy link
Member

mrniko commented Jun 24, 2024

Thanks for suggested changes. Can you share the example of stack you are trying to avoid?

@semistone
Copy link
Author

semistone commented Jun 24, 2024

stack.gz

it's related to issue
#5971

from the debug log implement by me in my debug branch, I could confirm that thread disappear.
so the semaphore never release in that test.
I also shared how to reproduce it in that issue.

@semistone
Copy link
Author

it's simple POC about reduce call stack in AsyncSemaphore

    private void tryRun() {
        while (true) {
            if (counter.decrementAndGet() >= 0) {
                CompletableFuture<Lock> future = listeners.poll();
                if (future == null) {
                    counter.incrementAndGet();
                    return;
                }

                if (!future.isDone()) {
                    // release only once by lock object
                    AtomicBoolean once = new AtomicBoolean();
                    AtomicBoolean tryNext = new AtomicBoolean();
                    long currentThreadId = Thread.currentThread().getId();
                    if (future.complete(() -> { <==== return release runnable 
                        if (once.compareAndSet(false, true)) {
                            // if in the same thread and tryNext is false means already release in the same thread.
                            release(Thread.currentThread().getId() != currentThreadId || tryNext.compareAndSet(false, true));
                        }
                    })) {
                        if (tryNext.compareAndSet(false, true)) { 
                            continue; // continue to tryRun  if not release yet.
                        } else {
                            return;
                        }
                    }
                }
            }

            if (counter.incrementAndGet() <= 0) {
                return;
            }
        }
    

I didn't test and optimism it yet.

});
// prevent deep stack, switch thread per 100 requests.
if (count.incrementAndGet() % 100 == 0) {
f.thenAcceptAsync(r -> connectTo(result, command));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to define serviceManager.getGroup() as an executor.

Copy link
Member

@mrniko mrniko Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serviceManager.getGroup().submit(() -> {
                releaseConnection();
});

in connectTo method now can be replaced with releaseConnection() method call only;

@mrniko mrniko added this to the 3.32.1 milestone Jun 25, 2024
@mrniko mrniko added the bug label Jun 25, 2024
Signed-off-by: Chen, YinChin | Angus <yinchin.chen@rakuten.com>
@semistone
Copy link
Author

semistone commented Jun 26, 2024

I change to always switch thread during release as your suggestion.
I checked when that error happen, it throw StackOverflowError and it's not Exception but throwable.
and the stack size is also depend on how user's implementation, it could happen after very few recursive loop.
so switch thread in every release is is better.

how to you think ?

@semistone
Copy link
Author

semistone commented Jun 26, 2024

or switch in ?

RedisExecutor.sendCommand
            if (connectionManager.getServiceManager().getConfig().getMasterConnectionPoolSize() < 10
                    && !command.isBlockingCommand()) {
                release(connection); <=== here
            }

or

    public void release() {
        counter.incrementAndGet();
        tryRun(); <=== here
    }

@mrniko
Copy link
Member

mrniko commented Jun 26, 2024

Thanks for your assistance. I have fixed it in 3c1c90f

@mrniko mrniko closed this Jun 26, 2024
@mrniko mrniko removed this from the 3.32.1 milestone Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

2 participants