vert-x3 / vertx-stomp

STOMP client/server implementation
Apache License 2.0
31 stars 28 forks source link

Memory leak with pendingReceipts with continuous PING frames #76

Closed DevMcC closed 1 year ago

DevMcC commented 1 year ago

Version

4.3.7

Context

Our applications were having memory issues. Upon analyzing the heapdump, it appears that the StompClientConnectionImpl had accumulated over 58k pending receipts.

I have created a reproducer, that simply creates the StompClient and StompClientConnection. Using a Timer and Reflection, it prints out the current number of pending receipts every second. When running this reproducer it appears the StompClientConnectionImpl is adding a pending receipt every second.

These receipts come from PING frames, that never get handled. According to the code, when a PING frame is received, nothing is being done - not even removing the pending receipt. Due to this the pendingReceipts Map is getting bigger and bigger every second.

Do you have a reproducer?

fun main() {
    val vertx = Vertx.vertx()
    val client = StompClient.create(vertx)
    client.connect(port, host) { connectionResult ->
        println("Connected!")
        val connection = connectionResult.result()

        Timer().schedule(1_000, 1_000) {
            val pendingReceiptsField = connection.javaClass.getDeclaredField("pendingReceipts")
            pendingReceiptsField.trySetAccessible()
            val pendingReceipts = pendingReceiptsField.get(connection) as Map<String, Promise<Void>>

            println("There are currently ${pendingReceipts.size} pending receipts")
        }
    }
}

Screenshots

image

adhesivee commented 1 year ago

It seems something has changed in a later version here. Receipt handler used to be null in some cases. This library consider PING as a Frame and thus using send which creates a non-null field for receiptHandler, which can't be handled because "PING" is just \n. And this will make sure this will stack up with every PING