RSB + NHibernate + Transactions

31 views
Skip to first unread message

Kenny Eliasson

unread,
Sep 19, 2011, 9:52:51 AM9/19/11
to Rhino Tools Dev
Hi there!

When I send my messages to the queue I include a JobLogId which I
later on uses in my consumer for loading a entity from NHibernate. I
then change the status of the job to processing, starts the invoke the
Consume-method that can take everywhere from 1 second to 10 minutes
and after the job is done I once again load the entity and mark it as
done.

The problem is that it seems the DTC-transaction is locking the table,
so that I can't query on it.

I'm using the following code

public void Consume(T message)
{

JobLog jobLog;
using (var session = _sessionFactory.OpenSession()){
//using (var tx = session.BeginTransaction()) {
jobLog = session.Get<JobLog>(message.JobLogId);
jobLog.Processing();
//tx.Commit();
session.Flush();
}

_logger.InfoFormat("Starting processing message of type
{0}.\nMessage data: {1}", jobLog.MessageType, jobLog.MessageData);

try {
Consume(message, _logger);
} catch (Exception e) {
_logger.Fatal(string.Format("The consumer {0} threw an
exception", this.GetType().Name), e);
using (var session = _sessionFactory.OpenSession())
using (var tx = session.BeginTransaction()) {
jobLog = session.Get<JobLog>(message.JobLogId);
jobLog.Crashed();
tx.Commit();
}
return;
}

_logger.InfoFormat("Done processing message of type {0}",
message.GetType().Name);

using (var session = _sessionFactory.OpenSession()) {
//using (var tx = session.BeginTransaction()) {
jobLog = session.Get<JobLog>(message.JobLogId);
jobLog.Done();
//tx.Commit();
session.Flush();
}
}

As you see I've tried to comment out the transaction parts of
NHibernate without success.

Is there a way to solve this without locking the whole table? Is there
a better way of logging then doing it in the consume-method? Any
thoughts?

Tom Cabanski

unread,
Sep 19, 2011, 11:05:46 PM9/19/11
to Rhino Tools Dev

I think you need to begin the transaction before you open the
session. If you don't, nhibernate does not enlist. It's been a
little while since I used nhibernate so I am not positive.

Kenny Eliasson

unread,
Sep 20, 2011, 2:34:42 AM9/20/11
to Rhino Tools Dev
I've found 3 other solutions to my problem

1) Use a logEndpoint and a another server that listens to start and
end messages.
2) Nest my code in a TransactionScope and Suppress it.
3) In the config use consumeInTransaction="false"

Iäve tried option 2 and 3 with success, but I think it most be
drawbacks to using them that I'm not aware of.

Corey Kaylor

unread,
Sep 20, 2011, 12:25:00 PM9/20/11
to rhino-t...@googlegroups.com
I would recommend implementing an IMessageModule to manage the session lifecycle. You shouldn't need to open the session directly from the ISessionFactory in consumers. Rather, have the ISession injected. There are event hooks on ITransport that will allow you to commit / dispose accordingly. This article covers the subject a bit.

http://msdn.microsoft.com/en-us/magazine/ff796225.aspx

--
You received this message because you are subscribed to the Google Groups "Rhino Tools Dev" group.
To post to this group, send email to rhino-t...@googlegroups.com.
To unsubscribe from this group, send email to rhino-tools-d...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rhino-tools-dev?hl=en.


Kenny Eliasson

unread,
Sep 21, 2011, 9:27:38 AM9/21/11
to Rhino Tools Dev
I've read through the article and implemented the MessageModule.
Solved a lot of problem but still, it locks the tables when executing
the Consume-method.
I solved it using consumeInTransaction="false" but it seems that the
RSB transaction is using some strange IsolationLevel.


On Sep 20, 6:25 pm, Corey Kaylor <co...@kaylors.net> wrote:
> I would recommend implementing an IMessageModule to manage the session
> lifecycle. You shouldn't need to open the session directly from the
> ISessionFactory in consumers. Rather, have the ISession injected. There are
> event hooks on ITransport that will allow you to commit / dispose
> accordingly. This article covers the subject a bit.
>
> http://msdn.microsoft.com/en-us/magazine/ff796225.aspx
>

Jason Meckley

unread,
Sep 21, 2011, 10:29:09 AM9/21/11
to rhino-t...@googlegroups.com
I wouldn't recommend disabling transactions. you can set the transaction level in the RSB configuration. the default is IsolationLevel.Serializable.
There are a number of reasons why you are receiving dead locks. I would even say is common when devs first start using the Serializable isolation level. How you resolve this becomes an art. typically querying less data & issuing fewer queries helps alleviate the problem.

For example I used to get errors and warnings when lazy loading data within a consumer. eager loading the data and using NH Futures to reduce the number of remote DB calls resolved the issue. The next step is query a projection of the required data. I haven't needed this, but would be required if the entities were very large and only a subset of information was used in the actual consumer.

Kenny Eliasson

unread,
Sep 22, 2011, 10:05:54 AM9/22/11
to Rhino Tools Dev
I've actually changed the IsolationLevel to ReadCommitted, even tried
ReadUnCommitted but the same problems still occur.

nightwatch77

unread,
Oct 3, 2011, 9:01:25 AM10/3/11
to Rhino Tools Dev
How is it possible that the transaction does not time-out? By default
the DTC transaction should be completed in 30 secs and it won't commit
if kept open for a longer time. Did you consider that?
For the locking problem I'd recommend splitting the work into a
sequence of 3 operations executed in separate transactions:
1. Get the job details from the database
2. Run the job
3. Update the job information in the database
Use RSB to execute them in sequence (by publishing a message at the
end of each step that will initiate the next step).

RG
Reply all
Reply to author
Forward
0 new messages