Skip to content

Vertx 4.2.6 WebsocketEndpoint InflightRequest Not Reducing #5415

@gonit

Description

@gonit

Version

Which version(s) did you encounter this bug ?
4.2.6

Context

I encountered a state in my Vertx Java application where the websocket connection requests are not reaching to the server (confirmed by TCPDump). On digging deeper, i noticed that each Vertx context has a HttpClient. Each HttpClient has 2 ConnectionManager one for Http and other for Websocket. WebsocketConnectionManager contains Endpoint map storing a WebsocketEndpoint object against a key - server. The class WebSocketEndpoint maintains 2 important properties - Waiters queue and inflightRequest. For each connection request, if inflightRequest count is less than maxPool, then the request is appended to Waiter queue otherwise inflightRequest is incremented. The inflightRequest count is only decremented after the connection is made(see second code block). Once number of inflight request crosses for a server crosses maxPool than all further requests are queued. We noticed when the maxPool number of connection request fails, the inflightRequest count is not decremented causing all further request to land onto Waiter queue. If this queue is unbounded its a never ending state. All these details are confirmed with heapdump.

public void requestConnection2(ContextInternal ctx, long timeout, Handler<AsyncResult<HttpClientConnection>> handler) {
        synchronized(this) {
            if (this.inflightConnections >= this.maxPoolSize) {
                this.waiters.add(new Waiter(handler, ctx));
                return;
            }
 
            ++this.inflightConnections;
        }
 
        this.tryConnect(ctx, handler);
    }
 
private void tryConnect(ContextInternal ctx, final Handler<AsyncResult<HttpClientConnection>> handler) {
        EventLoopContext eventLoopContext;
        if (ctx instanceof EventLoopContext) {
            eventLoopContext = (EventLoopContext)ctx;
        } else {
            eventLoopContext = ctx.owner().createEventLoopContext(ctx.nettyEventLoop(), ctx.workerPool(), ctx.classLoader());
        }
 
        class Listener implements Handler<AsyncResult<HttpClientConnection>> {
            Listener() {
            }
 
            private void onEvict() {
                WebSocketEndpoint.this.decRefCount();
                Waiter h;
                synchronized(WebSocketEndpoint.this) {
                    if (--WebSocketEndpoint.this.inflightConnections > WebSocketEndpoint.this.maxPoolSize || WebSocketEndpoint.this.waiters.isEmpty()) {
                        return;
                    }
 
                    h = (Waiter)WebSocketEndpoint.this.waiters.poll();
                }
 
                WebSocketEndpoint.this.tryConnect(h.context, h.handler);
            }
 
            public void handle(AsyncResult<HttpClientConnection> ar) {
                if (ar.succeeded()) {
                    HttpClientConnection c = (HttpClientConnection)ar.result();
                    if (WebSocketEndpoint.this.incRefCount()) {
                        c.evictionHandler((v) -> {
                            this.onEvict();
                        });
                        handler.handle(Future.succeededFuture(c));
                    } else {
                        c.close();
                        handler.handle(Future.failedFuture("Connection closed"));
                    }
                } else {
                    handler.handle(Future.failedFuture(ar.cause()));
                }
 
            }
        }
 
        this.connector.httpConnect(eventLoopContext, new Listener());
    }

Do you have a reproducer?

Sorry, this code is part of my repository for which i have not created a reproducer yet. I would like to take guidance on current problem.

Extra

  • JDK17

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions