Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
[ECO-5056] Updated retrieveObjectsGCGracePeriod to return gcperiod ev…
…ery time

CONNECTED message is received, updated tests for the same
  • Loading branch information
sacOO7 authored and ttypic committed Mar 17, 2026
commit 06a2157bf3bf1f4e869bed9f34df54c99b29e908
19 changes: 7 additions & 12 deletions liveobjects/src/main/kotlin/io/ably/lib/objects/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.ably.lib.realtime.ChannelState
import io.ably.lib.realtime.CompletionListener
import io.ably.lib.types.Callback
import io.ably.lib.realtime.ConnectionEvent
import io.ably.lib.realtime.ConnectionState
import io.ably.lib.realtime.ConnectionStateListener
import io.ably.lib.types.ChannelMode
import io.ably.lib.types.ErrorInfo
import io.ably.lib.types.ProtocolMessage
Expand Down Expand Up @@ -51,19 +51,14 @@ internal suspend fun ObjectsAdapter.attachAsync(channelName: String) = suspendCa
}
}

internal fun ObjectsAdapter.retrieveObjectsGCGracePeriod(block : (Long?) -> Unit) {
connectionManager.objectsGCGracePeriod?.let {
block(it)
return
}
// If already connected, no further `connected` event is guaranteed; return immediately.
if (connection.state == ConnectionState.connected) {
block(connectionManager.objectsGCGracePeriod)
return
}
connection.once(ConnectionEvent.connected) {
internal fun ObjectsAdapter.onGCGracePeriodUpdated(block : (Long?) -> Unit) : ObjectsSubscription {
connectionManager.objectsGCGracePeriod?.let { block(it) }
// Return new objectsGCGracePeriod whenever connection state changes to connected
val listener: (_: ConnectionStateListener.ConnectionStateChange) -> Unit = {
block(connectionManager.objectsGCGracePeriod)
}
connection.on(ConnectionEvent.connected, listener)
return ObjectsSubscription { connection.off(listener) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ internal class ObjectsPool(
private val gcScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private var gcJob: Job // Job for the garbage collection coroutine

@Volatile
private var gcGracePeriod = ObjectsPoolDefaults.GC_GRACE_PERIOD_MS
@Volatile private var gcGracePeriod = ObjectsPoolDefaults.GC_GRACE_PERIOD_MS
private var gcPeriodSubscription: ObjectsSubscription

init {
// RTO3b - Initialize pool with root object
pool[ROOT_OBJECT_ID] = DefaultLiveMap.zeroValue(ROOT_OBJECT_ID, realtimeObjects)
// Start garbage collection coroutine with server-provided grace period if available
realtimeObjects.adapter.retrieveObjectsGCGracePeriod { period ->
gcPeriodSubscription = realtimeObjects.adapter.onGCGracePeriodUpdated { period ->
period?.let {
gcGracePeriod = it
Log.i(tag, "Using objectsGCGracePeriod from server: $gcGracePeriod ms")
Expand Down Expand Up @@ -164,6 +164,7 @@ internal class ObjectsPool(
* Should be called when the pool is no longer needed.
*/
fun dispose() {
gcPeriodSubscription.unsubscribe()
gcJob.cancel()
gcScope.cancel()
pool.clear()
Expand Down
22 changes: 11 additions & 11 deletions liveobjects/src/test/kotlin/io/ably/lib/objects/unit/HelpersTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,52 +60,52 @@ class HelpersTest {
}

@Test
fun testRetrieveObjectsGCGracePeriodImmediateInvokesBlock() {
fun testOnGCGracePeriodImmediateInvokesBlock() {
val adapter = getMockObjectsAdapter()
val connManager = adapter.connectionManager
connManager.setPrivateField("objectsGCGracePeriod", 123L)

var value: Long? = null
adapter.retrieveObjectsGCGracePeriod { v -> value = v }
adapter.onGCGracePeriodUpdated { v -> value = v }

assertEquals(123L, value)
verify(exactly = 0) { adapter.connection.once(ConnectionEvent.connected, any<ConnectionStateListener>()) }
verify(exactly = 1) { adapter.connection.on(ConnectionEvent.connected, any<ConnectionStateListener>()) }
}

@Test
fun testRetrieveObjectsGCGracePeriodDeferredInvokesOnConnectedWithValue() {
fun testOnGCGracePeriodDeferredInvokesOnConnectedWithValue() {
val adapter = getMockObjectsAdapter()
val connManager = adapter.connectionManager
val connection = adapter.connection

var value: Long? = null
every { connection.once(ConnectionEvent.connected, any<ConnectionStateListener>()) } answers {
every { connection.on(ConnectionEvent.connected, any<ConnectionStateListener>()) } answers {
val listener = secondArg<ConnectionStateListener>()
connManager.setPrivateField("objectsGCGracePeriod", 456L)
listener.onConnectionStateChanged(mockk(relaxed = true))
}

adapter.retrieveObjectsGCGracePeriod { v -> value = v }
adapter.onGCGracePeriodUpdated { v -> value = v }

assertEquals(456L, value)
verify(exactly = 1) { connection.once(ConnectionEvent.connected, any<ConnectionStateListener>()) }
verify(exactly = 1) { connection.on(ConnectionEvent.connected, any<ConnectionStateListener>()) }
}
Comment thread
sacOO7 marked this conversation as resolved.

@Test
fun testRetrieveObjectsGCGracePeriodDeferredInvokesOnConnectedWithNull() {
fun testOnGCGracePeriodDeferredInvokesOnConnectedWithNull() {
val adapter = getMockObjectsAdapter()
val connection = adapter.connection

var value: Long? = null
every { connection.once(ConnectionEvent.connected, any<ConnectionStateListener>()) } answers {
every { connection.on(ConnectionEvent.connected, any<ConnectionStateListener>()) } answers {
val listener = secondArg<ConnectionStateListener>()
listener.onConnectionStateChanged(mockk(relaxed = true))
}

adapter.retrieveObjectsGCGracePeriod { v -> value = v }
adapter.onGCGracePeriodUpdated { v -> value = v }

assertNull(value)
verify(exactly = 1) { connection.once(ConnectionEvent.connected, any<ConnectionStateListener>()) }
verify(exactly = 1) { connection.on(ConnectionEvent.connected, any<ConnectionStateListener>()) }
}
Comment thread
sacOO7 marked this conversation as resolved.

@Test
Expand Down
Loading