my problem: earlier this week I had a task to speed up the task in our program. I looked at it and immediately understood the idea of using a parallel foreach loop for a function in this task.
I implemented it, went through a function (including all subfunctions) and modified SqlConnections (and other things) so that it could work in parallel. I started it all, and everything went well and quickly (alone it reduced the time for this task by ~ 45%)
Now we wanted to try the same thing with some additional data and ... I had some strange problem: whenever a parallel function was called, it worked ... but sometimes one of the threads hung for at least 4 minutes (time Outs are set for one minute, for the connection command I).
If I pause the program during this, I see that only one thread is still active from this loop, and it hangs on
connection.Open()
After ~ 4 minutes, the program simply goes without error (except for the message in the "Output" field, saying that an exception occurred somewhere, but it was not caught by my application, but somewhere in the SqlConnection / SqlCommand object).
I can kill all connections on MSSQLServer if nothing happens, also MSSQLServer does nothing in these 4 minutes, all connections are idle.
This is the procedure that is used to send update / insert / delete statements to the database:
int i = 80; bool itDidntWork = true; Random random = new Random(); while (itDidntWork && i > 0) { try { using (SqlConnection connection = new SqlConnection(sqlConnectionString)) { connection.Open(); lock (connection) { command.Connection = connection; command.ExecuteNonQuery(); } itDidntWork = false; } } catch (Exception ex) { if (ex is SqlException && ((SqlException)ex).ErrorCode == -2146232060) { Thread.Sleep(random.Next(500, 5000)); } else { SqlConnection.ClearAllPools(); } Thread.Sleep(random.Next(50, 110)); i--; if (i == 0) { writeError(ex); } } }
just in case: in small databases, deadlocks may occur (error number 2146232060), so if this happens, I have to make counter-statements at different times. Works great even on small databases / small servers. If the error was not caused by a dead end, most likely the connection was faulty, so I clear all the failed connections.
There are powerful functions for executing scalars, populating data / datasets (yes, the application is old) and executing stored procedures.
And yes, they are all used in a parallel loop.
Does anyone know what could be there? Or the idea of how I can find out what's going on there?
* edit the command object:
it is assigned to a function, the command object is always a new object when it is specified in a function.
about blocking: if I postpone the blocking, I get dozens and hundreds of “closed connections” or “the connection is already open” because the Open () function just gets the connection from the .NET connection pool. The lock works as intended.
Code example:
using(SqlCommand deleteCommand = new SqlCommand(sqlStatement)) { ExecuteNonQuerySafely(deleteCommand);
* edit 2
I have to make a fix: it hangs on this
command.Connection = connection;
At least I assume this is happening because when I pause the application, the “step” label is green and on
command.ExecuteNonQuery();
saying that this is an operator that will be executed as follows.
* edit 3 just to make sure that I just started a new test without any locks around the connection object ... it will take several minutes to get the results.
* change 4 well, I was wrong. I deleted the lock statements and ... it still worked. Perhaps the first time I tried, there was a repeated connection or something like that. Thanks for pointing this out.
* change 5 I get the feeling that this happens only with one specific call to a specific database procedure. I do not know why. C # wise there is no difference between this call and other calls, see Edit 6. And since he was not executing the instruction at that moment (I think maybe someone can fix me about this. If in debug mode, the line green is marked (instead of yellow), it has not yet executed this statement, but is waiting for expression before this line ends, is that right?) This is strange.
* edit 6 There were three command objects that were reused all the time. They were defined above the parallel function. I do not know how bad this is. They were used only to call one stored procedure (each of them was called a different procedure), of course, with different parameters and a new connection (using the aforementioned method).
* edit 7 is normal, this is only valid when invoking one specific stored procedure. Except that when assigning a connection object it hangs (the next line is marked in green). Trying to find out what is the reason for this.
* change 8 yay, this happened only on another command. So here it is.
* change 9 OK. The problem is solved. Hangs were actually CommandTimeouts that were set for 10 minutes (!). They were installed for only two teams (the one that I mentioned in edition 7 and the one that I mentioned in edition 8). Since I found both of them while I was rebuilding my commands to make them look like devundef, I marked his answer as the one that solved my problem. In addition, his proposal to limit the number of threads used by my loop accelerated the process even further.
Special thanks to Mark Gravel for explaining things and hanging himself here with me on Saturday;)