here is my unit of work module MessageProcessingCompleted handler. Placing
this in MessageProcessingFailure handler seems to fix matters.
NHProf was showing me that all the DTC errors were in the same session but I
didn't think about the problems that creates. Let me be a little cheesy and
say that if I didn't have NHProf showing me such clear boundaries on events
I really am not sure how I was going to track this sucka down.

Anyways, here is my new UnitOfWorkMessageModule for future victims of
transaction torture :)
public class UnitOfWorkMessageModule : IMessageModule
    {

        public void Init(ITransport transport)
        {
            transport.MessageArrived += transport_MessageArrived;
            transport.MessageProcessingCompleted +=
transport_MessageProcessingCompleted;
            transport.MessageProcessingFailure +=
transport_MessageProcessingFailure;
        }

        private void
transport_MessageProcessingFailure(CurrentMessageInformation information,
Exception exception)
        {
            TryDisposeUnitOfWork();

            if (exception != null)
            {
                Log.For(this).Error(exception.ToString());
            }

        }

        public void Stop(ITransport transport)
        {
            transport.MessageArrived -= transport_MessageArrived;
            transport.MessageProcessingCompleted -=
transport_MessageProcessingCompleted;
            transport.MessageProcessingFailure -=
transport_MessageProcessingFailure;
        }


        bool transport_MessageArrived(CurrentMessageInformation arg)
        {
            UnitOfWork.Start();
            return false;
        }

        void transport_MessageProcessingCompleted(CurrentMessageInformation
obj, Exception exception)
        {
            if(exception!=null)
            {
                Log.For(this).Error("An error occured on message process
completion:" + exception);
            }
            else
            {
                TryDisposeUnitOfWork();
            }



        }
        private void TryDisposeUnitOfWork()
        {
            try
            {
                if (UnitOfWork.IsStarted)
                {
                    UnitOfWork.Current.Dispose();
                }
            }
            catch (Exception ex)
            {
                Log.For(this).Error(ex.ToString());
                throw;
            }
        }
    }
On Mon, Jun 1, 2009 at 4:11 PM, Ayende Rahien <[email protected]> wrote:

> Oh, that is probably it! You are not properly disposing the session in
> that scenario?
> Don't forget that if a message is failing, it is going to get retried.
>
> On Mon, Jun 1, 2009 at 7:08 PM, Mike Nichols <[email protected]>wrote:
>
>> The test I wrote passes (unfortunately). I just can't figure out why NH
>> would try to insert again after the previous message was failed, almost like
>> the session wasn't disposed in my UnitOfWork module after the  previous
>> message was moved to the error queue...Let me check to make sure the message
>> module MessageProcesSINGCompleted event is getting fired after a transaction
>> failure....
>>   On Mon, Jun 1, 2009 at 4:03 PM, Ayende Rahien <[email protected]>wrote:
>>
>>> Hm, can you try to write a test that reproduce this?
>>>
>>>
>>> On Mon, Jun 1, 2009 at 6:55 PM, Mike Nichols 
>>> <[email protected]>wrote:
>>>
>>>>
>>>> Yes I see all those passing. Here's what's happening:
>>>> 1. Send good message and all works
>>>> 2. Send message that causes transaction exception (as before) :: App
>>>> doesn't crash now (yeaay!)
>>>> 3. Send good message : Message is received (therefore the transport is
>>>> still alive even after previous exception) but before it is processed
>>>> the previous bad transaction is attempted ONCE and so the whole
>>>> processing fails...almost like the transaction wasn't disposed after
>>>> the error queue handling was done. Interestingly, NHProf isn't showing
>>>> all this in a new session either, but not sure that is truthful or
>>>> not. The 'Error DTC Transaction prepre  (sic) phase failed' error pops
>>>> up BEFORE the other message has a chance to fully process.
>>>> Oi-vey
>>>>
>>>>
>>>> On Jun 1, 2:57 pm, Ayende Rahien <[email protected]> wrote:
>>>> > Hm, can you look at the tests that I committed, they are testing that
>>>> the
>>>> > message arrive in the error queue.
>>>> >
>>>> > On Mon, Jun 1, 2009 at 4:05 PM, Mike Nichols <
>>>> [email protected]>wrote:
>>>>  >
>>>> >
>>>> >
>>>> >
>>>> >
>>>> > > Hm...the host isn't crashing now but the message that causes the
>>>> error
>>>> > > seems not to be moving to the errors queues, or, the transport is
>>>> > > shutting down. i can't tell yet.
>>>> >
>>>> > > On Jun 1, 11:01 am, Ayende Rahien <[email protected]> wrote:
>>>> > > > Okay, this is now fixed, have fun :-)
>>>> >
>>>> > > > On Mon, Jun 1, 2009 at 1:40 PM, Mike Nichols <
>>>> [email protected]
>>>> > > >wrote:
>>>> >
>>>> > > > > Man thanks Ayende
>>>> >
>>>> > > > > On Mon, Jun 1, 2009 at 10:13 AM, Ayende Rahien <
>>>> [email protected]>
>>>> > > wrote:
>>>> >
>>>> > > > >> Yes, I can.You need to use IEnlistmentNotification impl and
>>>> register
>>>> > > that
>>>> > > > >> on the TX.
>>>> > > > >> I'll have a fix shortly
>>>> >
>>>> > > > >> On Mon, Jun 1, 2009 at 1:01 PM, Mike Nichols <
>>>> > > [email protected]>wrote:
>>>> >
>>>> > > > >>> Are you able to test this without all the NH dependency cruft?
>>>> I want
>>>> > > to
>>>> > > > >>> keep hacking on this but can't figure out how to write a
>>>> lighter test
>>>> > > for it
>>>> > > > >>> to stick in RSB code base. Just throwing TransactionExceptions
>>>> > > doesn't work
>>>> >
>>>> > > > >>> On Mon, Jun 1, 2009 at 8:12 AM, Ayende Rahien <
>>>> [email protected]>
>>>> > > wrote:
>>>> >
>>>> > > > >>>> Sigh,
>>>> > > > >>>> I reproduced the issue, not fun.
>>>> >
>>>> > > > >>>> On Mon, Jun 1, 2009 at 1:29 PM, Mike Nichols <
>>>> > > [email protected]>wrote:
>>>> >
>>>> > > > >>>>> I think I have narrowed the problem to transaction commit
>>>> failures
>>>> > > > >>>>> that take place AFTER the 'Consume' invocation is made (ie
>>>> in
>>>> > > > >>>>> MessageCompleted). These exceptions are outside the scope of
>>>> the
>>>> > > > >>>>> try...catch wrapping the ProcessMessage call.
>>>> > > > >>>>> In the code I have above the transaction tries to commit (in
>>>> ATM)
>>>> > > and
>>>> > > > >>>>> then rolls back since SQL Server throws, aborting the
>>>> transaction.
>>>> > > > >>>>> When the TransactionScope attempts to Dispose in RQTransport
>>>> > > > >>>>> ReceiveMessage method, the InternalTransaction throws since
>>>> it was
>>>> > > > >>>>> aborted, making the whole app crash since it is unhandled.
>>>> >
>>>> > > > >>>>> Any ideas on this? My attempts to handle
>>>> TransactionException have
>>>> > > so
>>>> > > > >>>>> far just thrown RSB into a loop.
>>>> >
>>>> > > > >>>>> On May 30, 11:41 pm, Mike Nichols <[email protected]
>>>> >
>>>> > > wrote:
>>>> > > > >>>>> > I have a test case here that demonstrates the problem. I
>>>> changed
>>>> > > > >>>>> > RQTransport on ReceiveMessage to catch
>>>> > > TransactionAbortedException
>>>> > > > >>>>> and
>>>> > > > >>>>> > Debugger.Launch inside the catch statement.
>>>> > > > >>>>> > Note this test is 'heavy' intentionally . RSB tries 5
>>>> times to
>>>> > > handle
>>>> > > > >>>>> > the message but when the Session flushes the transaction
>>>> aborts
>>>> > > and
>>>> > > > >>>>> > since the transaction reference is outside the try...catch
>>>> in
>>>> > > > >>>>> > ReceiveMessage it goes unhandled.
>>>> >
>>>> > > > >>>>> > First here is output from NHProf Error logging:
>>>> > > > >>>>> > It seems like since the exception is thrown inside the
>>>> > > > >>>>> > TransactionScope (tx) instead the inner try..catch is
>>>> ignored and
>>>> > > > >>>>> > hence the TransactionAbortedException goes unhandled in
>>>> the app,
>>>> > > > >>>>> > stopping it altogether.
>>>> >
>>>> > > > >>>>> > ERROR:
>>>> > > > >>>>> > DTC transaction prepre phase failed
>>>> > > > >>>>> > System.Data.SqlTypes.SqlTypeException: SqlDateTime
>>>> overflow. Must
>>>> > > be
>>>> > > > >>>>> > between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
>>>> > > > >>>>> >    at
>>>> System.Data.SqlTypes.SqlDateTime.FromTimeSpan(TimeSpan
>>>> > > value)
>>>> > > > >>>>> >    at
>>>> System.Data.SqlTypes.SqlDateTime.FromDateTime(DateTime
>>>> > > value)
>>>> > > > >>>>> >    at System.Data.SqlClient.MetaType.FromDateTime(DateTime
>>>> > > dateTime,
>>>> > > > >>>>> > Byte cb)
>>>> > > > >>>>> >    at System.Data.SqlClient.TdsParser.WriteValue(Object
>>>> value,
>>>> > > > >>>>> > MetaType type, Byte scale, Int32 actualLength, Int32
>>>> > > > >>>>> encodingByteSize,
>>>> > > > >>>>> > Int32 offset, TdsParserStateObject stateObj)
>>>> > > > >>>>> >    at
>>>> System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[]
>>>> > > > >>>>> > rpcArray, Int32 timeout, Boolean inSchema,
>>>> SqlNotificationRequest
>>>> > > > >>>>> > notificationRequest, TdsParserStateObject stateObj,
>>>> Boolean
>>>> > > > >>>>> > isCommandProc)
>>>> > > > >>>>> >    at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds
>>>> > > > >>>>> > (CommandBehavior cmdBehavior, RunBehavior runBehavior,
>>>> Boolean
>>>> > > > >>>>> > returnStream, Boolean async)
>>>> > > > >>>>> >    at System.Data.SqlClient.SqlCommand.RunExecuteReader
>>>> > > > >>>>> > (CommandBehavior cmdBehavior, RunBehavior runBehavior,
>>>> Boolean
>>>> > > > >>>>> > returnStream, String method, DbAsyncResult result)
>>>> > > > >>>>> >    at
>>>> System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery
>>>> > > > >>>>> > (DbAsyncResult result, String methodName, Boolean
>>>> sendToPipe)
>>>> > > > >>>>> >    at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
>>>> > > > >>>>> >    at
>>>> > > NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand
>>>> > > > >>>>> > cmd)
>>>> > > > >>>>> >    at
>>>> > > NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation
>>>> > > > >>>>> > expectation)
>>>> > > > >>>>> >    at
>>>> NHibernate.Persister.Entity.AbstractEntityPersister.Insert
>>>> > > > >>>>> > (Object id, Object[] fields, Boolean[] notNull, Int32 j,
>>>> > > > >>>>> > SqlCommandInfo sql, Object obj, ISessionImplementor
>>>> session)
>>>> > > > >>>>> >    at
>>>> NHibernate.Persister.Entity.AbstractEntityPersister.Insert
>>>> > > > >>>>> > (Object id, Object[] fields, Object obj,
>>>> ISessionImplementor
>>>> > > session)
>>>> > > > >>>>> >    at NHibernate.Action.EntityInsertAction.Execute()
>>>> > > > >>>>> >    at NHibernate.Engine.ActionQueue.Execute(IExecutable
>>>> > > executable)
>>>> > > > >>>>> >    at NHibernate.Engine.ActionQueue.ExecuteActions(IList
>>>> list)
>>>> > > > >>>>> >    at NHibernate.Engine.ActionQueue.ExecuteActions()
>>>> > > > >>>>> >    at
>>>> >
>>>> > >
>>>> NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions
>>>> > > > >>>>> > (IEventSource session)
>>>> > > > >>>>> >    at
>>>> NHibernate.Event.Default.DefaultFlushEventListener.OnFlush
>>>> > > > >>>>> > (FlushEvent event)
>>>> > > > >>>>> >    at NHibernate.Impl.SessionImpl.Flush()
>>>> > > > >>>>> >    at
>>>> >
>>>> > >
>>>> NHibernate.Impl.AbstractSessionImpl.System.Transactions.IEnlistmentNotifica­tion.Prepare
>>>>  > > > >>>>> > (PreparingEnlistment preparingEnlistment)
>>>> >
>>>> > > > >>>>> > TEST
>>>> >
>>>> > >
>>>> ===========================================================================­===
>>>> > > > >>>>> > I am attaching it here in the Files section to make it
>>>> easier to
>>>> > > > >>>>> read,
>>>> > > > >>>>> > too:
>>>> >
>>>> > > > >>>>> > namespace Cei.MaterialsTesting.Bugs
>>>> > > > >>>>> > {
>>>> > > > >>>>> >         public class RSBBugs :
>>>> > > > >>>>> > OccasionalConsumerOf<RSBBugs.ConsumerCompleteMessage>
>>>> > > > >>>>> >         {
>>>> > > > >>>>> >                 private WindsorContainer container;
>>>> > > > >>>>> >                 private ManualResetEvent wait;
>>>> >
>>>> > > > >>>>> >                 public RSBBugs()
>>>> > > > >>>>> >                 {
>>>> > > > >>>>> >                         container = new
>>>> WindsorContainer(new
>>>> > > > >>>>> XmlInterpreter("Bugs/
>>>> > > > >>>>> > RSBBugs.config"));
>>>> > > > >>>>> >                         container.AddFacility("rhino.esb",
>>>> new
>>>> > > > >>>>> RhinoServiceBusFacility
>>>> > > > >>>>> > ().AddMessageModule<UnitOfWorkMessageModule>());
>>>> > > > >>>>> >                         container.AddFacility("trx", new
>>>> > > > >>>>> RhinoTransactionFacility());
>>>> > > > >>>>> >                         container.AddFacility("nh_uow",
>>>> new
>>>> > > > >>>>> NHibernateUnitOfWorkFacility(
>>>> >
>>>> > > new
>>>> > > > >>>>> > NHibernateUnitOfWorkFacilityConfig(
>>>> >
>>>> > > > >>>>>   Assembly.GetAssembly(typeof
>>>> > > > >>>>> > (BadDateEntity)))));
>>>> >
>>>> > > container.AddComponent<SQLMessageConsumer>();
>>>> >
>>>> > > > >>>>> >                         var cfg = new Configuration()
>>>> >
>>>> > > > >>>>> .SetProperty(Environment.ReleaseConnections, "on_close")
>>>> > > > >>>>> >
>>>> .SetProperty(Environment.Dialect,
>>>> > > > >>>>> > "NHibernate.Dialect.MsSql2005Dialect")
>>>> >
>>>> > > > >>>>> .SetProperty(Environment.ConnectionDriver,
>>>> > > > >>>>> > "NHibernate.Driver.SqlClientDriver")
>>>> >
>>>> > > > >>>>> .SetProperty(Environment.ConnectionString,
>>>>  string.Format("Server=
>>>> > > > >>>>> > (local);initial catalog={0};Integrated Security=SSPI",
>>>> > > > >>>>> > "MaterialsTesting_Test"))
>>>> >
>>>> > > > >>>>> .SetProperty(Environment.ProxyFactoryFactoryClass, typeof
>>>> > > > >>>>> > (ProxyFactoryFactory).AssemblyQualifiedName)
>>>> > > > >>>>> >
>>>> .SetProperty(Environment.ShowSql,
>>>> > > > >>>>> "true")
>>>> >
>>>> > > .SetProperty(Environment.BatchSize,
>>>> > > > >>>>> "10")
>>>> >
>>>> > > > >>>>> .SetProperty(Environment.GenerateStatistics, "true");
>>>> >
>>>> > > cfg.AddXmlFile("Bugs/BadDateEntity.hbm.xml");
>>>> >
>>>> > > > >>>>> >                         SessionFactory =
>>>> > > cfg.BuildSessionFactory();
>>>> > > > >>>>> >                         using (var session =
>>>> > > > >>>>> SessionFactory.OpenSession())
>>>> > > > >>>>> >                         {
>>>> > > > >>>>> >                                 new
>>>> > > SchemaExport(cfg).Execute(false,
>>>> > > > >>>>> true, false,
>>>> > > > >>>>> > session.Connection, null);
>>>> > > > >>>>> >                         }
>>>> > > > >>>>> >                         IoC.Initialize(container);
>>>> > > > >>>>> >                 }
>>>> >
>>>> > > > >>>>> >                 protected ISessionFactory SessionFactory {
>>>> get;
>>>> > > set;
>>>> > > > >>>>> }
>>>> >
>>>> > > > >>>>> >                 [Observation]
>>>> > > > >>>>> >                 public void should_throw()
>>>> > > > >>>>> >                 {
>>>> > > > >>>>> >                         using(var bus =
>>>> > > > >>>>> container.Resolve<IStartableServiceBus>())
>>>> > > > >>>>> >                         {
>>>> > > > >>>>> >                                 bus.Start();
>>>> >
>>>> > > > >>>>> using(bus.AddInstanceSubscription(this))
>>>> > > > >>>>> >                                 {
>>>> > > > >>>>> >                                         wait = new
>>>> > > > >>>>> ManualResetEvent(false);
>>>> > > > >>>>> >                                         bus.Send(new
>>>> > > SQLMessage());
>>>> >
>>>> > > > >>>>> Assert.True(wait.WaitOne(TimeSpan.FromSeconds(60),false));
>>>> > > > >>>>> >                                 }
>>>> > > > >>>>> >                         }
>>>> >
>>>> > ...
>>>> >
>>>> > read more »- Hide quoted text -
>>>> >
>>>> > - Show quoted text -
>>>>
>>>>
>>>
>>>
>
> >
>

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

Reply via email to