Skip to content

Commit

Permalink
Add support for multiplexing (enabled by default) and SyncTimeoutOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Melchior committed Nov 21, 2023
1 parent a88bb46 commit fff8aac
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

### Enhancements
* Support for experimental K2-compilation with `kotlin.experimental.tryK2=true`. (Issue [#1483](https://github.com/realm/realm-kotlin/issues/1483))
* [Sync] Added support for multiplexing sync connections. When enabled (the default), a single
connection is used per sync user rather than one per synchronized Realm. This
reduces resource consumption when multiple Realms are opened and will
typically improve performance. The behavior can be controlled through [AppConfiguration.Builder.enableSessionMultiplexing]. (Issue [#XXXX]())
* [Sync] Various sync timeout options can now be configured through `AppConfiguration.Builder.syncTimeouts()`. (Issue [#971](https://github.com/realm/realm-kotlin/issues/971)).

### Fixed
* None.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,12 @@ expect object RealmInterop {
applicationInfo: String
)

fun realm_sync_client_config_set_connect_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong)
fun realm_sync_client_config_set_connection_linger_time(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong)
fun realm_sync_client_config_set_ping_keepalive_period(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong)
fun realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong)
fun realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong)

fun realm_sync_config_new(
user: RealmUserPointer,
partition: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,26 @@ actual object RealmInterop {
)
}

actual fun realm_sync_client_config_set_connect_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realmc.realm_sync_client_config_set_connect_timeout(syncClientConfig.cptr(), timeoutMs.toLong())
}

actual fun realm_sync_client_config_set_connection_linger_time(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realmc.realm_sync_client_config_set_connection_linger_time(syncClientConfig.cptr(), timeoutMs.toLong())
}

actual fun realm_sync_client_config_set_ping_keepalive_period(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realmc.realm_sync_client_config_set_ping_keepalive_period(syncClientConfig.cptr(), timeoutMs.toLong())
}

actual fun realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realmc.realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig.cptr(), timeoutMs.toLong())
}

actual fun realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realmc.realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig.cptr(), timeoutMs.toLong())
}

actual fun realm_network_transport_new(networkTransport: NetworkTransport): RealmNetworkTransportPointer {
return LongPointerWrapper(realmc.realm_network_transport_new(networkTransport))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,26 @@ actual object RealmInterop {
)
}

actual fun realm_sync_client_config_set_connect_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_connect_timeout(syncClientConfig.cptr(), timeoutMs)
}

actual fun realm_sync_client_config_set_connection_linger_time(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_connection_linger_time(syncClientConfig.cptr(), timeoutMs)
}

actual fun realm_sync_client_config_set_ping_keepalive_period(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_ping_keepalive_period(syncClientConfig.cptr(), timeoutMs)
}

actual fun realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig.cptr(), timeoutMs)
}

actual fun realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig.cptr(), timeoutMs)
}

