Question

I have an agent that employs a saga to track incoming messages of a given type until a Timeout message is received. In the Timeout handler, I have the following:

public override void Timeout(object state)
        {
            // If Data.IsNull: Do nothing!!! Report to log only.
            Logger.Debug("=========================================================================");
            Logger.Debug(string.Format("Timeout message received. State: {0}.", state.ToString()));

            QuickBatch qbBuilder = new QuickBatch();
            // Create new message and publish it
            BankRequestBatchClosed eventMessage = Bus.CreateInstance<BankRequestBatchClosed>();

            eventMessage.UniqueBatchIdentifier = qbBuilder.GenerateUniqueBatchIdentifier(QuickBatch.QB_BATCHTYPE_CC);
            eventMessage.ScheduleBatchID = this.Data.ScheduleBatchID;
            eventMessage.EventDate = DateTime.Now;
            eventMessage.EventID = Guid.NewGuid();
            eventMessage.TransactionItems = this.Data.PaymentRequestedTransactionItems;

            Logger.Debug("=========================================================================");
            Logger.Debug(string.Format("Timeout method about to send BankRequestBatchClosed message. UniqueBatchIdentifier: {0}",eventMessage.UniqueBatchIdentifier));

            Bus.Publish(eventMessage);
            Complete();
        }

TransactionItems is an ICollection

Here is TransactionDetail class:

   [Serializable]
    public class TransactionDetail
    {
        // Guid needed for NHibernate to store it in database. All
        // member variables are virtual for the same reason.
        public virtual Guid Id { get; set; }
        public virtual Int32 ScheduleBatchID { get; set; }
        public virtual Int32 PseudoSagaID { get; set; }
        public virtual String CreditCardNumber { get; set; }
        public virtual String ExpiryDate { get; set; }

        public virtual String AccountNumber { get; set; }
        public virtual String BSB { get; set; }

        public virtual Decimal Amount { get; set; }
        public virtual Int32 Firm_fk { get; set; }
        public virtual String FirmName { get; set; }
        public virtual TransactionType PaymentType { get; set; }
        // transaction number, max 15 chars, to use one of the following:
        public virtual int ApplicationPaymentInfo_fk { get; set; }
        public virtual BankRequestResponseSagaBase Parent { get; set; }
    }

If I don't have any subscriptions in place, the Bus.Publish() call goes through fine. If I have another service subscribed to it, I get the following error message:

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

There is no further information on the overflow other than this: {Cannot evaluate expression because the current thread is in a stack overflow state.}

I have my own SagaPersister, Profile and SagaRegistry but not sure if they are pertinent to this issue but can supply them if needed.

Was it helpful?

Solution

The one thing that looks a bit suspicious here is the BankRequestResponseSagaBase Parent property on your TransactionDetail object. It could be that you have a loop in your references that is causing your saga persister to blow up.

OTHER TIPS

A stack overflow is usually caused by re-entrant code (a method that calls itself, possibly indirectly. Each time a method is called, it uses a bit more space on the stack, so if it calls itself it can create an infinite loop which uses up all the stack).

It is possible there is a bug in .net, but it's more likely that your TimeOut event handler is calling something that causes another call to Timeout while it is still processing the first one, which causes another call, and so on, causing an infinite loop. Are you getting an enormous amount of "Timeout received" text in your output log?

Ways to avoid this situation are:

  • Don't make the calls that cause the reentrancy
  • remove the event subscription in your event handler before it does any work (and possibly resubscribe it on exit from your handler)
  • use a bool variable or other interlock to detect re-entrant calls
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top