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

Introduce EnvironmentPostProcessor for R2DBC support in Cloud Sql #772

Merged
merged 27 commits into from
Jan 24, 2022

Conversation

mpeddada1
Copy link
Contributor

@mpeddada1 mpeddada1 commented Dec 7, 2021

Part 1 of #205
This PR adds two starters for R2DBC support in Cloud SQL (one for mySQL and another for postgreSQL). It also includes 2 samples, associated integrations tests and documentation. The starters bring in spring-boot-starter-data-r2dbc which takes care of the creation of the ConnectionFactory bean.

Currently, authentication can only be done through the use of Application Default Credentials. The feature to pass credentials through spring.cloud.gcp.credentials or spring.cloud.gcp.sql.credentials will be in the next PR(#775).

Copy link
Member

@meltsufin meltsufin left a comment

Choose a reason for hiding this comment

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

I mostly commented just on the MySQL side as the comments equally apply to Postgres.

*
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = { SqlApplication.class }, properties = {
"spring.r2dbc.url=r2dbc:gcp:mysql://spring-cloud-gcp-ci:us-central1:testmysql/code_samples_r2dbc_db",
Copy link
Member

Choose a reason for hiding this comment

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

Can we put this configuration in under the spring-cloud-gcp-ci-it profile like we did for the non-R2DBC sample?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The integration tests use different properties based on the database they're talking to. This might make it harder for them to be under the same profile? The non-R2DBC samples seem to use a hybrid approach where the mySQL test uses the profile while the postgreSQL test configures properties in the test. However, moving all the setup to the test classes could potentially help with readability. I could also be missing some maven-related knowledge here so let me know what you think.

@mpeddada1 mpeddada1 changed the title Create starter and samples for R2dbc support in Cloud Sql Introduce EnvironmentPostProcessor for R2DBC support in Cloud Sql Dec 13, 2021
@@ -52,8 +53,7 @@
* @author Eddú Meléndez
*/
public class CloudSqlEnvironmentPostProcessor implements EnvironmentPostProcessor {
Copy link
Member

Choose a reason for hiding this comment

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

Seeing now how handling both JDBC and R2DBC in one class here is not really reusing that much code, and making it less readable, maybe a separate EventPostProcessor is a better design after all. You can still re-use some code bt introducing a common AbstractCloudSqlEnvironmentPostProcessor that both would extend.

Copy link
Contributor

@elefeint elefeint left a comment

Choose a reason for hiding this comment

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

I ran into a self-created issue (provided wrong format for instance), but as part of troubleshooting noticed that Spring Data R2DBC configuration has a concept of ConnectionFactoryOptionsBuilderCustomizer. This may be a more natural way to prepare options for the socket factory than the more generic environment post-processor. Could you try to replace R2dbcCloudSqlEnvironmentPostProcessor with a new autoconfiguration class? It will need to be listed in spring.factories under EnableAutoConfiguration.

https://docs.spring.io/spring-boot/docs/2.5.7/reference/html/features.html#features.sql.r2dbc

spring-cloud-gcp-autoconfigure/pom.xml Outdated Show resolved Hide resolved
}

DatabaseType getEnabledDatabaseType(ConfigurableEnvironment environment) {
if (Boolean.parseBoolean(environment.getProperty("spring.cloud.gcp.sql.enabled", "true"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Make sure that R2dbc post-processing is not triggered if the user just wants plain old JDBC>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. This method now returns null if the dependency that is used to enable Spring R2DBC auto-configuration is not present.

Copy link
Member

Choose a reason for hiding this comment

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

What if the user has the R2DBC dependency, but doesn't want our auto-configuration to kick in? I think we might need something like spring.cloud.gcp.sql.r2dbc.enabled.

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be instead of spring.cloud.gcp.sql.enabled for Cloud SQL R2DBC autoconfig being on, not in addition to, right?
I guess theoretically it's possible that an app wants spring.cloud.gcp.sql.enabled for blocking connection to Cloud SQL, and then an R2DBC driver to another type of database. It would be weird for a reactive app to have blocking components, but not impossible.

Copy link
Member

Choose a reason for hiding this comment

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

Ideally, spring.cloud.gcp.sql.enabled=false would disable all of our Cloud SQL starters.
Maybe we need to add two properties: spring.cloud.gcp.sql.r2dbc.enabled and spring.cloud.gcp.sql.jdbc.enabled.

Copy link
Contributor

Choose a reason for hiding this comment

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

So for this PR, the check would be that both spring.cloud.gcp.sql.enabled and spring.cloud.gcp.sql.r2dbc.enabled are true. That sounds good.

Copy link
Member

Choose a reason for hiding this comment

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

Yep. Also, consider introducing spring.cloud.gcp.sql.jdbc.enabled.
Eventually, we might want to deprecate spring.cloud.gcp.sql.enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is something that I did not think of before. Since it is possible for an app to expect a blocking connection while having the r2dbc driver on the classpath, introducing spring.cloud.gcp.sql.r2dbc.enabled sounds like a good idea. I'll add spring.cloud.gcp.sql.jdbc.enabled in a separate PR since it might break existing behavior for current cloud sql customers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@meltsufin @elefeint Added spring.cloud.gcp.sql.r2dbc.enabled which will be true by default.

@mpeddada1
Copy link
Contributor Author

I ran into a self-created issue (provided wrong format for instance), but as part of troubleshooting noticed that Spring Data R2DBC configuration has a concept of ConnectionFactoryOptionsBuilderCustomizer. This may be a more natural way to prepare options for the socket factory than the more generic environment post-processor. Could you try to replace R2dbcCloudSqlEnvironmentPostProcessor with a new autoconfiguration class? It will need to be listed in spring.factories under EnableAutoConfiguration.

https://docs.spring.io/spring-boot/docs/2.5.7/reference/html/features.html#features.sql.r2dbc

That is a great discovery! I agree with you that ConnectionFactoryOptionsBuilderCustomizer is the most natural way to customize the ConnectionFactory. However, it may come down to the kind of user experience we want to provide. The r2dbc url is expected if we go with this particular workflow. If we don't provide a url then we run into the following exception at the connection factory bean creation step:
Error creating bean with name 'connectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations$Pool.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.r2dbc.pool.ConnectionPool]: Factory method 'connectionFactory' threw exception; nested exception is org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsInitializer$ConnectionFactoryBeanCreationException: Failed to determine a suitable R2DBC Connection URL

If we want to only require the database name and the instance connection name to be provided without the need for the url (similar to what we have for the jdbc workflow) then the environment post processor will still be needed. Let me know what you think?

@elefeint
Copy link
Contributor

@mpeddada1 That makes sense, thank you for checking!

Copy link
Contributor

@elefeint elefeint left a comment

Choose a reason for hiding this comment

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

Looks good, only a couple of minor changes.

private static final Log LOGGER = LogFactory.getLog(R2dbcCloudSqlEnvironmentPostProcessor.class);

@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Copy link
Member

Choose a reason for hiding this comment

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

It's a minor thing, but I think there is even more code here that can be shared by adding some extra parameters to a helper method. It could make sense, unless it makes the code less readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm that's a good point. One potential area I can think of refactoring is CloudJdbcInfoProvider and DefaultCloudSqlJdbcProvider. We used this interface in the past because we also had AppEngineCloudSqlJdbcInfoProvider but we may not need this interface anymore -- this can help reduce added hierarchy in the code. Additionally some of the code that is used to create a url can be shared between the r2dbc and jdbc workflow. I can do this refactoring in a follow-up PR if you think this is a good idea.

Copy link
Member

@meltsufin meltsufin left a comment

Choose a reason for hiding this comment

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

LGTM, but note one comment on spring.cloud.gcp.sql.r2dbc.enabled.
@elefeint WDYT?

}

DatabaseType getEnabledDatabaseType(ConfigurableEnvironment environment) {
if (Boolean.parseBoolean(environment.getProperty("spring.cloud.gcp.sql.enabled", "true"))
Copy link
Member

Choose a reason for hiding this comment

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

What if the user has the R2DBC dependency, but doesn't want our auto-configuration to kick in? I think we might need something like spring.cloud.gcp.sql.r2dbc.enabled.

@sonarcloud
Copy link

sonarcloud bot commented Jan 21, 2022

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

94.6% 94.6% Coverage
0.0% 0.0% Duplication

Copy link
Contributor

@elefeint elefeint left a comment

Choose a reason for hiding this comment

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

Looks great.

Could you add a paragraph to refdoc in cloud-sql.adoc? Can be a followup PR.

@mpeddada1
Copy link
Contributor Author

Thank you! Yup, I'll do this in a follow-up PR.

@mpeddada1 mpeddada1 merged commit 7b69799 into main Jan 24, 2022
@mpeddada1 mpeddada1 deleted the autoconfigure-r2dbc branch January 24, 2022 16:30
elefeint pushed a commit that referenced this pull request Jan 28, 2022
This PR adds two starters for R2DBC support in Cloud SQL (one for mySQL and another for postgreSQL). It also includes 2 samples, associated integrations tests and documentation. The starters bring in spring-boot-starter-data-r2dbc which takes care of the creation of the ConnectionFactory bean.
kateryna216 added a commit to kateryna216/spring-cloud-gcp that referenced this pull request Oct 20, 2022
…ogleCloudPlatform#772)

This PR adds two starters for R2DBC support in Cloud SQL (one for mySQL and another for postgreSQL). It also includes 2 samples, associated integrations tests and documentation. The starters bring in spring-boot-starter-data-r2dbc which takes care of the creation of the ConnectionFactory bean.
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.

None yet

3 participants