-
Notifications
You must be signed in to change notification settings - Fork 294
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
Support parallel Physical Connection creation for multi-threaded scenarios #408
Comments
How are you spinning up the threads? |
@benaadams , I've tried this two ways: with and without threads (without threads, I just set "Min Pool Size=50" in the connection string then open a single connection, which triggers some background process to start opening connections. But one of the iterations with threads is:
|
I should also add that I'm running the following SQL to monitor the connections, and I see them getting created at a rate of 1 per 2 seconds using both methods (multithreading or a single thread with Min Pool Size set).
|
In the multi-threading scenario, if I look at the thread list once all 50 threads start, most of them (48ish) are waiting with the following stack track:
|
If I use
|
I think unless you are specifying You might want to open this issue in https://github.com/dotnet/runtime (or have it transferred) |
I have MaxPoolSize=500 just in case that was a factor, but it wasn't. |
I should also note that if I turn connection pooling off, these 50 connections open almost all at the same time then close again almost all at the same time. So the client/server itself isn't a factor. In other words, opening 50 connection simultaneously posed no issue until pooling is enabled. |
We can transfer this issue to dotnet/sqlclient and investigate more. |
I'm looking into the issue, could you also test and confirm if this issue is reproducible with previous System.Data.SqlClient versions or it's only happening recently; to rule out the factor of being a regression? |
When you say you've tried it with and without threads do you mean that you manually created 100 threads and used them to connect? or that you used the threadpool? With direct threadpool or tasks (indirectly threadpool assuming |
What I mean by "without threads" is that if I simply set "Min Pool Size=50", then do this:
Then while the And what I mean by "Without threading", is that I remove "Min Pool Size" from the connection string then try to open up 50 connection manually at the same time. In answer to your question, with threads, it should not be using the threadpool because I have the LongRunning option set. But just in case, I just tried this with the same result:
|
Hi @joshmouch I tried using your repro in the comment above but could not reproduce the problem. I ran the test on Windows with both Native and Managed SNI (for Unix). Could you provide me below info:
Please provide specific details as necessary to reproduce. |
@cheenamalhotra , |
This is another case where a user is asking to create n (where n>30) connections instantly. Unless those are n connections to n different database servers it seems like something of an antipattern. What can a single process be doing where it's capable of feeding 50 queries at once? |
@Wraith2 50 is an arbitrary number. I'm actually looking for around 10. |
Attached my repro, I ran this in debug mode where we can toggle Managed SNI, and the logs didn't suggest any such behavior and all connections connect instantly. Unless this is specific to Unix (I'm going to try today), Windows doesn't reproduce this error with both Native/Managed SNI. |
The unix implementation could hit the same problem I identified in #422 if even a single threadpool thread is unavailable at the time the connections are initiated one or more may block and fail. |
@cheenamalhotra , I had to update a few things: |
Strange to see you are getting different results, I also tested with direct reference of M.D.S 1.1.1 and without environment variable on Windows. It uses Native SNI (SNI.dll), which should be working anyhow. Did you also do any modifications in test in the way you connect? |
@cheenamalhotra , I only modified the connection string to point to my development SQL instance. |
I think your server is probably slow to connect here. Anyways, when connecting to Azure DB I do see the behavior, but that's because connections are not getting closed. I'll try to explain how does it work:
This behavior is as expected, as I said, there is only 1 TDS Stream with SQL Server that driver communicates on. Opening 50 "physical" connections is going to be sequential. |
@cheenamalhotra , You said you were getting different results. So you were actually seeing the same behavior on the local SQL server in your test? And just to clarify: You're saying that the driver is not capable of opening more than one physical connection at a time? So if you have "Min Pool Size=100" in connection string, it'll take 200 seconds until the connections become available? And if you try to access them before that 200 seconds, one should expect a "Connection Timeout" exception? |
Yes, I think it was same, it didn't surface in my case as connection time was in milliseconds, and all 50 connections could be made within 1 second.
Yes this is true. It will take 100 * (time to make 1 physical connection) to open 100 physical connections. This is current design of driver. I've attached log with milliseconds captured, and you can see it takes diff 1-2 milliseconds to create a physical connection to local SQL Server 2019, and diff 100-150 milliseconds to connect to Azure DB. But 2 seconds (2000 ms) is way too slow to connect so you must investigate that case. And if you are opening 50 slow connections back to back, it will take 100 seconds. |
Also to add: This is not linux specific, and I've also tested this with System.Data.Odbc and System.Data.Oledb namesapces and behaviors are same. cc @saurabh500 |
Can you also give me output of |
@cheenamalhotra , I also tried a different server, and it no longer took 2 seconds per connection! |
No problem @joshmouch After further investigation with different cases and comparing behaviors will other SQL Client drivers I don't see this as a limitation from protocol. Some delay is possible in later connections due to Server limitation of resources, but the driver should be able to initiate connections in parallel. I'll keep this issue open to investigate better ways of opening connections considering multi-threading scenarios like this at our end. This is only applicable to Azure connections using federated authentication modes with MSAL.NET. Also in doubt is the MSAL.NET library which no other client drivers use currently. |
Hi @joshmouch The PR #466 fixes this issue for Azure Connections, please give the NuGet package attached under a try (contains PR changes) : Microsoft.Data.SqlClient.2.0.0-dev.nupkg.zip
We will consider shipping this change in next release. |
@cheenamalhotra is the PR #466 fix already in Nuget package 2.0.0-preview4.20142.4 from 22 May 2020? I am still encountering sequential instead of parallel connections to Azure SQL while using this package similar to what you show in log.txt. Please let me know so I know if I should open a new issue with a clean repro. |
@OQO |
@cheenamalhotra @OQO FWIW this serial opening of concurrent connections for the same pool, even in case of multithreaded scenario is intentional and by design of the Connection Pool. From the past owners of SqlClient I understand that the idea was to not overwhelm a SQL server because of a flood of connections from the single Connection Pool which needs many connections as well as many clients trying to connect to a single SQL server when failover is in progress. As of whether the behavior should be changed or not, it may be OK to relax the requirement for Azure SQL DBs, but it may be good to keep this behavior for on-prem SQL servers. |
@cheenamalhotra Thank you. After I asked, I guessed the same so I already did. Please see #601 |
I'm trying to spin up 50 threads to immediately open 50
SqlConnection
s to a Microsoft SQL Server connection pool. However, no matter what I try, theSqlConnection
s seem to only open one at a time. In other words, if each connection takes 2 seconds to open, then the 50'th thread'sOpen
method will take 100 seconds to complete. Further to this point, if the Connection Timeout isn't set to > 100 seconds then the connection attempts will eventually start timing out. With the default Connection Timeout = 30, the 16'th thread'sOpen
times out.To put this another way, it seems like the 1st
SqlConnection.Open
takes 2 seconds, the 2ndSqlConnection.Open
takes 4 seconds, the 3rdSqlConnection.Open
takes 6 seconds... the 16thSqlConnection.Open
takes 32 seconds and causes a timeout exception.I also tried setting Min Pool Size=50 and just opening a single SqlConnection, and that has the same behavior. There seems to be a background process that starts opening connections until it gets to 50, but the 16th one times out because it's over the 30 second connection timeout.
It's like there's something blocking internally to only allow this one-at-a-time addition to the connection pool.
I've asked around on stack overflow, and the responses seem to suggest that if you open 50 connections in parallel, they should all open.
So this makes me wonder if this is a .Net Core bug or "feature".
The text was updated successfully, but these errors were encountered: