I have a WCF webservice that caters simultaneous requests from different clients. The service has to communicate with mysql Db on every request. The issue that i am facing is that a large number of requests come in at a same time (about 300-400) and while communicating with MySQL 'Unable to connect to any of the specified MySQL hosts' pops up.
What i've figured out till now is that Mysql is unable to manage so many simultaneous connections and fails to cater any more and starts throwing the error. What is the best way to get around this issue?
Following is the code snippet that is called every time a requests comes in:
try
{
MySqlCommand command = new MySqlCommand(query);
command.Connection = conn;
conn.Open();
long id = -1;
int _result = command.ExecuteNonQuery();
if (_result == 1)
{
//do something
}
conn.Close();
return id;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
}
How long does "//do something" take? If it's short, I don't see a whole lot you can do to
speed it up from the code. If it's long, consider closing the connection before doing whatever that is (presumably error handling).
(As a side note, you should try the using statement instead of the try/catch block to close the connection)
edit: Also, as SLaks said, "throw;" will do the same thing as "throw ex;" except it will preserve the stack trace, which is why you should not have the "ex;" on there, but this point is moot if you replace it with using.
Related
I'm working on an ASP.NET application where, as part of some logic, I want to lock some tables and do work on them. The method runs in a separate thread running as a kind of background task, spawned via a Task. The problem comes in with the error handling...
The code looks more or less like this:
MySqlConnection connection = new MySqlConnection(ConfigurationManager.AppSettings["prDatabase"]);
try
{
connection.Open();
MySqlCommand lock_tables = new MySqlCommand(Queries.lockTables(), connection);
lock_tables.ExecuteNonQuery();
// do a bunch of work here
MySqlCommand unlock_tables = new MySqlCommand(Queries.unlockTables(), connection);
unlock_tables.ExecuteNonQuery();
}
catch (MySqlException mex)
{
// Mostly error logging here
}
finally
{
connection.Close();
}
Pretty simple stuff. Everything works fine and dandy assuming nothing goes wrong. That's a terrible assumption to make, though, so I deliberately set up a situation where things would foul up in the middle and move to the finally block.
The result was that my table locks remained until I closed the app, which I learned by trying to access the tables with a different client once the method completed. Needless to say this isn't my intention, especially since there's another app that's supposed to access those tables once I'm done with them.
I could quickly fix the problem by explicitly releasing the locks before closing the connection, but I'm still left curious about some things. Everything I've read before has sworn that closing a connection should implicitly release the table locks. Obviously in this case it isn't. Why is that? Does connection.Close() not actually completely close the connection? Is there a better way I should be closing my connections?
Try wrapping your Connection and MySqlCommand instance in a using statement. That will release the objects as soon as it leaves the brackets.
using(MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
using(MySqlCommand command = new MySqlCommand("command to execute",conn))
{
//Code here..
}
}
According to Microsoft's article (SQL Server Connection Pooling (ADO.NET)),
When connection pooling is enabled, and if a timeout error or other login error occurs, an exception will be thrown and subsequent connection attempts will fail for the next five seconds, the "blocking period". If the application attempts to connect within the blocking period, the first exception will be thrown again. After the blocking period ends, another connection failure by the application will result in a blocking period that is twice as long as the previous blocking period. Subsequent failures after a blocking period ends will result in a new blocking periods that is twice as long as the previous blocking period, up to a maximum of five minutes.
How would you detect that the blocking period is active? I would assume that there is some property to check before attempting the connection so that you could avoid extending the blocking period.
There shouldn't be a need to check if you're in a blocking period to avoid extending it. As it says in the excerpt above, any attempts to connect during the blocking period will re-throw the first exception, it says nothing about extending the blocking period. However, each new blocking period will be twice as long as the previous.
In my experience, the exceptions that get thrown (due to timeouts, connection leaks, etc.) are either environmental issues or failing to properly close/dispose connections. It's a good idea to log these exceptions so that you can track down the real issue.
If you do keep coming across a timeout exception, you could catch it and try to clear all the pools, but it's likely due to a connection leak. You'll want to make sure you're wrapping your connections with a using statement, which will help to close/dispose of your connections when you're done with them or if an exception occurs.
using(SqlConnection connection = new SqlConnection("connection_string"))
{
using(SqlCommand command = new SqlCommand())
{
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandTimeout = [some timeout value];
command.CommandText = "Update SomeTable Set Value = 1";
connection.Open();
command.ExecuteNonQuery();
}
}
In addition to the ClientConnectionId field,
The SqlException.Message will also be reference-equal. That is, the cached string will be returned for connections that fail within the "blocking period".
However, this is also an implementation detail and may change.
Unfortunately there is no easy way to detect if you are in the ADO.NET "Blocking Period" or not (without resorting to something fragile like reflection).
However, if you are using .Net 4.5 or later, then you can detect if the last exception you observed from Open/OpenAsync is duplicated or not by looking at the ClientConnectionId of the SqlException and comparing that to the id of the last SqlException that you've seen (since the exceptions are duplicated, the ids are duplicated as well).
Assuming that you have a single place where you create\open SqlConnections for a single connection string, you can do the following:
public static class DataAccessLayer
{
// Single connection string that all connections use
private static readonly string _connectionString = "server=(local);integrated security=true;";
// Stores that last observed connection if when opening a connection
// NOTE: Using an object so that the Volatile methods will work
private static object _lastErrorConnectionId = Guid.Empty;
public static SqlConnection GetOpenedConnection()
{
try
{
SqlConnection connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
catch (SqlException ex)
{
// Did the connection open get to the point of creating an internal connection?
if (ex.ClientConnectionId != Guid.Empty)
{
// Verify that the connection id is something new
var lastId = (Guid)Volatile.Read(ref _lastErrorConnectionId);
if (ex.ClientConnectionId != lastId)
{
// New error, save id and fall-through to re-throw
// NOTE: There is a small timing window here where multiple threads could end up switching this between
// a duplicated id and a new id. Since this is unlikely and will only cause a few additional exceptions to be
// thrown\logged, there isn't a large need for a lock here.
Volatile.Write(ref _lastErrorConnectionId, (object)ex.ClientConnectionId);
}
else
{
// Duplicate error
throw new DuplicatedConnectionOpenException(_connectionString, ex);
}
}
// If we are here, then this is a new exception
throw;
}
}
}
public class DuplicatedConnectionOpenException : Exception
{
public string ConnectionString { get; private set; }
internal DuplicatedConnectionOpenException(string connectionString, SqlException innerException)
: base("Hit the connection pool block-out period and a duplicated SqlException was thrown", innerException)
{
ConnectionString = connectionString;
}
}
Now if you call GetOpenedConnection and you see a DuplicatedConnectionOpenException being thrown, you will know that you have hit the "Blocking Period".
NOTE: I'm using Volatile Read/Write here instead of a lock since I'm opting for better performance versus being 100% accurate about being in the "Blocking Period". If you would prefer the accuracy you can use a lock instead.
Additionally, I do have code that works as an extension method on SqlConnection and can handle multiple connection strings, but its performance is much worse since it uses a ConcurrentDictionary to map connection strings to connection ids.
I have a C# Console Application that is essentially a long batch process that processes data on a nightly basis across many different databases. What is the proper or preferred way to test basic database connectivity at the beginning of this type of program? I ran into the issue of having an expired database password for one of my connections that was caught by exception handling but I want to test for basic connectivity at the very beginning.
Would a simple SELECT query suffice or is there a more efficient way of doing this for numerous databases?
IMHO the simplest way is trying to connect to database and, if you have a failure, you give up.
As you're running a night batch, it's not important to understand immediately the reason and solve it.
So something like this
using(SqlConnection conn = new SqlConnection(connectionString))
{
try
{
conn.Open();
// Do what you please here
}
catch (Exception ex)
{
// Write error to file
File.Append(...,
DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + " " +
ex.Message);
}
finally
{
conn.Close();
}
}
Next morning you can check file for errors...
'Connection.open`
is the simple way to determine if you can connect to db or not.
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
}
If you get a SqlException with number 18487 or 18488 it indicates the password has been changed.
Connection.changePassword
You don't need to run any query.
If you use SqlConnection passing the connection string, you can just try to Open() the connection and you'll get an exception if you cannot connect
Something like:
try
{
var cnn = new SqlConnection(connectionString);
cnn.Open();
}
catch
{
// connection failed, do something
}
Opening (and then closing) a connection should be sufficient to test the password. however, this does not tell you , if a db-user has permissions to access specific tables.
In my Windows application I try to connect to SQL Server 2008 with following code:
SqlConnection connection = new SqlConnection(Properties.Settings.Default.KargarBandarConnectionString);
SqlCommand command = new SqlCommand("Select IsAdmin from Users where UserName=#UserName And Password=#Password", connection);
SqlDataReader dataReader = null;
command.Parameters.AddWithValue("#UserName", UserNameTextBox.Text.Trim());
command.Parameters.AddWithValue("#Password", PasswordTextBox.Text);
try
{
connection.Open();
dataReader = command.ExecuteReader();
if (dataReader.HasRows)
{
while (dataReader.Read())
{
IsAdmin = dataReader.GetBoolean(0);
}
this.DialogResult = DialogResult.OK;
}
else
{
FMessageBox.ShowWarning("error");
UserNameTextBox.Focus();
}
}
catch (Exception ex)
{
if (progressForm != null)
progressForm.Close();
FMessageBox.ShowError(ex.Message);
}
finally
{
if (dataReader != null)
{
dataReader.Close();
dataReader.Dispose();
}
if (connection != null)
{
connection.Close();
connection.Dispose();
}
}
Everything works properly, but sometimes I get the following error:
timeout expired. the timeout period elapsed prior to obtaining a
connection from the pool ...
How can this be solved?
The reason you're getting this exception is because you have exhausted your connection pool and the number of "available" connections in your application.
Every time you open a connection, one is pulled from the connection pool if possible, or a new one is created if not.
However, to prevent galloping usage of connections, a limit of 100 (I think this is configurable) exists, and if you try to use more than 100 simultaneous connections, the code will not create new ones, and instead sit down to wait for one to be returned to the pool, and in this case you get a timeout if it sits too long.
So, for the particular example of code you've shown, I would:
Close the connection before I show an error messages to the user
However, unless 100 users are seeing the error message and leaving it there at the same time, it is unlikely the code you've shown is the cause of this problem.
Other than that, I would go through the entire application and ensure you don't have any connection leaks other places.
This particular type of exception can occur in one spot even though the problem is somewhere else. Example: A report is leaking an open connection every time it runs, and you run it 100 times successfully, then someone tries to log in, and the exception occurs in the login form.
That happens if you either:
leak connections (leaving them for GC to deal with rather than disposing them)
just have too much happening, such that the pool is exhausted
The first is the most common, and I expect it relates a lot to the fact that you are over-complication your error handling. This makes it easy to miss, and hard to spot that you've missed it. The code shown looks OK, but it would be far preferable to use using blocks for all the IDisposable elements, rather than finally. Also; don't keep the connection while you show modal things like the message box, unless you need the connection afterwards. Frankly, a lot of benefit here could be made by cleanly separating the UI and data-access code, then there is not temptation to stick a message-box in the middle of a database query.
However! To be explicit, I believe this code is the victim of some other code that is hogging connections. Look at your other data access code for the cause of this.
Refactor your code to look something like this. Implement using blocks. The other answers here are very important, be sure to understand them.
bool res=false;
try
{
using(var connection = new SqlConnection(Properties.Settings.Default.KargarBandarConnectionString))
using(var cmd = conn.CreateCommand())
{
cmd.commandText = "Select IsAdmin from Users where UserName=#UserName And HashedAndSaltedPassword=#PwdHash";
cmd.Parameters.AddWithValue("#UserName", UserNameTextBox.Text.Trim());
cmd.Parameters.AddWithValue("#PwdHash", SaltAndHash(PasswordTextBox.Text));
connection.Open();
var result = cmd.ExecuteScalar();
if (result!=null)
{
res=bool.Parse(result);
this.DialogResult = DialogResult.OK;
}
}
}
catch (Exception ex)
{
if (progressForm != null){progressForm.Close();}
FMessageBox.ShowError(ex.Message);
}
if(res==false)
{
FMessageBox.ShowWarning("error");
UserNameTextBox.Focus();
}
I one of my c# application, i have written sql connection code as following
try
{
myConnection = new SqlConnection(m_resourceDB.GetResourceString(nSiteID, ApplicationID.XClaim,(short)nResID ) );
myConnection.open();
}
I want to handle unkown issue of sqlserver like database down, time out.
For this i though to introduce for loop 3 times with 3 minute sleep between loop and if at all problem is there then i will exit from loop
I don't know my though is right or not? I want some expert advice on this? Any example?
I would say simply: the code that talks to connections etc should not be doing a sleep/retry, unless that code is already asynchronous. If the top-level calling code wants to catch an exception and set up a timer (not a sleep) and retry, then fine - but what you want to avoid is things like:
var data = dal.GetInfo();
suddenly taking 3 minutes. You might get away with it if it is an async/callback, and you have clearly advertised that this method may take minutes to execute. But even that feels like a stretch. And if you are up at the application logic, why not just catch the exception the first time, tell the user, and let the user click the button again at some point in the future?
If you are running a service with no user interface, then by all means, keep on looping until things start working, but at least log the errors to the EventLog while you're at it, so that the server admin can figure out when and why things go wrong.
For a client application, I would no suggest that you make the user wait 9 minutes before telling them things are not working like they should. Try to connect, assess the error condition, and let the user know what is going wrong so that they can take it further.
If you are using the SqlException class you can check the Exception Class and decide based on that what is going wrong, for example:
switch (sqlEx.Class)
{
case 20:
//server not found
case 11:
//database not found
All the classes have the SQL Server message on them, it is a matter of testing the different conditions.
It really depends on how you want your application to behave.
If your database access is dealt with on the same thread as your UI then whilst you are attempting to connect to a database it will become unresponsive.
The default time period for a connection timeout is already pretty long and so running it in a for loop 3 times would triple that and leave you with frustrated users.
In my opinion unless your specifically attempting to hide connection issues from the user, it is by far better to report back that a connection attempt has failed and ask the user if they wish to retry. Then having a count on the number of times that you'll allow a reconnection attempt before informing the user that they can't continue or putting the application into an "out of service" state.
I want to handle unkown issue of sqlserver like database down, time out.
Try to surround connection operation with using statement to capture connection related problems .
using( sqlcon = new SqlConnection(constr))
{}
Use the Try/Catch Statement for capturing the exception:
try
{
con.Open();
try
{
//Execute Queries
// ....
}
catch
{
// command related or other exception
}
finally
{
con.Close();
}
}
catch
{
// connection error
}
To prevent Exception of such type check these:
Troubleshooting Timeout SqlExceptions
you can set the CommandTimeout to some value on a SqlCommand:
objCmd.CommandTimeout = 600
You can catch the SqlException.
SqlException.Class
Gets the severity level of the error returned from the .NET Framework Data Provider for SQL Server.
SqlException.Errors
Gets a collection of one or more SqlError objects that give detailed information about exceptions generated by the .NET Framework Data Provider for SQL Server.
SqlCommand cmd = new SqlCommand();
cmd.Connection = new SqlConnection("CONNECTION_STRING");
cmd.CommandText = "SELECT * FROM ....";
// cmd.CommandType = System.Data.CommandType.Text;
try
{
cmd.Connection.Open();
try
{
SqlDataReader reader = cmd.ExecuteReader();
// ....
}
finally
{
cmd.Connection.Close();
}
}
catch (SqlException ex)
{
// ex.Class contains the ErrorCode, depends on your dataprovider
foreach (SqlError error in ex.Errors)
{
// error.LineNumber
// error.Message
}
}
The best way would be to putt it in a try catch statement and display the error in a better format, If it fails for 1 time, trying it continue sly 3 times will not change anything untill and unless you dc and send request again, In a separate in separate packed as a new request.
use try this.
try
{
Executing code.
}
catch (Exception err)
{
Display["ErrorMsg"] = err.Message.ToString() + "|" + err.GetBaseException() + "|" + Request.Url.ToString();
}
Good Luck.