joffrey-bion / krossbow

A Kotlin multiplatform coroutine-based STOMP client over websockets, with built-in conversions.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

FATAL EXCEPTION: Frames flow closed unexpectedly while waiting for RECEIPT frame

domin0311 opened this issue · comments

What happened?

Hi, I am testing the library against my springboot server and noticed a crash in my Android app coming from the BaseStompSession class.

I can't seem to reproduce it anymore, however, it would be great if there is a way to handle the exception more gracefully by disconnecting the session instead.

FATAL EXCEPTION: DefaultDispatcher-worker-396
java.lang.IllegalStateException: Frames flow closed unexpectedly while waiting for RECEIPT frame with id='42d156cc-2720-407a-9de2-a303a5642d8a'
at org.hildan.krossbow.stomp.BaseStompSession$sendAndWaitForReceipt$2.invokeSuspend(BaseStompSession.kt:157)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

I am also using HeartBeat option with the following settings:

 heartBeat = HeartBeat(
                    minSendPeriod = 2.seconds,
                    expectedPeriod = 2.seconds
            )
            heartBeatTolerance = HeartBeatTolerance(
                    outgoingMargin = Duration.ZERO,
                    incomingMargin = 4.seconds
            )

Modules:

  implementation("org.hildan.krossbow:krossbow-websocket-ktor:5.3.0"){
        exclude group: "org.slf4j"
    }
    implementation("org.hildan.krossbow:krossbow-stomp-kxserialization-json:5.3.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
    implementation("io.ktor:ktor-client-cio:2.3.1") {
        exclude group: "org.slf4j"
    }

Other than the library is working great! Thanks!

Reproduction and additional details

No response

Krossbow version

5.3.0

Krossbow modules

krossbow-stomp-kxserialization-json, krossbow-websocket-ktor

Kotlin version

1.8.0

Kotlin target platforms

Android

Hi, thanks a lot for the report and the kind words.

When sending a frame, the contract of the send function is that it suspends until the corresponding RECEIPT frame is received from the server (some sort of acknowledgement). The error you got happens when the connection is closed in the meantime.

it would be great if there is a way to handle the exception more gracefully by disconnecting the session instead

I'm not entirely sure what you mean. The session is in fact disconnected if the websocket is closed. But we still have to do something about the send() call. Would you prefer the send call to just silently return in a way that looks successful? What should the returned StompReceipt be in that case? It would be a bit strange. To me sending a frame with RECEIPT is a bit like an HTTP request. If the request fails, we want the call to fail too. So I think it would be better to have some error handling on the caller's side if this case matters.

That being said, if we consider that there should be error handling on the caller's side, then IllegalStateException is not a good choice. This is usually for when some internal state is broken or state changes happened in an unexpected way. But this case should be expected to happen sometimes and deserves its own exception type so it can be caught properly.

I will look into this.

Hi, thanks for the fast response!
Error handling on the caller's side seems to be the best option, if you could look into introducing a new exception for this scenario that would be great.
I am currently using the try-catch block to catch all the exceptions coming from the lib, but for whatever reason it seems like the IllegalStateException crashed the app instead.
Since I couldn't reproduce this behavior again and could only capture limited logs, there might be some other underlying exception happening.
Thanks for looking into this!

Closing this issue since it will be tracked in #379