Exception Handling in RavenDb UnitOfWork

316 views
Skip to first unread message

aa

unread,
Apr 16, 2012, 4:38:51 PM4/16/12
to rav...@googlegroups.com
Hello

What would be the appropiate way to handle Exceptions or temporary problems with Raven during a UnitOfWork/Session Request using the Client API?

In other words what exception we would need to catch in order to handle all problems that might arise while communicating with Raven, is there something akin to SqlException on SqlServer?

I have found approximately 15 types of different exceptions that Raven throws, it would be really cumbersome to catch all of these for every session/unitofwork in our application.

What would be the best approach in this case?

Regards
Albert

Itamar Syn-Hershko

unread,
Apr 16, 2012, 6:24:52 PM4/16/12
to rav...@googlegroups.com
What exceptions / temp problems?

The UoW is your actual POCOs, btw

aa

unread,
Apr 16, 2012, 6:37:37 PM4/16/12
to rav...@googlegroups.com
It could be any exception.

It could be problems caused by disk, Timeout Exceptions when we want non stale results, security exception, any exception.

We need to properly handle error situations when making DB calls on the session.



On Monday, April 16, 2012 5:24:52 PM UTC-5, Itamar Syn-Hershko wrote:
What exceptions / temp problems?

The UoW is your actual POCOs, btw

Oren Eini (Ayende Rahien)

unread,
Apr 17, 2012, 4:07:51 AM4/17/12
to rav...@googlegroups.com
There is no RavenException.
There is some internal error handling, but in general, the rule is. If you got an exception from the RavenDB session, that session is toast. Its state is no longer valid, and the only method that you can call on it is dispose.

In general, you'll see three types of exceptions:
* Network errors - server unreachable, etc.
* Concurrency errors - someone modified the doc
* Just plain errors - you tried to do something not possible. (document key too long, etc).

None of them are really things that you can recover from. 

On Mon, Apr 16, 2012 at 11:38 PM, aa <albert...@gmail.com> wrote:

Ryan Heath

unread,
Apr 17, 2012, 4:16:49 AM4/17/12
to rav...@googlegroups.com
How would you recover from any exception?
Think about how should that work in real code?

Now there are, you say, 15 different exceptions, how would your response to these exceptions differ when there was one generic ravenexception? You would (or really should) still handle that exception accordingly to say an errorcode or inner exception, which brings you back to handle the 15 different situations. Thus the generic exception did not really solve the problem at hand.

// Ryan

Sent from my iPhone 

Oren Eini (Ayende Rahien)

unread,
Apr 17, 2012, 5:33:05 AM4/17/12
to rav...@googlegroups.com
inline

On Tue, Apr 17, 2012 at 11:16 AM, Ryan Heath <ryan.q...@gmail.com> wrote:
How would you recover from any exception?

From what aspect. From RavenDB Session PoV, exceptions are not recoverable, you have to discard the session.
From your PoV, catch the exception and see what you need to do based on your logic.
 
Think about how should that work in real code?

Now there are, you say, 15 different exceptions, how would your response to these exceptions differ when there was one generic ravenexception? You would (or really should) still handle that exception accordingly to say an errorcode or inner exception, which brings you back to handle the 15 different situations. Thus the generic exception did not really solve the problem at hand.


Show the error to the user. There really are no real good scenarios for you to make error handling distinctions unless you actually know what the error IS.

aa

unread,
Apr 17, 2012, 10:59:36 AM4/17/12
to rav...@googlegroups.com
Hi

I dont need to recover from an exception, I just need to be able to catch them easily and cleanly.

If I dont catch the exceptions that the session might throw the application will crash, I just want to catch them, then log the exception to the logger (maybe send back some error message to the UI) and dispose the session.

Right now to do this I need to catch around 15 exceptions each time I use the session, how can I achieve what I need without having to catch all of these exceptions every time I use the session?

Regards