actual fun realm_sync_config_set_error_handler(
syncConfig: RealmSyncConfigurationPointer,
errorHandler: SyncErrorCallback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ import io.realm.kotlin.mongodb.internal.AppConfigurationImpl
import io.realm.kotlin.mongodb.internal.KtorNetworkTransport
import io.realm.kotlin.mongodb.internal.LogObfuscatorImpl
import io.realm.kotlin.mongodb.sync.SyncConfiguration
import io.realm.kotlin.mongodb.sync.SyncTimeoutOptions
import io.realm.kotlin.mongodb.sync.SyncTimeoutOptionsBuilder
import kotlinx.coroutines.CoroutineDispatcher
import org.mongodb.kbson.ExperimentalKBsonSerializerApi
import org.mongodb.kbson.serialization.EJson
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

/**
* An **AppConfiguration** is used to setup linkage to an Atlas App Services Application.
Expand Down Expand Up @@ -102,6 +107,23 @@ public interface AppConfiguration {
*/
public val httpLogObfuscator: HttpLogObfuscator?

/**
* If enabled (the default), a single connection is used for all Realms opened
* with a single sync user. If disabled, a separate connection is used for each
* Realm.
*
* Session multiplexing reduces resources used and typically improves
* performance. When multiplexing is enabled, the connection is not immediately
* closed when the last session is closed, and instead remains open for
* [SyncTimeoutOptions.connectionLingerTime] defined in [syncTimeoutOptions].
*/
public val enableSessionMultiplexing: Boolean

/**
* The configured timeouts for various aspects of the sync connection from realms.
*/
public val syncTimeoutOptions: SyncTimeoutOptions

public companion object {
/**
* The default url for App Services applications.
Expand Down Expand Up @@ -149,6 +171,8 @@ public interface AppConfiguration {
private var httpLogObfuscator: HttpLogObfuscator? = LogObfuscatorImpl
private val customRequestHeaders = mutableMapOf<String, String>()
private var authorizationHeaderName: String = DEFAULT_AUTHORIZATION_HEADER_NAME
private var enableSessionMultiplexing: Boolean = true
private var syncTimeoutOptions: SyncTimeoutOptions = SyncTimeoutOptionsBuilder().build()

/**
* Sets the encryption key used to encrypt the user metadata Realm only. Individual
Expand Down Expand Up @@ -325,6 +349,33 @@ public interface AppConfiguration {
this.ejson = ejson
}

/**
* If enabled (the default), a single connection is used for all Realms opened
* with a single sync user. If disabled, a separate connection is used for each
* Realm.
*
* Session multiplexing reduces resources used and typically improves
* performance. When multiplexing is enabled, the connection is not immediately
* closed when the last session is closed, and instead remains open for
* [SyncTimeoutOptions.connectionLingerTime] as defined by [syncTimeouts] (30 seconds by
* default).
*/
public fun enableSessionMultiplexing(enabled: Boolean): Builder {
this.enableSessionMultiplexing = enabled
return this
}

/**
* Configure the assorted types of connection timeouts for sync connections.
* See [SyncTimeoutOptionsBuilder] for a description of each option.
*/
public fun syncTimeouts(action: SyncTimeoutOptionsBuilder.()->Unit): Builder {
val builder = SyncTimeoutOptionsBuilder()
action(builder)
syncTimeoutOptions = builder.build()
return this
}

/**
* Allows defining a custom network transport. It is used by some tests that require simulating
* network responses.
Expand Down Expand Up @@ -411,6 +462,8 @@ public interface AppConfiguration {
httpLogObfuscator = httpLogObfuscator,
customRequestHeaders = customRequestHeaders,
authorizationHeaderName = authorizationHeaderName,
enableSessionMultiplexing = enableSessionMultiplexing,
syncTimeoutOptions = syncTimeoutOptions,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import io.realm.kotlin.internal.util.DispatcherHolder
import io.realm.kotlin.mongodb.AppConfiguration
import io.realm.kotlin.mongodb.AppConfiguration.Companion.DEFAULT_BASE_URL
import io.realm.kotlin.mongodb.HttpLogObfuscator
import io.realm.kotlin.mongodb.sync.SyncTimeoutOptions
import org.mongodb.kbson.ExperimentalKBsonSerializerApi
import org.mongodb.kbson.serialization.EJson

Expand All @@ -56,6 +57,8 @@ public class AppConfigurationImpl @OptIn(ExperimentalKBsonSerializerApi::class)
override val httpLogObfuscator: HttpLogObfuscator?,
override val customRequestHeaders: Map<String, String>,
override val authorizationHeaderName: String,
override val enableSessionMultiplexing: Boolean,
override val syncTimeoutOptions: SyncTimeoutOptions,
) : AppConfiguration {

/**
Expand Down Expand Up @@ -154,9 +157,6 @@ public class AppConfigurationImpl @OptIn(ExperimentalKBsonSerializerApi::class)
syncRootDirectory
)

// Disable multiplexing. See https://github.com/realm/realm-core/issues/6656
RealmInterop.realm_sync_client_config_set_multiplex_sessions(syncClientConfig, false)

encryptionKey?.let {
RealmInterop.realm_sync_client_config_set_metadata_encryption_key(
syncClientConfig,
Expand All @@ -177,6 +177,31 @@ public class AppConfigurationImpl @OptIn(ExperimentalKBsonSerializerApi::class)
it
)
}

// Setup multiplexing
RealmInterop.realm_sync_client_config_set_multiplex_sessions(syncClientConfig, enableSessionMultiplexing)

// Setup SyncTimeoutOptions
RealmInterop.realm_sync_client_config_set_connect_timeout(
syncClientConfig,
syncTimeoutOptions.connectTimeout.inWholeMilliseconds.toULong()
)
RealmInterop.realm_sync_client_config_set_connection_linger_time(
syncClientConfig,
syncTimeoutOptions.connectionLingerTime.inWholeMilliseconds.toULong()
)
RealmInterop.realm_sync_client_config_set_ping_keepalive_period(
syncClientConfig,
syncTimeoutOptions.pingKeepalivePeriod.inWholeMilliseconds.toULong()
)
RealmInterop.realm_sync_client_config_set_pong_keepalive_timeout(
syncClientConfig,
syncTimeoutOptions.pongKeepalivePeriod.inWholeMilliseconds.toULong()
)
RealmInterop.realm_sync_client_config_set_fast_reconnect_limit(
syncClientConfig,
syncTimeoutOptions.fastReconnectLimit.inWholeMilliseconds.toULong()
)
}

internal companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2023 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.realm.kotlin.mongodb.sync

import kotlin.time.Duration

/**
* The configured timeouts for various aspects of the sync connection between synchronized realms
* and App Services.
*/
public data class SyncTimeoutOptions(

/**
* The maximum time to allow for a connection to become fully established. This includes
* the time to resolve the network address, the TCP connect operation, the SSL
* handshake, and the WebSocket handshake.
*/
val connectTimeout: Duration,

/**
* If session multiplexing is enabled, how long to keep connections open while there are
* no active session.
*/
val connectionLingerTime: Duration,

/**
* How long to wait between each ping message sent to the server. The client periodically
* sends ping messages to the server to check if the connection is still alive. Shorter
* periods make connection state change notifications more responsive at the cost of
* battery life (as the antenna will have to wake up more often).
*/
val pingKeepalivePeriod: Duration,

/**
* How long to wait for the server to respond to a ping message. Shorter values make
* connection state change notifications more responsive, but increase the chance of
* spurious disconnections.
*/
val pongKeepalivePeriod: Duration,

/**
* When a client first connects to the server, it downloads all data from the server
* before it begins to upload local changes. This typically reduces the total amount
* of merging needed and gets the local client into a useful state faster. If a
* disconnect and reconnect happens within the time span of the fast reconnect limit,
* this is skipped and the session behaves as if it were continuously
* connected.
*/
val fastReconnectLimit: Duration
)
Loading

0 comments on commit fff8aac

Please sign in to comment.