Skip to content

per-channel back off#22

Open
UnknownJoe796 wants to merge 2 commits into
version-6from
version-6-per-channel-backoff
Open

per-channel back off#22
UnknownJoe796 wants to merge 2 commits into
version-6from
version-6-per-channel-backoff

Conversation

@UnknownJoe796

Copy link
Copy Markdown
Contributor

No description provided.

@UnknownJoe796 UnknownJoe796 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Automated Review (commit d8b127b)Now I have a complete picture. Here's my review:


PR #22: per-channel back off — Review Summary

Clean, well-structured addition of exponential backoff for websocket channel reconnection. The ChannelBackoff class is well-tested and the integration into BulkFetcher.WebsocketChannel is straightforward. Two issues found.


1. Backoff bypassed when server rejects a channel (end without start)

File: ChannelBackoff.kt:29 / BulkFetcher.kt:164-177
Confidence: Medium

connectionOpenedAt is initialized to 0L and only updated when onConnectionOpened() is called (on receiving message.start). If the server rejects a channel by sending end=true without a prior start=true (confirmed possible from server-side MultiplexWebSocketHandler), then onConnectionOpened() is never called for that attempt.

When onConnectionClosed(false) runs:

connectionDuration = timeSource() - 0   // = current epoch ms, a huge number
huge_number < 5000                       // = false
→ backoff resets to 0

This means any built-up backoff is incorrectly wiped out by a rejection. In the specific scenario where a channel starts rapidly failing after some successful connections, a rejection resets the backoff counter and the client retries immediately.

Suggested fix: Track whether onConnectionOpened was called since the last close, and treat "never opened" as an immediate failure:

private var connectionOpenedAt: Long = 0L
private var wasOpened = false

fun onConnectionOpened() {
    connectionOpenedAt = timeSource()
    wasOpened = true
}

fun onConnectionClosed(wasIntentional: Boolean) {
    if (wasIntentional) {
        backoffMs = 0L
        wasOpened = false
        return
    }

    val isQuickFailure = if (!wasOpened) {
        true  // Never opened = immediate rejection = failure
    } else {
        (timeSource() - connectionOpenedAt) < stableConnectionThreshold.inWholeMilliseconds
    }
    wasOpened = false

    if (isQuickFailure) {
        backoffMs = if (backoffMs == 0L) initialBackoff.inWholeMilliseconds
                    else (backoffMs * 2).coerceAtMost(maxBackoff.inWholeMilliseconds)
    } else {
        backoffMs = 0L
    }
}

2. package-lock.json accidentally committed

File: package-lock.json (5,154 new lines)
Confidence: High

A full package-lock.json was added in this PR. The project's .gitignore doesn't exclude it, but this is a Kotlin Multiplatform project where the sole package.json dependency (@js-joda/timezone) is a Gradle-managed JS interop dependency. This lockfile is almost certainly an accidental commit and adds noise to the diff. Consider adding package-lock.json to .gitignore and removing it from this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants