-
Notifications
You must be signed in to change notification settings - Fork 803
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
Use MySqlDataSource in AddMySql
by default
#2096
Conversation
Users can opt in to setting a command that will be executed on the server, but the default is now a more efficient ping.
This pushes users towards good defaults: firstly registering a MySqlDataSource as a singleton in DI, then just calling .AddHealthChecks().AddMySql() with no other arguments to retrieve and use that same data source for health checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bgrainger thank you for your contribution! I believe it's the step in the right direction, it needs only some polishing before getting merged.
/// </summary> | ||
public string ConnectionString { get; set; } = null!; | ||
public MySqlDataSource? DataSource { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposing a DataSource
can lead to issues similar to the ones I've fixed in #2045 for PostgreSQL. IMO we should make it internal.
public MySqlDataSource? DataSource { get; set; } | |
internal MySqlDataSource? DataSource { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you worried that this could lead to API misuse because someone could create a new MySqlDataSource
just to set this property?
I'm more worried that without this property, someone would be forced to set the ConnectionString
property, which would be a worse outcome. Making this internal
would also make it impossible for an external client to initialize this object properly (i.e., with a data source), making the AddMySql
overload that takes it a "pit of failure".
I'm happy to make this internal
if we also make AddMySql(this IHealthChecksBuilder builder, MySqlHealthCheckOptions options, ...)
[Obsolete]
because it doesn't allow one to set to set the data source. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After thinking about it for a while and experimenting with Npgsql in #2116 I got to the following conclusions:
Let's make the parameterless MySqlHealthCheckOptions
ctor internal and add two pubic ctors: one that accepts ConnectionString
and one that accepts DataSource
.
Moreover, let's make the setters for these properties internal.
This allows us to ensure that every instance of this type is valid: it has either the connection string or the data source. Never both.
This is the only change I would like to make before merging this PR.
@bgrainger thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me; I'll update this PR with that pattern soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make the parameterless MySqlHealthCheckOptions ctor internal ... Moreover, let's make the setters for these properties internal.
When developing this, I found that I didn't need a parameterless constructor nor to set the properties after construction. Thus, the MySqlHealthCheckOptions
type has been updated to have just two public constructors and private setters for all properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When developing this, I found that I didn't need a parameterless constructor nor to set the properties after construction. Thus, the MySqlHealthCheckOptions type has been updated to have just two public constructors and private setters for all properties.
Great! 👍
Conflicts: src/HealthChecks.MySql/HealthChecks.MySql.csproj
@bgrainger I've created #2113, PTAL and let me know what do you think |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's almost ready, PTAL at my comments. Thank you!
/// </summary> | ||
public string ConnectionString { get; set; } = null!; | ||
public MySqlDataSource? DataSource { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After thinking about it for a while and experimenting with Npgsql in #2116 I got to the following conclusions:
Let's make the parameterless MySqlHealthCheckOptions
ctor internal and add two pubic ctors: one that accepts ConnectionString
and one that accepts DataSource
.
Moreover, let's make the setters for these properties internal.
This allows us to ensure that every instance of this type is valid: it has either the connection string or the data source. Never both.
This is the only change I would like to make before merging this PR.
@bgrainger thoughts?
There are two constructors: one to initialise with a MySqlDataSource, and one with a connection string. This helps enforce correct uasge of the API by ensuring that exactly one is set.
Conflicts: src/HealthChecks.MySql/HealthChecks.MySql.csproj
Codecov ReportAttention:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #2096 +/- ##
===========================================
+ Coverage 0.00% 64.23% +64.23%
===========================================
Files 7 248 +241
Lines 356 8396 +8040
Branches 28 592 +564
===========================================
+ Hits 0 5393 +5393
- Misses 356 2852 +2496
- Partials 0 151 +151
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you very much @bgrainger !
Guard.ThrowIfNull(options); | ||
if (options.DataSource is null && options.ConnectionString is null) | ||
throw new InvalidOperationException("One of options.DataSource or options.ConnectionString must be specified."); | ||
if (options.DataSource is not null && options.ConnectionString is not null) | ||
throw new InvalidOperationException("Only one of options.DataSource or options.ConnectionString must be specified."); | ||
_options = options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need these checks anymore, as the public ctors ensure that the state is always valid.
Guard.ThrowIfNull(options); | |
if (options.DataSource is null && options.ConnectionString is null) | |
throw new InvalidOperationException("One of options.DataSource or options.ConnectionString must be specified."); | |
if (options.DataSource is not null && options.ConnectionString is not null) | |
throw new InvalidOperationException("Only one of options.DataSource or options.ConnectionString must be specified."); | |
_options = options; | |
_options = Guard.ThrowIfNull(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason I can't apply this suggestion. I am going to merge it now since it's not blocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RabbitMQHealthCheck is maintaining it's private static cache of instances. It's wrong, as it can cause a memory leak (it won't ever be freed). Moreover, it's can create instances of IConnection. This is wrong, as it can lead into a situation when we have multiple instances of IConnection that are connected to the same server: one used by the app and another created and used by the health check. And we should have only one. We should do the same as in Xabaril#2040, Xabaril#2116 and Xabaril#2096 I left RabbitMQ.v6 as-is to limit the breaking change. Only when an existing user moves from RabbitMQ.Client v6 to v7 will they need to update their health check (along with other breaking changes in RabbitMQ.Client).
* Remove private static cache from RabbitMQHealthCheck RabbitMQHealthCheck is maintaining it's private static cache of instances. It's wrong, as it can cause a memory leak (it won't ever be freed). Moreover, it's can create instances of IConnection. This is wrong, as it can lead into a situation when we have multiple instances of IConnection that are connected to the same server: one used by the app and another created and used by the health check. And we should have only one. We should do the same as in #2040, #2116 and #2096 I left RabbitMQ.v6 as-is to limit the breaking change. Only when an existing user moves from RabbitMQ.Client v6 to v7 will they need to update their health check (along with other breaking changes in RabbitMQ.Client). * Add test when no IConnection is registered in DI.
What this PR does / why we need it:
This pushes users towards best practices (similar to what @adamsitnik described in #2040). The user should register a
MySqlDataSource
with DI, then call.AddHealthChecks().AddMySql()
to use that sameMySqlDataSource
by default.This PR also builds on top of #2093. They could be separated if needed, but there would be a merge conflict if they were opened separately, and both changes are useful.
Which issue(s) this PR fixes:
Special notes for your reviewer:
Does this PR introduce a user-facing change?:
Yes, the
.AddMySql
method now has an overload with all default parameters that "does the right thing".Please make sure you've completed the relevant tasks for this PR, out of the following list:
I didn't see a place to update documentation or samples for MySql, but I'd be happy to do this to show how to add this health check properly.