On Tuesday, April 17, 2012 3:07:51 AM UTC-5, Oren Eini wrote:
There is no RavenException.
There is some internal error handling, but in general, the rule is. If you got an exception from the RavenDB session, that session is toast. Its state is no longer valid, and the only method that you can call on it is dispose.

In general, you'll see three types of exceptions:
* Network errors - server unreachable, etc.
* Concurrency errors - someone modified the doc
* Just plain errors - you tried to do something not possible. (document key too long, etc).

None of them are really things that you can recover from. 
On Mon, Apr 16, 2012 at 11:38 PM,
Hello

Oren Eini (Ayende Rahien)

unread,
Apr 17, 2012, 11:04:22 AM4/17/12
to rav...@googlegroups.com
catch(Exception e) {

aa

unread,
Apr 17, 2012, 12:03:51 PM4/17/12
to rav...@googlegroups.com
If I do this, then I will catch all exceptions, not RavenDB only Exceptions.

I might be doing other stuff on the same try block where the session code is invoked, I might be calling other services or calling some Model Logic, in the case I get an unexpected exception from this code, I would want the app to crash, if I catch these exceptions with (catch Exception) then the application might be in a unstable/unknown state.

It would be much easier if there was one Raven Exception that would catch everything Raven related.


On Tuesday, April 17, 2012 10:04:22 AM UTC-5, Oren Eini wrote:
catch(Exception e) {

}

On Tue, Apr 17, 2012 at 5:59 PM, aa
Hi

Ryan Heath

unread,
Apr 17, 2012, 1:01:11 PM4/17/12
to rav...@googlegroups.com
You really are recovering from an exception thrown by the session. 

I do not believe in recovering from exceptions at a higher level. I think if you really want this you need to wrap your session calls with try catch(exception). Perhaps it's even feasible to put it into an helper function. 

// Ryan


On Tuesday, April 17, 2012, aa wrote:

aa

unread,
Apr 17, 2012, 1:28:10 PM4/17/12
to rav...@googlegroups.com
If I dont catch these exceptions the application crashes.  Catching (Exception) is way to broad.  There must be a middle ground.

Ryan Heath

unread,
Apr 17, 2012, 1:40:52 PM4/17/12
to rav...@googlegroups.com
Could you post an example how you recover from session exceptions
right now in your code?

// Ryan

aa

unread,
Apr 17, 2012, 1:50:24 PM4/17/12
to rav...@googlegroups.com

This is one example of session usage:

------------------------------------------------------------------------------------------------------------------------------------------------------


 [HttpOperation(HttpMethod.POST, ForUriName = "Execute")]

        public OperationResult Execute(String operationId)

        {

            _logger.Info(String.Format

                ("Operation Manager: Adding a new task to the database for Operation with Id ({0})..."

                operationId));


            var session = _documentStore.OpenSession();

            try

            {

                Operation operation = session.Get<Operation>(operationId);

                Task task = session.CreateTaskFromOperation();


                session.Store(task);


                session.SaveChanges();


                //Initiate Saga to control the task execution lifetime

                Bus.SendLocal(new BeginTask() { TaskId = task.Id });

                

                TaskDto taskDto = Mapper.Map<Task, TaskDto>(task);


                return new OperationResult.OK(taskDto);


            }

            catch (InvalidOperationException invalidOperationException)

            {

                //Tried to create a task from an operation marked as inactive

                _logger.Error(invalidOperationException.Message,invalidOperationException);

                return new OperationResult.BadRequest()

                           {

                               ResponseResource =

                                   new RestApiErrorReply()

                                       {

                                           MainDescription = invalidOperationException.Message, 

                                           ExceptionDetail = invalidOperationException.ToString()

                                       }

                           };

                

            }

            

            finally

            {

                session.Dispose();

            }

        }

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


I need to catch DB related errors somewhere, I need to catch them NOT to recover from them , I need to catch them to be able to LOG them and send that proper error reply back.  If I dont catch them there will be an unhandled exception and the application will crash.  

This is not running on IIS, is a Windows service and the DB is Embedded.

On Tuesday, April 17, 2012 12:40:52 PM UTC-5, Ryan Heath wrote:
Could you post an example how you recover from session exceptions
right now in your code?

// Ryan

Ryan Heath

unread,
Apr 17, 2012, 2:14:17 PM4/17/12
to rav...@googlegroups.com
How does it differ from this?

public OperationResult Execute(String operationId)
{
_logger.Info(String.Format("Operation Manager: Adding a new task to


the database for Operation with Id ({0})...", operationId));

using(var session = _documentStore.OpenSession())
{


try
{
Operation operation = session.Get<Operation>(operationId);
Task task = session.CreateTaskFromOperation();
session.Store(task);
session.SaveChanges();

Bus.SendLocal(new BeginTask() { TaskId = task.Id });
TaskDto taskDto = Mapper.Map<Task, TaskDto>(task);
return new OperationResult.OK(taskDto);
}

catch (Exception e)
{
_logger.Error(e.Message,e);
if !(e is InvalidOperationException) ? return new
OperationResult.InternalServerError() :


return new OperationResult.BadRequest()
{
ResponseResource = new RestApiErrorReply()
{

MainDescription = e.Message,
ExceptionDetail = e.ToString()
}
};
}
}
}

What bothers me now is that your code assumes that only
session.CreateTaskFromOperation could throw an
InvalidOperationException,
while in a few months from now it could be possible that other lines
are throwing InvalidOperationException as well ...
I would rather see the try catch wrapped on session.CreateTaskFromOperation.
All other exceptions would be logged and a runtime error response
would be returned.

I say 'recover' since you do not only catch the exception but you also
handle it, that is, you do not rethrow the exception, effectively you
have swallowed it.

// Ryan

Bryan Johns

unread,
Apr 17, 2012, 2:25:11 PM4/17/12
to rav...@googlegroups.com
Given that Raven doesn't have it's own exception type, you could wrap
it something like this:

(I'm a VB guy so please excuse the VB.Net code)

Public Class RavenException
Inherits DataException

Public Sub New(msg As String, innerEx As Exception)
MyBase.New(String.Format("An exception what thrown from RavenDB
while tyrying to {0}",msg), innerEx)
End Sub

End Class

Then wrap all calls to the Raven session in their own try catch blocks like this

Public Sub DoSomeWork()
Try
' do some stuff
Try
' do some stuff with raven
Catch ex As Exception
Throw New RavenException("do some work.", ex)
End Try
Catch ex As RavenException
'log the RavenException and/or show the user a message
Catch ex As WhateverTypeOfExceptionsYouWantToHandle
' start handling non-raven exceptions
End Try
End Sub

--
Bryan Johns
K4GDW
http://www.greendragonweb.com

Do not meddle in the affairs of dragons, for you are crunchy and taste
good with ketchup.

aa

unread,
Apr 17, 2012, 2:29:11 PM4/17/12
to rav...@googlegroups.com
This would have been much easier with one ExceptionType that encapsulates all Raven related exceptions.

In any event handling (Exception) only is not ideal, there might be other exceptions that the application might throw that I DO NOT want to catch.  There might be exceptions that might be thrown by ASP.NET itself that are really unexpected and are supposed to make the app crash, if I handle everything then there might be exceptions that get handled and logged but the application stays running, running in an inconsistent state.

I would have to surround only session calls with a try to solve this, this means I will need many try/catch blocks in each of my controller actions.

Its much easier/cleaner to catch one exception type that you know it comes from raven.

Ryan Heath

unread,
Apr 17, 2012, 2:40:42 PM4/17/12
to rav...@googlegroups.com
You can handle exceptions in a base controller class or in a filterattribute
so you do not have to sprinkle try catch every where.

But how do you know that you really can continue with your app when an
exception is thrown from a ravensession had it thrown a generic
ravenexception?

// Ryan

aa

unread,
Apr 18, 2012, 4:54:09 PM4/18/12
to rav...@googlegroups.com
inline


On Tuesday, April 17, 2012 1:40:42 PM UTC-5, Ryan Heath wrote:
You can handle exceptions in a base controller class or in a filterattribute
so you do not have to sprinkle try catch every where.

Yes I could, but a Generic Raven Exception would make it much easier, and I cant see any downsides to it.

 

But how do you know that you really can continue with your app when an
exception is thrown from a ravensession had it thrown a generic
ravenexception?

Because we dispose the session, everything related to the session is gone, thus we get back to a good known state.

 

// Ryan

aa

unread,
Apr 18, 2012, 4:57:13 PM4/18/12
to rav...@googlegroups.com
This works, but it gets complicated easily.

Specially if your controller actions are like this:

--Do stuff with raven
--Do stuff with the model (nothing to do with raven)
--Do some other stuff with raven

Now you need to different inner try blocks each time you do something with raven.

This problem gets solved immediately with a RavenException type.

Bryan Johns

unread,
Apr 18, 2012, 5:16:04 PM4/18/12
to rav...@googlegroups.com
Oh definitely, it would be easier if Raven threw it's own exception.
Maybe that'll come in a future version.

In the meantime, we're left with dealing with that ourselves. Another
way would be to use some form of method interception to wrap all raven
activities inside an implicit try-catch block that throws a custom
exception. PostSharp comes to mind. Another option would be to use
an IOC container that supports interception. I know that Unity and
Castle.Windsor both have this capability.

Do not meddle in the affairs of dragons, for you are crunchy and taste
good with ketchup.

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 3:16:35 AM4/19/12
to rav...@googlegroups.com
Really?
What happen when we get a OutOfMemoryException ? Should we wrap it?

In general, you are working with persistent storage, you have very few options of how to actually handle an error. Basically, abort the transaction and try again.
If you have expected errors in the model, you catch and handle that, but that is about it. 
You shouldn't be trying to handle ravendb errors, you just don't have a way to do that, in most scenarios.

Ryan Heath

unread,
Apr 19, 2012, 4:20:59 AM4/19/12
to rav...@googlegroups.com
Looking again at your example, it seems you are struggling with
exceptions from raven because
you use exceptions to control flow?

This example catches InvalidOperation to handle "Tried to create a


task from an operation marked as inactive"

I think you should explicitly check for this instead of using an
exception to control this state. If that is the case, exceptions
thrown from raven are suddenly not that 'important' anymore ...

// Ryan

Bryan Johns

unread,
Apr 19, 2012, 7:36:44 AM4/19/12
to rav...@googlegroups.com
I agree that generally, exceptions should not be used as a flow control
system. The topic of when it's appropriate to check-for and prevent vs.
catching exceptions can degenerate into a religious debate really quickly
so I won't go there.

What I think the OP is looking for is a way to easily identify exceptions
raised from Raven without sprinkling nested try-catch blocks all over his
code so that he can treat them differently in some way. This capability
has value even if it's nothing more than logging and re-throwing them.

--
Bryan Johns
K4GDW

"Where is the wisdom?
Lost in the knowledge.
Where is the knowledge?
Lost in the information. -- T. S. Eliot

Where is the information?
Lost in the data.
Where is the data?
Lost in the ***** database!-- Joe Celko"
(http://www.sqlservercentral.com/articles/Database+Design/72612/)

> -----Original Message-----
> From: rav...@googlegroups.com [mailto:rav...@googlegroups.com] On
> Behalf Of Ryan Heath
> Sent: Thursday, April 19, 2012 3:21 AM
> To: rav...@googlegroups.com
> Subject: Re: [RavenDB] Exception Handling in RavenDb UnitOfWork
>
> Looking again at your example, it seems you are struggling with
> exceptions from raven because you use exceptions to control flow?
>
> This example catches InvalidOperation to handle "Tried to create a task
> from an operation marked as inactive"
> I think you should explicitly check for this instead of using an
> exception to control this state. If that is the case, exceptions thrown
> from raven are suddenly not that 'important' anymore ...
>
> // Ryan
>
> On Tue, Apr 17, 2012 at 7:50 PM, aa <albert...@gmail.com> wrote:
> > This is one example of session usage:
> >
> > ---------------------------------------------------------------------
> -
> > ---------------------------------------------------------------------
> -
> > ----------
> >
> >

> > á[HttpOperation(HttpMethod.POST, ForUriName = "Execute")]
> >
> > á á á á public OperationResult Execute(String operationId)
> >
> > á á á á {
> >
> > á á á á á á _logger.Info(String.Format
> >
> > á á á á á á á á ("Operation Manager: Adding a new task to the


> database
> > for Operation with Id ({0})...",
> >

> > á á á á á á á á operationId));
> >
> >
> > á á á á á á var sessioná=á_documentStore.OpenSession();
> >
> > á á á á á á try
> >
> > á á á á á á {
> >
> > á á á á á á á á Operation operation =
> > session.Get<Operation>(operationId);
> >
> > á á á á á á á á Task task =ásession.CreateTaskFromOperation();
> >
> >
> > á á á á á á á áásession.Store(task);
> >
> >
> > á á á á á á á áásession.SaveChanges();
> >
> >
> > á á á á á á á á //Initiate Saga to control the task execution
> lifetime
> >
> > á á á á á á á á Bus.SendLocal(new BeginTask() { TaskId = task.Id });
> >
> >
> >
> > á á á á á á á á TaskDto taskDto = Mapper.Map<Task, TaskDto>(task);
> >
> >
> > á á á á á á á á return new OperationResult.OK(taskDto);
> >
> >
> > á á á á á á }
> >
> > á á á á á á catch (InvalidOperationException
> > invalidOperationException)
> >
> > á á á á á á {
> >
> > á á á á á á á á //Tried to create a task from an operation marked as
> > inactive
> >
> >
> >
> _logger.Error(invalidOperationException.Message,invalidOperationExcept
> > ion);
> >
> > á á á á á á á á return new OperationResult.BadRequest()
> >
> > áá á á á á á á á á á á á á {
> >
> > áá á á á á á á á á á á á á á á ResponseResource =
> >
> > áá á á á á á á á á á á á á á á á á new RestApiErrorReply()
> >
> > áá á á á á á á á á á á á á á á á á á á {
> >
> > áá á á á á á á á á á á á á á á á á á á á á MainDescription =
> > invalidOperationException.Message,
> >
> > áá á á á á á á á á á á á á á á á á á á á á ExceptionDetail =
> > invalidOperationException.ToString()
> >
> > áá á á á á á á á á á á á á á á á á á á }
> >
> > áá á á á á á á á á á á á á };
> >
> >
> >
> > á á á á á á }
> >
> >
> >
> > á á á á á á finally
> >
> > á á á á á á {
> >
> > á á á á á á á á session.Dispose();
> >
> > á á á á á á }
> >
> > á á á á }
> >
> > ---------------------------------------------------------------------
> -
> > ---------------------------------------------------------------------
> -
> > ---------------------------------------------------------------------
> -


> > ---------------
> >
> >
> > I need to catch DB related errors somewhere, I need to catch them NOT
> > to recover from them , I need to catch them to be able to LOG them
> and

> > send that proper error reply back. áIf I dont catch them there will


> be
> > an unhandled exception and the application will crash.
> >
> > This is not running on IIS, is a Windows service and the DB is
> Embedded.
> >
> > On Tuesday, April 17, 2012 12:40:52 PM UTC-5, Ryan Heath wrote:
> >>
> >> Could you post an example how you recover from session exceptions
> >> right now in your code?
> >>
> >> // Ryan
> >>

> >> On Tue, Apr 17, 2012 at 7:28 PM, aa áwrote:


> >>
> >>
> >> > If I dont catch these exceptions the application crashes.

> áCatching
> >> > (Exception) is way to broad. áThere must be a middle ground.

> >> >>>>> that Raven throws, it would be reallyácumbersomeáto catch all

Ryan Heath

unread,
Apr 19, 2012, 7:52:53 AM4/19/12
to rav...@googlegroups.com
Trying to not turn this into a religious debat;
IMO a generic ravendb exception has low value, it could hide exceptions
you really do not want to shove under an generic exception.
So to help the OP, I am trying to see what is really hurting. Based on
his single example
it seems he is depending on certain exceptions to be thrown.
This is a problem when those exceptions could be thrown from
*everywhere*, hence his urge to catch *raven* exceptions ...

// Ryan

aa

unread,
Apr 19, 2012, 10:52:43 AM4/19/12
to rav...@googlegroups.com
inline


On Thursday, April 19, 2012 2:16:35 AM UTC-5, Oren Eini wrote:
Really?
What happen when we get a OutOfMemoryException ? Should we wrap it?


No, Raven should only wrap the ones related to Raven itself, the ones you outlined before:

* Network errors - server unreachable, etc.
* Concurrency errors - someone modified the doc
* Just plain errors - you tried to do something not possible. (document key too long, etc).

 
In general, you are working with persistent storage, you have very few options of how to actually handle an error. Basically, abort the transaction and try again.

We dont want to handle the error, we want to abort the transaction and try again, to do that I need to catch the exceptions, otherwise the entire app will crash.

So far the proposed solutions I see are:

1) Catch (Exception e) - Problem: You are catching things that you dont want to catch (ie. OutOfMemoryException)
2) Dont catch anything - Problem: App Crashes
3) Do a lot of Inner  and Nested Try Catch blocks - Problem: Ugly Code


How do you handle this?

 
If you have expected errors in the model, you catch and handle that, but that is about it. 
You shouldn't be trying to handle ravendb errors, you just don't have a way to do that, in most scenarios.

Chris Marisic

unread,
Apr 19, 2012, 11:05:02 AM4/19/12
to rav...@googlegroups.com


On Thursday, April 19, 2012 10:52:43 AM UTC-4, aa wrote:


How do you handle this?


Honestly you don't. You log it and move on.

If you want to truly be able "handle" real exceptions you want to look at using an Event Store and/or a Enterprise Service Bus so that you can reply full sequences, send correcting messages etc. If you don't want or can't have a ESB and/or an Event store you're tilting at windmills.

aa

unread,
Apr 19, 2012, 11:37:58 AM4/19/12
to rav...@googlegroups.com
inline


On Thursday, April 19, 2012 10:05:02 AM UTC-5, Chris Marisic wrote:


On Thursday, April 19, 2012 10:52:43 AM UTC-4, aa wrote:


How do you handle this?


Honestly you don't. You log it and move on.


This is exactly what I want, to log it and move on, but how can I log it if I dont catch the exception.  If I dont catch any exception the application will crash.
 
If you want to truly be able "handle" real exceptions you want to look at using an Event Store and/or a Enterprise Service Bus so that you can reply full sequences, send correcting messages etc. If you don't want or can't have a ESB and/or an Event store you're tilting at windmills.

I dont want to recover from the Raven Exceptions, I just want to catch them, log them, discard the session, and move on. 

Troy

unread,
Apr 19, 2012, 11:47:53 AM4/19/12
to rav...@googlegroups.com
it seems you would want to distinguish Raven exceptions... even if just one wrapper exception to a general exception. I mean let's say your raven server went down.. or disk full or something. Instead of just log it and move on... it would be nice to escalate that kind of exception to have someone on call to look at the server to make sure something is not permanently broken. For the most part, log exception and move on is the way you would want to go... but in cases were your database is down, that is pretty critical to know that immediately.
 
How do people handle catastrophic raven errors that really need a set of eyes on it...

Bryan Johns

unread,
Apr 19, 2012, 4:58:49 PM4/19/12
to rav...@googlegroups.com

I would hope for a way to distinguish the difference between those
types of errors that could be fixed by an administrator so that I
could send a notification then display a friendly, apologetic message
to the user. Others errors that really can't be recovered from should
be logged, the session disposed, then either retry or move on to the
next transaction with a new session.

Reply all
Reply to author
Forward
0 new messages