453bcdaba9ef

fix: dialer takes 60s cooldown after clean-complete close (v2.9.17)

After a clean 'relay session complete' the dialer was re-dialing the same target at the 5_000ms reconcile cadence, burning a full 20s ping/pong timeout per cycle. Reproduced live: 00:10:16.688 clean close -> 00:10:21.691 redial -> 00:11:08.408 timeout. Visible in logcat as the third/fourth dial attempts after a successful sync. - New constant DIAL_CLEAN_COMPLETE_COOLDOWN_MS=60_000L (private), used only by the new early-return in nextRelayRetryDelay. - nextRelayRetryDelay: succeed+dialer now returns the long cooldown instead of resetting to DIAL_RETRY_DELAY_MS. Waiter success and failed-dialer paths unchanged. Minimum diff, single line of new branch logic. - Existing test renamed and updated to pin the new behavior: succeeded+dialer pins 60_000L, not 5_000L. The spec test 'clean-complete dial ramps the dialer cooldown above the reconcile hot loop' (added in v2.9.16) flips from intentionally red to green as the executable spec for this fix. CONTINUITY.md updated; checkpoint ce204bae8990 is the pre-edit restore point. Gate: testDebugUnitTest --tests com.peachpasswords.sync.* --rerun-tasks — 37 sync classes, 0 failures, 0 errors. Full sync package clean.

time
2026-07-31 11:01 UTC · 2 months ago
machine
koh2
parent
36feed1efeba
3 files VERSION+1 −1app/src/main/java/com/peachpasswords/sync/CloudRelayReconnectListener.kt+9 −0app/src/test/java/com/peachpasswords/sync/CloudRelayReconnectListenerTest.kt+9 −2
VERSION+1 −1
@@ -1 +1 @@1-2.9.16\ No newline at end of file1+2.9.17\ No newline at end of file

view file

app/src/main/java/com/peachpasswords/sync/CloudRelayReconnectListener.kt+9 −0
@@ -53,6 +53,14 @@5353 internal const val DIAL_SESSION_TIMEOUT_MS = SESSION_TIMEOUT_MS + DIAL_HANDSHAKE_HEADROOM_MS5454 /** Slower than the waiter's retry, so the peer is already in the room. */5555 private const val DIAL_RETRY_DELAY_MS = 5_000L56+/**57+ * Cooldown the dialer takes after a clean `relay session complete` close58+ * before re-dialing the same target. Must clear the reconcile cadence by59+ * an order of magnitude or the dialer re-dials into a closed socket and60+ * burns a full 20s ping/pong timeout per cycle. 60s is conservative: the61+ * reconcile loop will re-spawn this target sooner if anything changes.62+ */63+private const val DIAL_CLEAN_COMPLETE_COOLDOWN_MS = 60_000L5664 private const val RELAY_CONNECT_TIMEOUT_MS = 10_000L5765 private const val RELAY_INBOX_CAPACITY = 85866 private const val PEACH_SYNC_LOG_TAG = "PeachSync"@@ -122,6 +130,7 @@122130 }123131 124132 internal fun nextRelayRetryDelay(current: Long, succeeded: Boolean, dialer: Boolean): Long {133+    if (succeeded && dialer) return DIAL_CLEAN_COMPLETE_COOLDOWN_MS125134     val initial = if (dialer) DIAL_RETRY_DELAY_MS else RETRY_DELAY_MS126135     return if (succeeded) initial else (current * 2).coerceAtMost(MAX_RETRY_DELAY_MS)127136 }

view file

app/src/test/java/com/peachpasswords/sync/CloudRelayReconnectListenerTest.kt+9 −2
@@ -70,11 +70,18 @@7070     }7171 7272     @Test73-    fun `relay retry backs off after failures and resets after success`() {73+    fun `relay retry backs off after failures, resets on waiter success, and cooldowns on dialer success`() {74+        // Failures double up to the cap regardless of role.7475         assertEquals(2_000L, nextRelayRetryDelay(1_000L, succeeded = false, dialer = false))7576         assertEquals(30_000L, nextRelayRetryDelay(20_000L, succeeded = false, dialer = false))77+        // A waiter that just finished a clean session is parked for the78+        // ordinary RETRY_DELAY_MS; nothing for it to reconcile against.7679         assertEquals(1_000L, nextRelayRetryDelay(30_000L, succeeded = true, dialer = false))77-        assertEquals(5_000L, nextRelayRetryDelay(30_000L, succeeded = true, dialer = true))80+        // A dialer that just finished a clean session must NOT re-dial at the81+        // reconcile cadence — that is the idle-burn. It takes the long cooldown82+        // so the reconcile loop has time to either re-spawn it for a reason or83+        // leave it parked.84+        assertEquals(60_000L, nextRelayRetryDelay(30_000L, succeeded = true, dialer = true))7885     }7986 8087     @Test

view file