Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Firestore database id configuration #2164

Merged
merged 11 commits into from
Sep 19, 2023

Conversation

meltsufin
Copy link
Member

Fixes #2145.

@meltsufin meltsufin requested a review from a team as a code owner September 13, 2023 19:51
@suztomo
Copy link
Contributor

suztomo commented Sep 13, 2023

@diegomarquezp I found the error message your pull request should fix "Caused by: java.sql.SQLException: Access denied for user 'root'@'cloudsqlproxy~20.49.35.21' (using password: YES)"

https://github.com/GoogleCloudPlatform/spring-cloud-gcp/actions/runs/6177169510/job/16767718916?pr=2164

@meltsufin
Copy link
Member Author

Error:  Errors: 
Error:    FirestoreSampleApplicationIntegrationTests.saveUserTest » StatusRuntime INVALID_ARGUMENT: Missing routing header. Please fill in the request header with format x-goog-request-params:project_id=spring-cloud-gcp-ci&database_id=firestoredb. Please refer to https://firebase.google.com/docs/firestore/manage-databases#access_a_named_database_with_a_client_library for more details.

@blakeli0 Do I need a newer version of GAX or Firestore client lib?

@lqiu96
Copy link
Contributor

lqiu96 commented Sep 14, 2023

Iirc, Firestore temporarily removed code for gRPC's Dynamic Routing Headers a while back (due to an encoding issue). I think they only recently added that back in: googleapis/java-firestore#1418. I think this would work with Firestore v3.14.3+ (I have no idea how the tests work and it could be a different issue).

@blakeli0
Copy link
Contributor

As Lawrence mentioned, they recently added the routing headers back and Spring Cloud GCP didn't get it yet. But if this is the root cause, it means that the server made a behavior breaking change that requires the customers to update their client code, which is very bad.

.withCallCredentials(
MoreCallCredentials.from(
GcpFirestoreAutoConfiguration.this.credentialsProvider.getCredentials()));

// add routing header for custom database id
if (databaseId != null && !databaseId.equals("(default)")) {
Copy link

@tom-andersen tom-andersen Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Routing header should be passed in all requests. It just happens it is only enforced on non-default database ids.

The projectId and databaseId should be URL encoded. Basically, treat this string the same as you would treat query part of a URL.

Ask Sichen Liu if you need more details. There is a performance impact if you omit header for default database (I believe).

Metadata routingHeader = new Metadata();
Metadata.Key<String> key =
Metadata.Key.of(Headers.DYNAMIC_ROUTING_HEADER_KEY, Metadata.ASCII_STRING_MARSHALLER);
routingHeader.put(key,
Copy link
Contributor

@blakeli0 blakeli0 Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may work for this specific case, but routing header config could change in the future. I'm not sure it works for all the new routing header configs either, but maybe those are not required.
In long term, switching to GAPIC stubs should solve this issue, but I'm not sure it is compatible with the Reactive paradigm. Why did we choose to use the gRPC generated stubs instead of GAPIC ones? Only because of Reactive?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this integration was specifically made to support reactive, and I believe at the time GAPIC was not compatible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. My concern is that every time the backend makes the routing header required for a new RPC, or change how the routing header is constructed, we would have to come back and manually change it in spring-cloud-gcp. It may not in scope of this PR, can we investigate the possibility of switching to the GAPIC stubs?

@meltsufin meltsufin requested review from blakeli0 and lqiu96 and removed request for tom-andersen September 19, 2023 15:35
Metadata.Key<String> key =
Metadata.Key.of(Headers.DYNAMIC_ROUTING_HEADER_KEY, Metadata.ASCII_STRING_MARSHALLER);
routingHeader.put(key,
"project_id=" + URLEncoder.encode(projectId, StandardCharsets.US_ASCII)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use PercentEscaper in gax, which I think has slight advantage over the Java default one based on @lqiu96's investigation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switch to `PercentEscaper1.

Copy link

@tom-andersen tom-andersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sonarcloud
Copy link

sonarcloud bot commented Sep 19, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

100.0% 100.0% Coverage
0.0% 0.0% Duplication

@meltsufin meltsufin enabled auto-merge (squash) September 19, 2023 18:07
@@ -68,7 +68,7 @@ The following configuration options are available:
| Name | Description | Required | Default value
| `spring.cloud.gcp.datastore.enabled` | Enables the Cloud Datastore client | No | `true`
| `spring.cloud.gcp.datastore.project-id` | Google Cloud project ID where the Google Cloud Datastore API is hosted, if different from the one in the <<spring-cloud-gcp-core,Spring Framework on Google Cloud Core Module>> | No |
| `spring.cloud.gcp.datastore.database-id` | Google Cloud project can host multiple databases. You can specify which database will be used. | No |
| `spring.cloud.gcp.datastore.database-id` | Google Cloud project can host multiple databases. You can specify which database will be used. If not specified, the database id will be "(default)". | No |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Default value" column is missing.

@meltsufin meltsufin merged commit 1ea0cfb into main Sep 19, 2023
28 of 29 checks passed
@meltsufin meltsufin deleted the firestore-db-id branch September 19, 2023 18:08
meltsufin added a commit that referenced this pull request Sep 19, 2023
Also adds routing headers for all Firestore requests.

Fixes #2145.
meltsufin added a commit that referenced this pull request Sep 20, 2023
Also adds routing headers for all Firestore requests.

Backport of #2164.

Fixes #2145.
@thoastbrot
Copy link

Hi @meltsufin, this breaks my (test) configuration where I only define service credentials via spring.cloud.gcp.firestore.credentials.encoded-key - this contains the project ID, and somehow this worked till 4.7.2, but crashes here, as the percent escaper receives projectId null. Do you have an idea how this should work?

@meltsufin
Copy link
Member Author

@thoastbrot I guess it's because your project ID is null in the configuration. You can configure a dummy project ID for now as a workaround. Would you mind filing a separate issue?

@thoastbrot
Copy link

I'll do that. dummy project-id doesn't work the way you assume.

zhumin8 added a commit that referenced this pull request Nov 7, 2023
With feature in [#2164](#2164), now  Firestore can run in same project as datastore. This PR cleans up settings for integration tests ci so that tests for Firestore module and samples run in the same ci project as the rest.
Modules affected in this change:
- autoconfigure/firestore
- data-firestore
- data-firestore-sample
- starter-firestore-sample

Before this change, these modules are setup to run with `spring-cloud-gcp-ci-firestore` project.

After this change:
- database-id=firestoredb is specified for above module's tests.
- does not specify `firestore.project-id` and get project id info from `DefaultGcpProjectIdProvider`. For integration test ci, it is `spring-cloud-gcp-ci` set via gcloud [here](https://github.com/GoogleCloudPlatform/spring-cloud-gcp/blob/ef30225e60e270f8cc1a9213728f0cfdd7e4de50/.github/workflows/integrationTests.yaml#L51).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for specifying Firestore/Datastore DBs
7 participants