I'm using Oracle through their official managed drivers for .NET (a Nuget package).
My application uses the same connection to the DB from the beginning and it's used from several locations to perform queries.
In some cases, there may be connection "hiccups" that cause exceptions. The problem is that I don't know what's the best strategy to retry to perform a query when this happens.
Is there a common way to solve this situation?
Thank you.
I agree with the comment from Habib.
The oracle .NET Package uses connection pooling. Even if you open up multiple connections, it will manage them accordingly so that you don't have to keep it open.
That means that your code can be simplified, into something like this pseudo-code:
using(OracleConnection conn = MakeConnection())
{
//do stuff with connection
//not necessary, but I always manually close connection. Doesn't hurt.
conn.Close();
}
If you're uncertain of connection issues even in that small of an execution, you can wrap it in a try-catch block like so:
try
{
using(OracleConnection conn = MakeConnection())
{
//do stuff with connection
//not necessary, but I always manually close connection. Doesn't hurt.
conn.Close();
}
}
catch(OracleException ex)
{
//handle exception.
}
OracleException looks to be the main exception with the .NET oracle package. Please note that there may be others you want to catch more specifically.
It would be easier to instantiate the connection on the fly when the query is being made. I don't think a simple try/catch would help you here because even if you reinitialized the connection in the catch block, you would have to somehow re-execute your query.
I don't recommend this but you could use a Retry class that reinitializes the connection if an exception is caught....
public class Retry
{
public static void Do(Action action, TimeSpan retryInterval, int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
},
retryInterval, retryCount);
}
public static T Do<T>(Func<T> action, TimeSpan retryInterval, int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
return action();
}
catch (ConnectionException ex)
{
// ***Handle the reconnection in here***
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
Then you can call your query like
Retry.Do(() => MyQueryMethod, TimeSpan.FromSeconds(5));
I got the basis for this Retry code from SO a long time ago, don't recall the thread but it isn't my original code. I have used it quite a bit for some things though.
Related
public static void SetStatus( Status statusObject,int retryCount)
{
if (statusObject != null)
{
using (SqliteConnection dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
}
catch (Exception e)
{
dbTransaction.Rollback();
dbConn.Close();
if (retryCount > 0)
{
SetStatus(statusObject, --retryCount);
return;
}
else
throw e;
}
finally
{
try { dbTransaction.Commit(); }
catch (Exception e)
{
}
}
}
}
}
Whenever ExecuteNonQuery fails due due to some exception I have a retry mechanism which will run the same query again.In that case during the second time(while retrying) the following exception comes-
"Connection must be valid and open to commit transaction"
The problem is basically, that you commit the transaction in the finally block—which runs always per design—even though you may already have rolled back the transaction and closed the connection in the catch block.
The easier way would be to commit the transaction while still in the try block, because after all, you only want to commit the transaction if all the code in the try succeeded.
That being said, you are having some odd nesting with the try and using constructs there. You should try to keep things like usings as concise as possible. That way you will also not have to deal with closing connections yourself. Something like this:
public static void SetStatus(Status statusObject, int retryCount)
{
if (statusObject == null)
return;
try
{
using (var dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
dbTransaction.Commit();
}
catch
{
// transaction might be null if the connection couldn’t be opened
dbTransaction?.Rollback();
throw;
}
}
}
catch (Exception ex)
{
if (retryCount > 0)
SetStatus(statusObject, --retryCount);
else
throw ex;
}
}
One final note, one could argue that using a transaction is absolutely not necessary here: You are only executing a single query on this database connection, so either that works, or it will fail in which case there is not something to rollback anyway. So unless you are having multiple queries there, you don’t need an explicit transaction (but I’ll just assume that you just reduced your example to show just a single query).
My bad,screwed up using "finally" improperly myself.So if the query fails at first attempt it would go to catch block which will fire the call to re run the query.Second time query execution would be successful and transaction would be committed.Then the control goes to finally of first query which will again try to commit the already rolled back transaction. So,it would throw that exception.
So,if the moving the commit to main try block fixed the issue.
I have a Web API 2 endpoint set up to test a 2+ second ADO.NET call. When attempting to "burst" this api, it fails horribly when using async methods. I'm getting connection timeouts and reader timeouts. This doesn't happen with the synchronous version of this method when "bursted". The real problem is as follows...
Async ADO.NET behaves strangely when exceptions are thrown. I can see the exceptions thrown in the Visual Studio output, but they are not caught by my code. As you'll see below, I've tried wrapping try/catches around just about everything and have had no results. I did this to be able to set break points. I understand that catching exceptions just to throw them is bad. Initially, I only wrapped the call in the API layer. The worst part is, this locks up the entire server.
Clearly, there's something I'm missing about async ADO.NET. Any ideas?
EDIT:
Just to clarify what I'm trying to do here. This is just some test code on my local computer that's talking to our developmental database. It was just to prove/disprove that we can handle more traffic with async methods against our longer running db calls. I think what's happening is that as the calls are stacking up. In doing so, the await'ed connections and readers are timing out because we're not getting back to them quickly enough. This is why it doesn't fail when it's ran synchronously. This is a completely different issue. My concern here is that the operations are not throwing exceptions in a way that can be caught. The below is not production code :)
Web API2 Controller:
[Route("api/async/books")]
[HttpGet]
public async Task<IHttpActionResult> GetBookAsync()
{
// database class instantiation removed
// search args instantiation removed
try
{
var books = await titleData.GetTitlesAsync(searchArgs);
return Ok(books);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Data Access:
public async Task<IEnumerable<Book>> GetTitlesAsync(SearchArgs args)
{
var procName = "myProc"
using(var connection = new SqlConnection(_myConnectionString))
using (var command = new SqlCommand(procName, connection) { CommandType = System.Data.CommandType.StoredProcedure, CommandTimeout = 90 })
{
// populating command parameters removed
var results = new List<Book>();
try
{
await connection.OpenAsync();
}
catch(Exception ex)
{
throw;
}
try
{
using (var reader = await command.ExecuteReaderAsync())
{
try
{
while (await reader.ReadAsync())
{
// FROM MSDN:
// http://blogs.msdn.com/b/adonet/archive/2012/07/15/using-sqldatareader-s-new-async-methods-in-net-4-5-beta-part-2-examples.aspx
// Since this is non-sequential mode,
// all columns should already be read in by ReadAsync
// Therefore we can access individual columns synchronously
var book = new Book
{
Id = (int)reader["ID"],
Title = reader.ValueOrDefault<string>("Title"),
Author = reader.ValueOrDefault<string>("Author"),
IsActive = (bool)reader["Item_Active"],
ImageUrl = GetBookImageUrl(reader.ValueOrDefault<string>("BookImage")),
ProductId = (int)reader["ProductID"],
IsExpired = (bool)reader["Expired_Item"]
};
results.Add(book);
}
}
catch(Exception ex)
{
throw;
}
}
}
catch(Exception ex)
{
throw;
}
return results;
}
}
Consider such a function:
void RequestThings(List<Things> container, Connection connection, Int32 lastVersion) {
var version = lastVersion;
try {
foreach(var thing in connection.RequestThings(version)) {
container.Add(thing);
version = thing.lastVersion;
}
}
catch(Exception ex) {
RequestThings(container, connection, version + 1);
}
}
But this choice is far not perfect: it involves adding to a recursion depth (up to a stack overflow) in case if there are (many) exceptions.
How do I rewrite this the iterative way?
I've tried to do this like:
var container = new List<Things>();
var version = getLastVersionFromDB();
foreach(var thing in connection.RequestThings(version)) {
try {
container.Add(thing);
}
catch(Exception ex) {
continue;
}
}
But it appears that exception doesn't get handled. How do I do this?
edit. the details
Connection.RequestThings(Int32 startVersion) requests data from a remote server. Accepts a seed version as its only parameter. There might be blocked/damaged documents which you cannot request though they appear on the results returned by calls to Connection.RequestThings(Int32 startVersion). This piece throws the exception
Don't know why but the inner try/catch in my iterative example doesn't catch the exception.
Generally, it's a bad idea to have a catch clause for all exceptions. Consider catching only a specific exception type to be sure that you're not swallowing unexpected errors.
Additionally, if you got a stack overflow in the first place, it indicates that you might be doing something wrong. For example, what happens if you pass an invalid version number to this method, and there are no documents with a larger version number available? This method will keep running forever, with no chance to gracefully cancel it. Especially since it seems that you are getting the "last version" from a database somehow; if this fails, you can be pretty certain that no higher version exists.
Having said that, you can simplify the method by creating an "infinite" loop and then using return to exit the method on success:
void RequestThings(List<Things> container, Connection conn, int version)
{
while (true)
{
try
{
foreach (var thing in connection.RequestThings(version))
{
container.Add(thing);
version = thing.lastVersion;
}
return;
}
catch (Exception ex)
{
Log.Error(ex);
version++;
}
}
}
A slightly better approach might be to make sure that you really get the entire list on success, or nothing. The way your code is written right now leaves the possibility of container being filled multiple times if an exception happens while iterating.
List<Things> RequestThings(Connection conn, int version)
{
while (true)
{
try
{
// this will either create an entire list,
// or fail completely
return connection.RequestThings(version).ToList();
}
catch (Exception ex)
{
Log.Error(ex);
version++;
}
}
}
I've noticed this problem happening a lot in most things I do, so I'm thinking there must be a design pattern for this.
Basically if an exception is thrown, attempt to solve the problem and retry. If I place it in the try, all it will do is catch the exception, but I want to retry whatever it was doing and if it fails again, retry again a certain number of times.
Is there a common pattern for this sort of stuff?
check this SO answer.. hope that helps u
Cleanest way to write retry logic?
public static class RetryUtility
{
public static void RetryAction(Action action, int numRetries, int retryTimeout)
{
if(action == null)
throw new ArgumenNullException("action");
do
{
try
{
action();
return;
}
catch
{
if(numRetries <= 0)
throw; // Avoid silent failure
else
{
Thread.Sleep(retryTimeout);
numRetries--;
}
}
}
while(numRetries > 0);
}
}
Call
RetryUtility.RetryAction( () => SomeFunctionThatCanFail(), 3, 1000 );
Credit goes to LBushkin
This runs indefinately but it would be easy to add a loop counter to the while clause
var solved = false;
var tries = 0;
while (!solved)
{
try
{
//Do Something
solved = true;
}
catch
{
//Fix error
}
finally
{
if(solved || IsRediculous(tries))
break;
tries++;
}
}
try/catch inside a loop, with a counter for retries?
EDIT: And your requirement of "retry whatever it was doing," you need custom logic for that, how to retry varies wildly (ie, reopen a stream, recreate the object, pause for X milliseconds, etc...), so you need it's own try/catch inside a loop for every atomic operation.
By "atomic operation" I mean a set of related statements, such as read a file. The whole file read into memory might be an atomic operation, for example.
On some limited basis, you might want to put your try/catch into a loop, and force break if is ultimately successful. Such might be for internet access testing and you want user to have another attempt at connection.
Something like this, maybe:
int MAX_RETRIES = 5;
for (var attempt=1; attempt <= MAX_RETRIES; attempt++) {
try {
DoSomethingThatMightThrow();
}
catch (AnExceptionIKnowHowToHandle) {
if (attempt < MAX_RETRIES)
continue;
throw;
}
}
Depends what you are trying, but typically you want to check for the possibility of an exception happening PRIOR to executing the code that could cause an exception.
For example, check that a file exists before accessing it, and create it (or whatever) if it doesn't.
Are you sure exception handling is the proper methodology here? If you can "solve the problem" you can probably detect the error condition prior to calling the exception-generatiing code.
Exception handling is most natural for things which are truly exceptional. A failed Internet connection (as in the previous answer) is something that can be detected and handled before calling exception-throwing code.
Yes, it is quite common to have a loop with a number of retries where you break out of the loop on success. A couple of things:
You might want to add a delay before retrying so that you don't use up all your retries in just a few milliseconds before the temporary problem had time to fix itself.
If you eventually fail, you should throw the first exception you caught, not the last one. The second exception could be the result of failing to recover correctly from the first failure and might not help to debug the original problem.
Coding what others have already mentioned:
var success = false;
var attempts = 0;
var maxAttempts = 0;
do {
attempts++;
try {
/* your code */
success = condition;
} catch(SuperciliousException e) {
/* recover */
}
} while(!success && attempts < maxAttempts);
I have a C# database layer that with static read method that is called every second in background timer.
currently I create SqlCommand, SqlConnection once as a class memeber.
In every method call I execute the command to get the results,I am doing so to avoid creation of connection and command every second, but I am afraid from exception occurs in this method that will break the connection or put the object in the invalid state.
This is my current implementation (Timer Handler)
static void GetBarTime(object state)
{
lock (_staticConnection)
{
SqlDataReader dataReader = null;
try
{
dataReader = _getMaxTimeCommand.ExecuteReader();
dataReader.Read();
_currentTick = dataReader.GetInt32(0);
}
catch (Exception ex)
{
//Log the error
}
finally
{
dataReader.Dispose();
}
}
}
What is the best practise?
MORE DETAILS:
I am doing this in a timer as there is another prorcess update my table every second, and there is another exposed method used by set of clients and called every second to get the latest value.
So instead of executing select statement every second for each client, I am doing it in a timer and update global variable that is used by the clients.
SqlConnection has pooling built in; you would see almost no difference if you used:
using(SqlConnection conn = new SqlConnection(connectionString)) {
conn.Open();
// your code
}
each time. And that can react automatically to dead (underlying) connections.
Currently you have a bug, btw; if the command fails, the reader will still be null... either check for null before calling Dispose():
if(dataReader !=null) {dataReader.Dispose();}
or just use using:
try
{
using(SqlDataReader dataReader = _getMaxTimeCommand.ExecuteReader())
{
dataReader.Read();
_currentTick = dataReader.GetInt32(0);
}
}
catch (Exception ex)
{
//Log the error
}
It can be pretty difficult to find out if an execption means that the connection is a dead duck. To be on the safe side, you could close and reopen the SqlConnection and SqlCommand whenever you encounter an exception, just in case. That doesn't cause any overhead when everything works alright.