From 4dd5b81f28109a1ae5aaa87033ee5134cf488a3e Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 29 Jul 2026 15:16:02 -0400 Subject: [PATCH 1/5] DataConnectGrpcMetadata.kt: add "x-firebase-sqlconnect-affinity" header, but use appId/connectorLocation as the value for now for simplicity. A future commit will fix this to use projectId/connectorServiceId. --- .../GrpcMetadataIntegrationTest.kt | 7 +++++++ .../core/DataConnectGrpcMetadata.kt | 5 +++++ .../core/DataConnectGrpcMetadataUnitTest.kt | 12 +++++++++++ .../core/DataConnectGrpcRPCsUnitTest.kt | 21 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt index 5b2854db6f3..ccf26e9b812 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt @@ -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 @@ -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 + "$expectedAppId${dataConnect.config.location}" } } } @@ -479,6 +482,10 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() { val clientVersionHeader: Metadata.Key = Metadata.Key.of("x-client-version", Metadata.ASCII_STRING_MARSHALLER) + @Suppress("SpellCheckingInspection") + val sqlConnectAffinityHeader: Metadata.Key = + Metadata.Key.of("x-firebase-sqlconnect-affinity", Metadata.ASCII_STRING_MARSHALLER) + private val gmpAppIdHeader: Metadata.Key = Metadata.Key.of("x-firebase-gmpid", Metadata.ASCII_STRING_MARSHALLER) diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt index 7360a4f435b..54e16d61f2a 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt @@ -84,6 +84,7 @@ internal class DataConnectGrpcMetadata( it.put(googRequestParamsHeader, googRequestParamsHeaderValue) it.put(googApiClientHeader, googApiClientHeaderValue(callerSdkType)) it.put(clientVersionHeader, "android/$dataConnectSdkVersion") + it.put(sqlConnectAffinityHeader, "$appId$connectorLocation") if (appId.isNotBlank()) { it.put(gmpAppIdHeader, appId) } @@ -190,6 +191,10 @@ internal class DataConnectGrpcMetadata( private val gmpAppIdHeader: Metadata.Key = Metadata.Key.of("x-firebase-gmpid", Metadata.ASCII_STRING_MARSHALLER) + @Suppress("SpellCheckingInspection") + private val sqlConnectAffinityHeader: Metadata.Key = + Metadata.Key.of("x-firebase-sqlconnect-affinity", Metadata.ASCII_STRING_MARSHALLER) + fun forSystemVersions( firebaseApp: FirebaseApp, connectorLocation: String, diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt index 0e097d05fbf..2543d1fe784 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt @@ -154,6 +154,18 @@ class DataConnectGrpcMetadataUnitTest { getExpectedHeaderValue = { "tvsxjeb745.appId" }, ) + @Test + fun `should include x-firebase-sqlconnect-affinity`() = + testMetadataIncludesHeader( + dataConnectGrpcMetadataArb = + Arb.dataConnect.dataConnectGrpcMetadata( + appId = Arb.constant("tvsxjeb745.appId"), + connectorLocation = Arb.constant("q8mgtztcz2"), + ), + headerName = "x-firebase-sqlconnect-affinity", + getExpectedHeaderValue = { "tvsxjeb745.appIdq8mgtztcz2" }, + ) + private fun testMetadataIncludesHeader( dataConnectGrpcMetadataArb: Arb = Arb.dataConnect.dataConnectGrpcMetadata(), diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt index aa03817c231..19b6e4e9a95 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt @@ -850,6 +850,27 @@ 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.appId}${it.grpcMetadata.connectorLocation}" }, + ) + + @Test + fun `executeMutation sends x-firebase-sqlconnect-affinity header`() = + testExecuteMutationSendsHeader( + headerName = "x-firebase-sqlconnect-affinity", + getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorLocation}" }, + ) + + @Test + fun `connect sends x-firebase-sqlconnect-affinity header`() = + testConnectSendsHeader( + headerName = "x-firebase-sqlconnect-affinity", + getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorLocation}" }, + ) + private fun testExecuteQuerySendsHeader( headerName: String, getExpectedHeaderValue: (DataConnectGrpcRPCs) -> String, From 4b228704ef59eae13a931c67e61a7746e9a73491 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 31 Jul 2026 09:22:39 -0400 Subject: [PATCH 2/5] DataConnectGrpcMetadata.kt: fix: use serviceId instead of location in the "X-Firebase-SqlConnect-Affinity" header value --- .../firebase/dataconnect/GrpcMetadataIntegrationTest.kt | 2 +- .../firebase/dataconnect/core/DataConnectGrpcMetadata.kt | 6 +++++- .../firebase/dataconnect/core/FirebaseDataConnectImpl.kt | 1 + .../dataconnect/core/DataConnectGrpcMetadataUnitTest.kt | 5 ++++- .../dataconnect/core/DataConnectGrpcRPCsUnitTest.kt | 6 +++--- .../dataconnect/testutil/property/arbitrary/arbs.kt | 2 ++ 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt index ccf26e9b812..1c51d314ca0 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt @@ -340,7 +340,7 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() { metadata.get(gmpAppIdHeader) shouldBe expectedAppId metadata.get(clientVersionHeader) shouldBe "android/${BuildConfig.VERSION_NAME}" metadata.get(sqlConnectAffinityHeader) shouldBe - "$expectedAppId${dataConnect.config.location}" + "$expectedAppId${dataConnect.config.serviceId}" } } } diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt index 54e16d61f2a..a36fe4f435b 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt @@ -31,6 +31,7 @@ import io.grpc.Metadata internal class DataConnectGrpcMetadata( val connectorLocation: String, + val connectorServiceId: String, val kotlinVersion: String, val androidVersion: Int, val dataConnectSdkVersion: String, @@ -43,6 +44,7 @@ internal class DataConnectGrpcMetadata( debug { "created by ${parentLogger.nameWithId} with" + " connectorLocation=$connectorLocation" + + " connectorServiceId=$connectorServiceId" + " kotlinVersion=$kotlinVersion" + " androidVersion=$androidVersion" + " dataConnectSdkVersion=$dataConnectSdkVersion" + @@ -84,7 +86,7 @@ internal class DataConnectGrpcMetadata( it.put(googRequestParamsHeader, googRequestParamsHeaderValue) it.put(googApiClientHeader, googApiClientHeaderValue(callerSdkType)) it.put(clientVersionHeader, "android/$dataConnectSdkVersion") - it.put(sqlConnectAffinityHeader, "$appId$connectorLocation") + it.put(sqlConnectAffinityHeader, "$appId$connectorServiceId") if (appId.isNotBlank()) { it.put(gmpAppIdHeader, appId) } @@ -198,10 +200,12 @@ internal class DataConnectGrpcMetadata( fun forSystemVersions( firebaseApp: FirebaseApp, connectorLocation: String, + connectorServiceId: String, parentLogger: Logger, ): DataConnectGrpcMetadata = DataConnectGrpcMetadata( connectorLocation = connectorLocation, + connectorServiceId = connectorServiceId, kotlinVersion = "${KotlinVersion.CURRENT}", androidVersion = Build.VERSION.SDK_INT, dataConnectSdkVersion = BuildConfig.VERSION_NAME, diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt index d13d97ba35c..d660363be2d 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt @@ -300,6 +300,7 @@ internal class FirebaseDataConnectImpl( DataConnectGrpcMetadata.forSystemVersions( firebaseApp = app, connectorLocation = config.location, + connectorServiceId = config.serviceId, parentLogger = logger, ) val dataConnectGrpcRPCs = diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt index 2543d1fe784..11f2e2e0608 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt @@ -160,7 +160,7 @@ class DataConnectGrpcMetadataUnitTest { dataConnectGrpcMetadataArb = Arb.dataConnect.dataConnectGrpcMetadata( appId = Arb.constant("tvsxjeb745.appId"), - connectorLocation = Arb.constant("q8mgtztcz2"), + connectorServiceId = Arb.constant("q8mgtztcz2"), ), headerName = "x-firebase-sqlconnect-affinity", getExpectedHeaderValue = { "tvsxjeb745.appIdq8mgtztcz2" }, @@ -371,16 +371,19 @@ class DataConnectGrpcMetadataUnitTest { @Test fun `forSystemVersions() should return correct values`() = runTest { val connectorLocation = Arb.dataConnect.connectorLocation().next() + val connectorServiceId = Arb.dataConnect.connectorServiceId().next() val dataConnectGrpcMetadata = DataConnectGrpcMetadata.forSystemVersions( firebaseApp = firebaseAppFactory.newInstance(), connectorLocation = connectorLocation, + connectorServiceId = connectorServiceId, parentLogger = mockk(relaxed = true), ) dataConnectGrpcMetadata.asClue { it.connectorLocation shouldBeSameInstanceAs connectorLocation + it.connectorServiceId shouldBeSameInstanceAs connectorServiceId it.kotlinVersion shouldBe "${KotlinVersion.CURRENT}" it.androidVersion shouldBe Build.VERSION.SDK_INT it.dataConnectSdkVersion shouldBe BuildConfig.VERSION_NAME diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt index 19b6e4e9a95..742d8b19e0b 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt @@ -854,21 +854,21 @@ class DataConnectGrpcRPCsUnitTest { fun `executeQuery sends x-firebase-sqlconnect-affinity header`() = testExecuteQuerySendsHeader( headerName = "x-firebase-sqlconnect-affinity", - getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorLocation}" }, + getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorServiceId}" }, ) @Test fun `executeMutation sends x-firebase-sqlconnect-affinity header`() = testExecuteMutationSendsHeader( headerName = "x-firebase-sqlconnect-affinity", - getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorLocation}" }, + getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorServiceId}" }, ) @Test fun `connect sends x-firebase-sqlconnect-affinity header`() = testConnectSendsHeader( headerName = "x-firebase-sqlconnect-affinity", - getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorLocation}" }, + getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorServiceId}" }, ) private fun testExecuteQuerySendsHeader( diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt index e9f78da26bd..53500748d85 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt @@ -71,6 +71,7 @@ import kotlinx.serialization.modules.SerializersModule internal fun DataConnectArb.dataConnectGrpcMetadata( connectorLocation: Arb = connectorLocation(), + connectorServiceId: Arb = connectorServiceId(), kotlinVersion: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), androidVersion: Arb = Arb.int(0..100), dataConnectSdkVersion: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), @@ -79,6 +80,7 @@ internal fun DataConnectArb.dataConnectGrpcMetadata( ): Arb = arbitrary { DataConnectGrpcMetadata( connectorLocation = connectorLocation.bind(), + connectorServiceId = connectorServiceId.bind(), kotlinVersion = kotlinVersion.bind(), androidVersion = androidVersion.bind(), dataConnectSdkVersion = dataConnectSdkVersion.bind(), From d189109f43d2160ad2ea909fc461477985fe6791 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 31 Jul 2026 09:22:39 -0400 Subject: [PATCH 3/5] DataConnectGrpcMetadata.kt: fix: use projectId instead of appId in the "X-Firebase-SqlConnect-Affinity" header value --- .../dataconnect/GrpcMetadataIntegrationTest.kt | 2 +- .../dataconnect/core/DataConnectGrpcMetadata.kt | 8 ++++++-- .../dataconnect/core/FirebaseDataConnectImpl.kt | 1 + .../core/DataConnectGrpcMetadataUnitTest.kt | 7 +++++-- .../dataconnect/core/DataConnectGrpcRPCsUnitTest.kt | 12 +++++++++--- .../dataconnect/testutil/property/arbitrary/arbs.kt | 2 ++ 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt index 1c51d314ca0..eb82b6e574e 100644 --- a/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt +++ b/firebase-dataconnect/src/androidTest/kotlin/com/google/firebase/dataconnect/GrpcMetadataIntegrationTest.kt @@ -340,7 +340,7 @@ class GrpcMetadataIntegrationTest : DataConnectIntegrationTestBase() { metadata.get(gmpAppIdHeader) shouldBe expectedAppId metadata.get(clientVersionHeader) shouldBe "android/${BuildConfig.VERSION_NAME}" metadata.get(sqlConnectAffinityHeader) shouldBe - "$expectedAppId${dataConnect.config.serviceId}" + "${dataConnect.app.options.projectId}${dataConnect.config.serviceId}" } } } diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt index a36fe4f435b..452b533daa6 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadata.kt @@ -37,6 +37,7 @@ internal class DataConnectGrpcMetadata( val dataConnectSdkVersion: String, val grpcVersion: String, val appId: String, + val projectId: String, val parentLogger: Logger, ) { private val logger = @@ -49,7 +50,8 @@ internal class DataConnectGrpcMetadata( " androidVersion=$androidVersion" + " dataConnectSdkVersion=$dataConnectSdkVersion" + " grpcVersion=$grpcVersion" + - " appId=$appId" + " appId=$appId" + + " projectId=$projectId" } } val instanceId: String @@ -86,7 +88,7 @@ internal class DataConnectGrpcMetadata( it.put(googRequestParamsHeader, googRequestParamsHeaderValue) it.put(googApiClientHeader, googApiClientHeaderValue(callerSdkType)) it.put(clientVersionHeader, "android/$dataConnectSdkVersion") - it.put(sqlConnectAffinityHeader, "$appId$connectorServiceId") + it.put(sqlConnectAffinityHeader, "$projectId$connectorServiceId") if (appId.isNotBlank()) { it.put(gmpAppIdHeader, appId) } @@ -199,6 +201,7 @@ internal class DataConnectGrpcMetadata( fun forSystemVersions( firebaseApp: FirebaseApp, + projectId: String, connectorLocation: String, connectorServiceId: String, parentLogger: Logger, @@ -211,6 +214,7 @@ internal class DataConnectGrpcMetadata( dataConnectSdkVersion = BuildConfig.VERSION_NAME, grpcVersion = "", // no way to get the grpc version at runtime, appId = firebaseApp.options.applicationId, + projectId = projectId, parentLogger = parentLogger, ) } diff --git a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt index d660363be2d..bcda1282984 100644 --- a/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt +++ b/firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt @@ -299,6 +299,7 @@ internal class FirebaseDataConnectImpl( val grpcMetadata = DataConnectGrpcMetadata.forSystemVersions( firebaseApp = app, + projectId = projectId, connectorLocation = config.location, connectorServiceId = config.serviceId, parentLogger = logger, diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt index 11f2e2e0608..195cb379030 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt @@ -159,11 +159,11 @@ class DataConnectGrpcMetadataUnitTest { testMetadataIncludesHeader( dataConnectGrpcMetadataArb = Arb.dataConnect.dataConnectGrpcMetadata( - appId = Arb.constant("tvsxjeb745.appId"), + projectId = Arb.constant("tvsxjeb745.projectId"), connectorServiceId = Arb.constant("q8mgtztcz2"), ), headerName = "x-firebase-sqlconnect-affinity", - getExpectedHeaderValue = { "tvsxjeb745.appIdq8mgtztcz2" }, + getExpectedHeaderValue = { "tvsxjeb745.projectIdq8mgtztcz2" }, ) private fun testMetadataIncludesHeader( @@ -372,10 +372,12 @@ class DataConnectGrpcMetadataUnitTest { 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), @@ -384,6 +386,7 @@ class DataConnectGrpcMetadataUnitTest { 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 diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt index 742d8b19e0b..d3af81a364e 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcRPCsUnitTest.kt @@ -854,21 +854,27 @@ class DataConnectGrpcRPCsUnitTest { fun `executeQuery sends x-firebase-sqlconnect-affinity header`() = testExecuteQuerySendsHeader( headerName = "x-firebase-sqlconnect-affinity", - getExpectedHeaderValue = { "${it.grpcMetadata.appId}${it.grpcMetadata.connectorServiceId}" }, + 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.appId}${it.grpcMetadata.connectorServiceId}" }, + 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.appId}${it.grpcMetadata.connectorServiceId}" }, + getExpectedHeaderValue = { + "${it.grpcMetadata.projectId}${it.grpcMetadata.connectorServiceId}" + }, ) private fun testExecuteQuerySendsHeader( diff --git a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt index 53500748d85..73385909c26 100644 --- a/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt +++ b/firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt @@ -77,6 +77,7 @@ internal fun DataConnectArb.dataConnectGrpcMetadata( dataConnectSdkVersion: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), grpcVersion: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), appId: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), + projectId: Arb = Arb.string(size = 8, Codepoint.alphanumeric()), ): Arb = arbitrary { DataConnectGrpcMetadata( connectorLocation = connectorLocation.bind(), @@ -86,6 +87,7 @@ internal fun DataConnectArb.dataConnectGrpcMetadata( dataConnectSdkVersion = dataConnectSdkVersion.bind(), grpcVersion = grpcVersion.bind(), appId = appId.bind(), + projectId = projectId.bind(), parentLogger = mockk(relaxed = true), ) } From 301ecb9a436a2cf9c9fd70db31667a2f8deef9cc Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 31 Jul 2026 09:34:24 -0400 Subject: [PATCH 4/5] CHANGELOG.md: add entry [skip actions] --- firebase-dataconnect/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firebase-dataconnect/CHANGELOG.md b/firebase-dataconnect/CHANGELOG.md index 5695a672a32..0398b28c965 100644 --- a/firebase-dataconnect/CHANGELOG.md +++ b/firebase-dataconnect/CHANGELOG.md @@ -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. + ([#NNNN](https://github.com/firebase/firebase-android-sdk/pull/NNNN)) - [changed] Wait for 15 seconds before closing realtime streaming connection with backend after last subscriber unsubscribes (instead of closing the connection immediately). From 9ad3e4fdeffd18cb7de740d41099a819718475b9 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 31 Jul 2026 09:38:47 -0400 Subject: [PATCH 5/5] CHANGELOG.md: update PR number --- firebase-dataconnect/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-dataconnect/CHANGELOG.md b/firebase-dataconnect/CHANGELOG.md index 0398b28c965..6d203c6c1da 100644 --- a/firebase-dataconnect/CHANGELOG.md +++ b/firebase-dataconnect/CHANGELOG.md @@ -13,7 +13,7 @@ [#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. - ([#NNNN](https://github.com/firebase/firebase-android-sdk/pull/NNNN)) + ([#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).