Skip to main content
35 events
when toggle format what by license comment
Sep 11, 2023 at 9:10 answer added Alex from Jitbit timeline score: 0
Feb 22, 2023 at 17:48 comment added Triynko Bad info here. Even in .NET 4.7.2, the underlying OpenAsync implementation is synchronous and thread blocking; I've looked at the decompiled code. This can be demonstrated by attempting to open 100+ simultaneous connections and watching them unable to get a pool thread to complete on, because it's blocking the ones it has. Setting MinThreadCount to something like 200 solves the problem and allows 100+ connections to be established instantly, whereas without setting MInThreadCount, they all start to fail with timeouts. For example: stackoverflow.com/a/57337499/88409
Jan 24, 2022 at 6:04 history bumped CommunityBot This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
Dec 23, 2021 at 11:33 answer added developer65 timeline score: 4
Nov 17, 2016 at 23:06 comment added ccook @usr thanks again. So I set the TP min and max to 1000 and tested to see where the timeouts occurred. With Open() it's at the max parallelism of 100 where, with OpenAsync() its over 200. I suspect that's due to nuances of tasks being handled? As far as performance, i think we are just seeing the improvement in using async on the connection so that it's no longer blocking.
Nov 17, 2016 at 22:38 comment added usr Parallel is dumb, it does not ramp up. The TP ramps up. You must limit Parallel to far below the TP limits. Otherwise, internal ADO.NET TP usage might be crowded out. We want to eliminate TP overloading effects. Also, you probably should set the TP min value so that there is no ramp up. It just creates threads immediately up the the min setting. Let's take the ramp out of this investigation. Also note, that the Task.Run wrapping you are doing is doubling the number of threads required. That's why I suggested 500/5000 limit values.
Nov 17, 2016 at 22:18 comment added ccook @usr it seemed at first that setting the max/min value on the thread pool does make quite a difference, but I think it's just in the ramp up behavior of Parallel.ForEach. Oddly, OpenAsync is still able to handle more concurrent threads before timing out. With a connection pool of 100, I can only finish with a max degree of parallelism of 100 safely with Open(), with OpenAsync() I can push it into 200 without issue.
Nov 17, 2016 at 21:33 comment added ccook @Igor, ill try his suggestion now. But that's exactly why I was confused, the documentation describes it as a simple wrapper, so I expect the same behavior. If it should be different in any way, I wouldnt have worried about it.
Nov 17, 2016 at 21:25 comment added Igor Very odd, I can also duplicate it. The code for OpenAsync calls through to Open so should not be a difference. Have you tried what usr suggested?
Nov 17, 2016 at 20:40 history edited ccook CC BY-SA 3.0
added 175 characters in body
Nov 17, 2016 at 20:38 comment added ccook @Igor, I am only changing from Open() to await OpenAsync(), nothing else. Note the code is always sync, just comparing that one change Open() vs OpenAsync()
Nov 17, 2016 at 20:37 comment added Igor In your sync version are you also changing ExecuteReaderAsync() to ExecuteReader() and ReadAsync to Read() ?
Nov 17, 2016 at 20:32 comment added ccook @Igor, just added a second edit which reproduces the same difference with no external packages.
Nov 17, 2016 at 20:31 history edited ccook CC BY-SA 3.0
added 2487 characters in body
Nov 17, 2016 at 20:31 comment added Igor Actually not necessary. I can fit it in the comment: conn.Open(); using (var command = new SqlCommand("waitfor delay '00:00:05'", conn)) command.ExecuteNonQuery(); And removed the entire Task.Run(... wrapper
Nov 17, 2016 at 20:25 comment added ccook @Igor, definitely possible, trying an ADO.NET test without Dapper.
Nov 17, 2016 at 20:14 comment added Igor Is it possible there is something in Dapper that would cause this that it does with the SqlConnection instance? I changed it a little and used ado.net directly (no dapper) and got up to 250 concurrent connections with Open spinning the default 10,000 number of times. Is there anything else special you are doing for the Open version or just replaced the 2 Async calls with the synchronous calls?
Nov 17, 2016 at 20:02 comment added ccook @usr, i will give that a shot. It's worth noting though, when using OpenAsync instead of Open in our applications ORM, our load tests go from meager to being bottlenecked by the SQL server's available resources. So, whatever it is, it affects ASP.NET MVC as well, not just console applications.
Nov 17, 2016 at 19:51 comment added usr The case interests me now. I do not see a good reason there should be any difference. An idea: If Open blocks internally and the thread pool is totally overloaded (here it clearly is) then blocking might take a long time because completing the task being blocked on might require TP resources for IO completion processing. Set the TP to 5000 min/max threads and set MaxDOP for Parallel to 500. The difference should disappear.
Nov 17, 2016 at 19:48 history edited ccook CC BY-SA 3.0
added 38 characters in body
Nov 17, 2016 at 19:37 comment added ccook @usr, reproduced and included in an edit
Nov 17, 2016 at 19:37 history edited ccook CC BY-SA 3.0
added 2397 characters in body
Nov 17, 2016 at 19:12 comment added ccook Sure, give me a few minutes then :)
Nov 17, 2016 at 19:11 comment added usr You could just execute waitfor delay 1 second or select * from master..spt_values. A repro of this should fit into 10-30 lines if freshly written. My guess: You won't be able to repro and discover your bug in the process.
Nov 17, 2016 at 19:09 comment added ccook I agree, and that would be good. To share I'll need to set it up with just the nuget packages and against something like northwind - which should be just find as the test was already very minimal but dubiously proprietary. Might need a couple of hours though to sneak that in.
Nov 17, 2016 at 19:06 comment added usr What you describe is very unexpected. The conn pool has little magic. Very straight forward. Likely, the benchmark was flawed. Care to post code? I have seen a lot of different issues over the years. Could be many things.
Nov 17, 2016 at 19:05 comment added ccook Concrete issue, no, the issue is that we are having failures opening connections in production when under load which lead to the local benchmark and load testing. Note when I'm comparing the two mentioned above it's concurrent and asynchronous.
Nov 17, 2016 at 19:02 comment added ccook Exactly what I did actually (the micro benchmark). I found that I ran out of (timeout on opening) available connections in the application pool (default size of 100) when I had about 35 concurrent async commands executing on connections executing on connections opened with Open. I would have expected it to be at 100. Changing it to use OpenAsync, nothing else, and it would maintain about 300 concurrent commands before failing to open connections. It seems like the connections themselves are async with OpenAsync, not just the opening of them.
Nov 17, 2016 at 19:01 comment added usr Is there a concrete issue you are having in your application? If not that's fine but if yes that might be easier to answer.
Nov 17, 2016 at 19:00 comment added usr I believe not but you should test it in a micro benchmark. Spin up some workload, pause the debugger a few times and look at the Parallel Stacks window. It is very enlightening to watch internal call stacks of libraries to understand what code has impact on throughput. Starting with .NET 4.5 you should see all threads blocked on a task/event for sync APIs and almost no threads active in the async case.
Nov 17, 2016 at 18:58 comment added ccook I agree, I didnt expect any difference. Asking it a different way, would sql server and the thread pool handling the two connections differently?
Nov 17, 2016 at 18:57 comment added usr running the connection pool dry early it should not behave differently. Whether the thread blocks on IO or on an event makes little difference. executing asynchronous commands ... on those connections behaves differently This might be the case, I don't know. Asynchronous Processing=true must have had some effect in previous .NET versions. I think the wrong question is to look at Open(Async) and the right one is to look at what Asynchronous Processing=true did. But according to this it should at most have had a small CPU cost impact: stackoverflow.com/a/7852617/122718
Nov 17, 2016 at 18:53 comment added ccook So, is the unmanaged sql connection fundamentally different? edit - i realize you are saying it's the same.
Nov 17, 2016 at 18:52 comment added usr Starting with .NET 4.5 internally it's always async IO. The sync version just blocks...
Nov 17, 2016 at 18:49 history asked ccook CC BY-SA 3.0