You should call TryDisposeUoW in one place, Message Completed.This is called if there is a error or not, and intended for cleanup operations.
On Mon, Jun 1, 2009 at 7:39 PM, Mike Nichols <[email protected]>wrote: > 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.IEnlistmentNotification.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 -~----------~----~----~----~------~----~------~--~---
