-
Notifications
You must be signed in to change notification settings - Fork 337
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
Add lock-free connection pool #462
Comments
Wow, @sebastienros and @bgrainger, I didn't know you were working on a lock-free pool... The more I work and think about the pooling issue, the more I'm convinced there needs to be a totally common component (https://github.com/dotnet/corefx/issues/26714) shared across providers (in fact, I'm thinking about a generic pooling API that wouldn't even be specific to ADO.NET/databases). I'm planning to spend some time on making my implementation a bit more generic to go in this direction, any reviewing/feedback would be really great (here's my pool). I think that if you guys are planning to work on the pool it may be a thing we should try to do together, and maybe even go towards a shared component. Anyway, @sebastienros I've taken a quick look, here are some comments:
|
That's correct.
My last attempt at improving this was to reset the connection in the background: #178. I think queueing the reset message is a better approach except...
... performing the reset connection while retrieving a connection from the pool has the very nice side-effect that the connection is actually open when
... unless of course there is a catastrophic network/server failure a nanosecond after In theory, I agree that users should expect that any database call can fail and that appropriate retry logic needs to be implemented. In practice, it seems like there's a lot of code that just assumes everything's going to be just fine if (I'm all for allowing users to opt into high-performance at the potential cost of correctness or reliability if they determine they absolutely need it. However, it seems best to be "safe" by default. And FWIW MySqlConnector is already faster than Connector/NET for retrieving a pooled connection even with |
It's more of a POC to see how it would benefit performance, but your comments are great to go towards a better final implementation. |
I'd love to be able to reuse a high-performance, well-tested, already-debugged component (as opposed to having to develop and support my own implementation). It would be nice to be able to reuse common code without introducing a new dependency. Are NuGet source-only packages still a thing? (I've not used them in the past, and I'm not sure if they made it into the new world or not.) Git submodules might be an answer? (They're not without their own problems, of course.) |
I agree that there's a bit of a tradeoff here, although it seems to be the SqlClient behavior:
A similar note can be found in the ODBC driver developer guidelines:
As I said above, in Npgsql this can be somewhat mitigated via keepalive, an opt-in feature which does a roundtrip on connections every X seconds (even when in the pool). The advantage of this is that it does not happen on the code path of Open() or Close() - it's a totally "out-of-band" operation that checks your connection without affecting performance. But you're right that some users may be surprised here, I'd at least recommend an opt-out from this for high-perf apps.
Having this as a source-only nuget is a great idea, I think they still exist (see this thread). In any case I'll look into making my own pool less coupled to PostgreSQL etc. |
Thanks for this pointer; it's helpful. |
@roji By the time you told me you were planning to work on abstracting a connection pool, I had already ported most of the code over to MySqlConnector (but I didn't mention it, since it was still WIP). I decided to clean it up today and start to run some performance tests: #614. They also say you shouldn't create an abstraction until you've implemented it 3 times (or something like that) so it might be helpful to see how I needed to tweak the implementation to meet my specific needs. (Hopefully not breaking it in the process.) |
@sebastienros I've tested these commits with TFB Docker locally, but I don't think I have sufficient system resources (particularly with the overhead of Docker for Windows) to see if the changes make a difference. (That is, I'm think already able to push my system to 100% usage and making the code faster doesn't result in any more requests per second.) If you're able, can you test the following commits and report back the speeds achieved? I'm interested to know if any individual commit results in a significant performance increase or regression:
|
/cc @divega @ajcvickers @bricelam It's great to see this, and I'm really looking forward to seeing the perf impact here. FWIW we don't have to do an actual reusable component with an abstraction - porting the code could be a very good start, and as you say having an implement on 2 providers is a much better place to start from if we do want a reusable component. My plate is quite full at the moment anyway, let's see where your efforts lead - I may also end up taking some of your improvements back into Npgsql at some point (e.g. the LIFO behavior). |
@sebastienros I've taken those six commits and applied them to (a fork of) FrameworkBenchmarks (using a submodule), if that makes testing easier:
|
I ran all these branches on our local physical machines, which are not the TE ones but the ratio in perf will be the same. Only the RPS and Average lantency numbers matter in this case
|
For future reference (note to myself) I am adding the command line I used to run these:
|
If you expected more variance please provide a PR that makes things really bad to ensure I ran these correctly. |
@sebastienros Here's a PR with |
It works, which means the previous results are valid
|
Is this still actively discussed and/or developed? I'm having a certain workload that benefits from higher throughput/rps even it would mean a slight payoff in latency. If this is still on-going I'm willing to do some testing to see if it could benefit such use-cases in real life. 👍 |
The code is at https://github.com/bgrainger/MySqlConnector/commits/lock-free-pool (except for the last commit on that branch). It wasn't clear to me from the results above that this actually improved performance, so given the added complexity I decided not to integrate it yet. If you have a scenario where you can build MySqlConnector from source and test it, I'm very interested to hear your results. Note that MySqlConnector logs over 200k RPS on the TFB Benchmarks (see |
Thanks for pointing this out. It is also not clear for me if the there are real benifits in the implementation. Just wanted to reach out to see if the conversation would benefit from some additional testing. I will take a look at building the library from source and hook it into my application. If I have any meaningfull information to share I will drop you a message over here.
That is impressive, and from looking at the benchmark, it is not clear MySqlConnector is used over here. Might be worth adding this somewhere on the website and/or readme on GitHub. |
TechEmpower server hardware is at https://www.techempower.com/benchmarks/#section=environment:
I believe they use one for the benchmarking client, one for the web framework being tested, and one for the database server. I have not found any information on how they're tuning MySQL for performance in this environment. |
Note that the lock-free code branched from the main library somewhere between 0.49.3 and 0.50.0; there shouldn't be any major performance changes between those two versions but obviously some of the newer features won't be present. It looks like it won't currently merge cleanly. |
See example from @sebastienros here: sebastienros@ffe984f
Also implement the optimisation to stagger probes into the pool from @roji: npgsql/npgsql@19c436e#diff-33bb36ea0d45f66ae316561892d5b5a9R236
The text was updated successfully, but these errors were encountered: