Skip to content

Support datasource password change in runtime #1599

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

Merged
merged 23 commits into from
Jun 13, 2025
Merged

Conversation

radovanradic
Copy link
Contributor

This PR adds support for changing datasource password without need to restart the app. It might address some reported issues like this one.
The idea is that users publish event with datasource name and changed password so listeners would be able to update datasource passwords. Current connections created with old password are validated (tested with hikari and ucp) and only when connection expires and being recreated then new password will be used.
@sdelamo Suggested using bean refresh, but don't know how it would work - tried refreshing DataSourceConfiguration but that didn't have effect since DataSource beans are already created and injected with the old password and refreshing DataSourceConfiguration won't rebuild existing datasources. Or maybe I was not aware how it could be done.

@@ -22,5 +22,6 @@
@MicronautTest(transactional = false)
@Property(name = "jpa.default.properties.hibernate.connection.db-type", value = "mysql")
@Property(name = "jpa.default.reactive", value = "true")
@Property(name = "test-resources.containers.mysql.image-name", value = "mysql:8.4.5")
Copy link
Contributor Author

@radovanradic radovanradic May 8, 2025

Choose a reason for hiding this comment

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

Unrelated to this PR, mysql:latest docker image was failing to start in tests so needed to update it to 8.x

}
return dbcpDataSourcePoolMetadata;
}

@Override
public void onApplicationEvent(DataSourcePasswordChangedEvent event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think instead of a new event you should extend RefreshEventListener and override getObservedConfigurationPrefixes() to get the data which will make it work in other contexts like the refresh endpoint and periodic monitoring.

For UCP you should call:

public void reconfigureDataSource(Properties configuration) throws SQLException {

For Hikari you should call:

hikariDataSource.getHikariConfigMXBean().setPassword("newPassword");
hikariDataSource.getHikariPoolMXBean().softEvictConnections();

For DBCP:

// set the properties
// and then call
pool.evict();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't sure if I am able to populate all Properties for UCP or can only set password to properties?
Btw, I think this is not needed, since existing connections will have old password and it should work until connections are evicted by the pool and then regenerated with the new password.

As for the event, I thought this will be more convenient because users might have their own logic/events for password change and this might be simpler and more straightforward.

Copy link
Contributor

Choose a reason for hiding this comment

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

it isn't about being easier it is because this change won't work from the refresh management endpoint or wither periodic config refresh mechanisms

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I guess will wait this PR micronaut-projects/micronaut-core#11798 to be merged and then for which drivers needed listen to refresh event and I suppose datasources.*.password and extract datasource name and update password if needed.

Copy link
Contributor Author

@radovanradic radovanradic Jun 12, 2025

Choose a reason for hiding this comment

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

Changed logic in refresh event listener to get new value like
String password = applicationContext.getRequiredProperty(property, String.class);
where property will be datasources.{datasourcename}.password

@dstepanov
Copy link
Contributor

dstepanov commented May 9, 2025

Can we test this with two datasource and validate that rerouting works?
Nevermind, I see we only support password change

@radovanradic
Copy link
Contributor Author

radovanradic commented May 9, 2025

Can we test this with two datasource and validate that rerouting works? Nevermind, I see we only support password change

I have updates tests for DBCP, Hikari and Tomcat by altering default user in the test and then executing query. Something is not working as expected for UCP, investigating now.

@radovanradic radovanradic added the type: improvement A minor improvement to an existing feature label May 13, 2025
@radovanradic
Copy link
Contributor Author

Need to wait this PR micronaut-projects/micronaut-core#11798 and micronaut-core version with it be propagated before can merge this

@radovanradic radovanradic changed the title Support datasource password change in runtime [Not ready to merge yet] Support datasource password change in runtime May 14, 2025
@graemerocher graemerocher moved this to Ready for Review in 4.9.0 Release Jun 11, 2025
@graemerocher
Copy link
Contributor

Is this ready now?

@radovanradic
Copy link
Contributor Author

Is this ready now?

UCP is behaving differently after upgrade to core 4.8.17 so I made some changes. However, currently native UCP tests failing and I am investigating how to fix it.

@radovanradic radovanradic changed the title [Not ready to merge yet] Support datasource password change in runtime Support datasource password change in runtime Jun 11, 2025
@@ -51,7 +52,7 @@
public class DatasourceConfiguration implements BasicJdbcConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(DatasourceConfiguration.class);

@ConfigurationBuilder(allowZeroArgs = true, excludes = {"connectionFactoryProperties"})
@ConfigurationBuilder(allowZeroArgs = true, excludes = {"connectionFactoryProperties", "URL", "username", "password"})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@graemerocher This is what needed to change after upgrade to core 4.8.17 (containing fix for config refresh). So, when bean.inject is called from RefreshScope since this is ConfigurationBuilder it would call setter for all PoolDataSourceImpl bean and one of these are setURL, setUsername, setPassword. But, since we have these already in DatasourceConfiguration and are calling this delegate methods I think we don't need these.
Btw, when changed password in the database and refresh event is fired, calling delegate.setURL would stop and start connection pool but password has not been propagated yet. So, excluding these resolves the issue.
And in DatasourceConfiguration.setUrl() and other methods has now check to not call delegate if property has not changed since that would also trigger connection pool stop/start.

@radovanradic radovanradic marked this pull request as draft June 12, 2025 08:20
@radovanradic radovanradic marked this pull request as ready for review June 12, 2025 10:12
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
52.5% Coverage on New Code (required ≥ 70%)

See analysis details on SonarQube Cloud

@radovanradic radovanradic merged commit e202177 into 6.2.x Jun 13, 2025
45 of 48 checks passed
@radovanradic radovanradic deleted the password-change branch June 13, 2025 10:39
@github-project-automation github-project-automation bot moved this from Ready for Review to Done in 4.9.0 Release Jun 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: improvement A minor improvement to an existing feature
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants