Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions firebase-dataconnect/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
to enable metrics collection in cloud monitoring.
([#8486](https://github.com/firebase/firebase-android-sdk/pull/8486),
[#8495](https://github.com/firebase/firebase-android-sdk/pull/8495))
- [changed] Add grpc request header for SQL Connect server affinity, to improve
server resource usage efficiency and performance.
([#8499](https://github.com/firebase/firebase-android-sdk/pull/8499))
- [changed] Wait for 15 seconds before closing realtime streaming connection
with backend after last subscriber unsubscribes (instead of closing the
connection immediately).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() {
googRequestParamsHeader.name(),
googApiClientHeader.name(),
clientVersionHeader.name(),
sqlConnectAffinityHeader.name(),
)
assertSoftly {
// Do not verify "x-firebase-auth-token" here since that header is effectively tested by
Expand All @@ -338,6 +339,8 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() {
metadata.get(googApiClientHeader) shouldBe expectedGoogApiClientHeader(isFromGeneratedSdk)
metadata.get(gmpAppIdHeader) shouldBe expectedAppId
metadata.get(clientVersionHeader) shouldBe "android/${BuildConfig.VERSION_NAME}"
metadata.get(sqlConnectAffinityHeader) shouldBe
"${dataConnect.app.options.projectId}${dataConnect.config.serviceId}"
}
}
}
Expand Down Expand Up @@ -479,6 +482,10 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() {
val clientVersionHeader: Metadata.Key<String> =
Metadata.Key.of("x-client-version", Metadata.ASCII_STRING_MARSHALLER)

@Suppress("SpellCheckingInspection")
val sqlConnectAffinityHeader: Metadata.Key<String> =
Metadata.Key.of("x-firebase-sqlconnect-affinity", Metadata.ASCII_STRING_MARSHALLER)

private val gmpAppIdHeader: Metadata.Key<String> =
Metadata.Key.of("x-firebase-gmpid", Metadata.ASCII_STRING_MARSHALLER)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,27 @@ import io.grpc.Metadata

internal class DataConnectGrpcMetadata(
val connectorLocation: String,
val connectorServiceId: String,
val kotlinVersion: String,
val androidVersion: Int,
val dataConnectSdkVersion: String,
val grpcVersion: String,
val appId: String,
val projectId: String,
val parentLogger: Logger,
) {
private val logger =
Logger("DataConnectGrpcMetadata").apply {
debug {
"created by ${parentLogger.nameWithId} with" +
" connectorLocation=$connectorLocation" +
" connectorServiceId=$connectorServiceId" +
" kotlinVersion=$kotlinVersion" +
" androidVersion=$androidVersion" +
" dataConnectSdkVersion=$dataConnectSdkVersion" +
" grpcVersion=$grpcVersion" +
" appId=$appId"
" appId=$appId" +
" projectId=$projectId"
}
}
val instanceId: String
Expand Down Expand Up @@ -84,6 +88,7 @@ internal class DataConnectGrpcMetadata(
it.put(googRequestParamsHeader, googRequestParamsHeaderValue)
it.put(googApiClientHeader, googApiClientHeaderValue(callerSdkType))
it.put(clientVersionHeader, "android/$dataConnectSdkVersion")
it.put(sqlConnectAffinityHeader, "$projectId$connectorServiceId")
if (appId.isNotBlank()) {
it.put(gmpAppIdHeader, appId)
}
Expand Down Expand Up @@ -190,18 +195,26 @@ internal class DataConnectGrpcMetadata(
private val gmpAppIdHeader: Metadata.Key<String> =
Metadata.Key.of("x-firebase-gmpid", Metadata.ASCII_STRING_MARSHALLER)

@Suppress("SpellCheckingInspection")
private val sqlConnectAffinityHeader: Metadata.Key<String> =
Metadata.Key.of("x-firebase-sqlconnect-affinity", Metadata.ASCII_STRING_MARSHALLER)

fun forSystemVersions(
firebaseApp: FirebaseApp,
projectId: String,
connectorLocation: String,
connectorServiceId: String,
parentLogger: Logger,
): DataConnectGrpcMetadata =
DataConnectGrpcMetadata(
connectorLocation = connectorLocation,
connectorServiceId = connectorServiceId,
kotlinVersion = "${KotlinVersion.CURRENT}",
androidVersion = Build.VERSION.SDK_INT,
dataConnectSdkVersion = BuildConfig.VERSION_NAME,
grpcVersion = "", // no way to get the grpc version at runtime,
appId = firebaseApp.options.applicationId,
projectId = projectId,
parentLogger = parentLogger,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ internal class FirebaseDataConnectImpl(
val grpcMetadata =
DataConnectGrpcMetadata.forSystemVersions(
firebaseApp = app,
projectId = projectId,
connectorLocation = config.location,
connectorServiceId = config.serviceId,
parentLogger = logger,
)
val dataConnectGrpcRPCs =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ class DataConnectGrpcMetadataUnitTest {
getExpectedHeaderValue = { "tvsxjeb745.appId" },
)

@Test
fun `should include x-firebase-sqlconnect-affinity`() =
testMetadataIncludesHeader(
dataConnectGrpcMetadataArb =
Arb.dataConnect.dataConnectGrpcMetadata(
projectId = Arb.constant("tvsxjeb745.projectId"),
connectorServiceId = Arb.constant("q8mgtztcz2"),
),
headerName = "x-firebase-sqlconnect-affinity",
getExpectedHeaderValue = { "tvsxjeb745.projectIdq8mgtztcz2" },
)

private fun testMetadataIncludesHeader(
dataConnectGrpcMetadataArb: Arb<DataConnectGrpcMetadata> =
Arb.dataConnect.dataConnectGrpcMetadata(),
Expand Down Expand Up @@ -359,16 +371,22 @@ class DataConnectGrpcMetadataUnitTest {
@Test
fun `forSystemVersions() should return correct values`() = runTest {
val connectorLocation = Arb.dataConnect.connectorLocation().next()
val connectorServiceId = Arb.dataConnect.connectorServiceId().next()
val projectId = Arb.dataConnect.projectId().next()

val dataConnectGrpcMetadata =
DataConnectGrpcMetadata.forSystemVersions(
firebaseApp = firebaseAppFactory.newInstance(),
projectId = projectId,
connectorLocation = connectorLocation,
connectorServiceId = connectorServiceId,
parentLogger = mockk(relaxed = true),
)

dataConnectGrpcMetadata.asClue {
it.connectorLocation shouldBeSameInstanceAs connectorLocation
it.connectorServiceId shouldBeSameInstanceAs connectorServiceId
it.projectId shouldBeSameInstanceAs projectId
it.kotlinVersion shouldBe "${KotlinVersion.CURRENT}"
it.androidVersion shouldBe Build.VERSION.SDK_INT
it.dataConnectSdkVersion shouldBe BuildConfig.VERSION_NAME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,33 @@ class DataConnectGrpcRPCsUnitTest {
getExpectedHeaderValue = { "android/${it.grpcMetadata.dataConnectSdkVersion}" },
)

@Test
fun `executeQuery sends x-firebase-sqlconnect-affinity header`() =
testExecuteQuerySendsHeader(
headerName = "x-firebase-sqlconnect-affinity",
getExpectedHeaderValue = {
"${it.grpcMetadata.projectId}${it.grpcMetadata.connectorServiceId}"
},
)

@Test
fun `executeMutation sends x-firebase-sqlconnect-affinity header`() =
testExecuteMutationSendsHeader(
headerName = "x-firebase-sqlconnect-affinity",
getExpectedHeaderValue = {
"${it.grpcMetadata.projectId}${it.grpcMetadata.connectorServiceId}"
},
)

@Test
fun `connect sends x-firebase-sqlconnect-affinity header`() =
testConnectSendsHeader(
headerName = "x-firebase-sqlconnect-affinity",
getExpectedHeaderValue = {
"${it.grpcMetadata.projectId}${it.grpcMetadata.connectorServiceId}"
},
)

private fun testExecuteQuerySendsHeader(
headerName: String,
getExpectedHeaderValue: (DataConnectGrpcRPCs) -> String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,23 @@ import kotlinx.serialization.modules.SerializersModule

internal fun DataConnectArb.dataConnectGrpcMetadata(
connectorLocation: Arb<String> = connectorLocation(),
connectorServiceId: Arb<String> = connectorServiceId(),
kotlinVersion: Arb<String> = Arb.string(size = 8, Codepoint.alphanumeric()),
androidVersion: Arb<Int> = Arb.int(0..100),
dataConnectSdkVersion: Arb<String> = Arb.string(size = 8, Codepoint.alphanumeric()),
grpcVersion: Arb<String> = Arb.string(size = 8, Codepoint.alphanumeric()),
appId: Arb<String> = Arb.string(size = 8, Codepoint.alphanumeric()),
projectId: Arb<String> = Arb.string(size = 8, Codepoint.alphanumeric()),
): Arb<DataConnectGrpcMetadata> = arbitrary {
DataConnectGrpcMetadata(
connectorLocation = connectorLocation.bind(),
connectorServiceId = connectorServiceId.bind(),
kotlinVersion = kotlinVersion.bind(),
androidVersion = androidVersion.bind(),
dataConnectSdkVersion = dataConnectSdkVersion.bind(),
grpcVersion = grpcVersion.bind(),
appId = appId.bind(),
projectId = projectId.bind(),
parentLogger = mockk(relaxed = true),
)
}
Expand Down
Loading