Enlisting a transaction after a retry

86 na pagtingin
Lumaktaw sa unang mensaheng hindi pa nababasa

Adrian Wright

hindi pa nababasa,
Set 26, 2017, 3:30:08 PM9/26/17
para kaymasstransit-discuss
I am using the TransactionContext to enlist the MassTransit transaction into EntityFramework. However, I find that in a retry scenario, TransactionContext.Transaction throws an ObjectDisposedException. Is there a way to reset the transaction so that I get a new transaction when the retry is processed?

Here are my transactionality settings:
                Timeout = new TimeSpan(0, 0, 90),
               
IsolationLevel = IsolationLevel.ReadCommitted

And the retry settings:
                e.UseRetry(x => x.Interval(5, new TimeSpan(0, 0, 30)));

Here's the error:
Cannot access a disposed object.
Object name: 'Transaction'.
   at System.Transactions.Transaction.get_IsolationLevel()
   at System.Data.SqlClient.SqlDelegatedTransaction..ctor(SqlInternalConnection connection, Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SqlClient.SqlConnection.EnlistTransaction(Transaction transaction)

Chris Patterson

hindi pa nababasa,
Set 26, 2017, 8:32:30 PM9/26/17
para kaymasstrans...@googlegroups.com
Is the transaction middleware configured after the retry middleware?


--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstransit-discuss@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/masstransit-discuss/19ebdb73-f7b0-4633-9509-fc63fc335b24%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Adrian Wright

hindi pa nababasa,
Set 27, 2017, 8:57:24 AM9/27/17
para kaymasstransit-discuss
I've tried changing the sequence in which the retries and transactions are configured, but it doesn't seem to impact the result.

Here's the full configuration code. This may be a red herring, but you'll notice I made a new implementation of ITransactionConfigurator. If there's a native MassTransit type that I should use in its place, let me know. 
   


public class BusConfigurator
   
{
       
public static void ConfigureBus(IKernel kernel)
       
{
           
string url = ConfigurationManager.AppSettings["rabbitUrl"];
           
string workflowsQueueName = ConfigurationManager.AppSettings["workflowsQueueName"];


           
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
           
{
               
var host = config.Host(new Uri(url), hst =>
               
{
                    hst
.Username("guest");
                    hst
.Password("guest");
               
});


               
ConfigureConsumer<WorkflowMessageConsumer>(host, workflowsQueueName, config, kernel);
                config
.UseRetry(x => x.Interval(5, new TimeSpan(0, 0, 30)));


                config
.UseNLog();
           
});


           
var observer = new ReceiveObserver();
            bus
.ConnectReceiveObserver(observer);


           
var consumeObserver = new ConsumeObserver();
            bus
.ConnectConsumeObserver(consumeObserver);


            bus
.Start();
            kernel
.Bind<IBus>().ToConstant(bus);
       
}


       
private static void ConfigureConsumer<T>(IRabbitMqHost host, string queueName,
           
IRabbitMqBusFactoryConfigurator cfg, IKernel kernel) where T : class, IConsumer
       
{
            cfg
.ReceiveEndpoint(host, queueName, e =>
           
{
                e
.UseTransaction(x => GetTransactionConfigurator());
                e
.Consumer<T>(kernel);
           
});
       
}


       
private static ITransactionConfigurator GetTransactionConfigurator()
       
{
           
return new MyTransactionConfigurator
           
{

               
Timeout = new TimeSpan(0, 0, 90),
               
IsolationLevel = IsolationLevel.ReadCommitted

           
};
       
}
   
}


   
public class MyTransactionConfigurator : ITransactionConfigurator
   
{
       
public TimeSpan Timeout { get; set; }
       
public System.Transactions.IsolationLevel IsolationLevel { get; set; }
   
}





On Tuesday, September 26, 2017 at 8:32:30 PM UTC-4, Chris Patterson wrote:
Is the transaction middleware configured after the retry middleware?

On Tue, Sep 26, 2017 at 12:30 PM, Adrian Wright <adrian...@gmail.com> wrote:
I am using the TransactionContext to enlist the MassTransit transaction into EntityFramework. However, I find that in a retry scenario, TransactionContext.Transaction throws an ObjectDisposedException. Is there a way to reset the transaction so that I get a new transaction when the retry is processed?

Here are my transactionality settings:
                Timeout = new TimeSpan(0, 0, 90),
               
IsolationLevel = IsolationLevel.ReadCommitted

And the retry settings:
                e.UseRetry(x => x.Interval(5, new TimeSpan(0, 0, 30)));

Here's the error:
Cannot access a disposed object.
Object name: 'Transaction'.
   at System.Transactions.Transaction.get_IsolationLevel()
   at System.Data.SqlClient.SqlDelegatedTransaction..ctor(SqlInternalConnection connection, Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SqlClient.SqlConnection.EnlistTransaction(Transaction transaction)

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstrans...@googlegroups.com.

Chris Patterson

hindi pa nababasa,
Set 27, 2017, 10:26:38 AM9/27/17
para kaymasstrans...@googlegroups.com
The retry must be configured before you configure the consumer.

And you must use the configurator passed as X, you can't create your own.



To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsubscribe...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.

Adrian Wright

hindi pa nababasa,
Set 27, 2017, 10:46:26 AM9/27/17
para kaymasstransit-discuss
Thanks, I made the changes suggested, and got the same result. I also tried moving transaction configuration up to the bus level instead of the consumer level, with no change in the result.

Ideas on how to get past this issue?

Here is the bus configuration:

        public static void ConfigureBus(IKernel kernel)
       
{
           
string url = ConfigurationManager.AppSettings["rabbitUrl"];
           
string workflowsQueueName = ConfigurationManager.AppSettings["workflowsQueueName"];


           
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
           
{
               
var host = config.Host(new Uri(url), hst =>
               
{
                    hst
.Username("guest");
                    hst
.Password("guest");
               
});



                config
.UseRetry(x => x.Interval(5, new TimeSpan(0, 0, 30)));
                config
.UseTransaction(x =>
               
{
                    x
.Timeout = TimeSpan.FromSeconds(90);
                    x
.IsolationLevel = IsolationLevel.ReadCommitted;

               
});
               
ConfigureConsumer<WorkflowMessageConsumer>(host, workflowsQueueName, config, kernel);



                config
.UseNLog();

           
});


           
var observer = new ReceiveObserver();
            bus
.ConnectReceiveObserver(observer);



           
var consumeObserver = new ConsumeObserver(kernel.Get<ISurveyService>());

            bus
.ConnectConsumeObserver(consumeObserver);


            bus
.Start();
            kernel
.Bind<IBus>().ToConstant(bus);
       
}


       
private static void ConfigureConsumer<T>(IRabbitMqHost host, string queueName,
           
IRabbitMqBusFactoryConfigurator cfg, IKernel kernel) where T : class, IConsumer
       
{
            cfg
.ReceiveEndpoint(host, queueName, e =>
           
{

                e
.Consumer<T>(kernel);
           
});
       
}
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstrans...@googlegroups.com.

Adrian Wright

hindi pa nababasa,
Set 27, 2017, 12:24:59 PM9/27/17
para kaymasstransit-discuss
Here's a complete recreation of the scenario:


class Program
   
{
       
static void Main(string[] args)
       
{

           
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
           
{

               
var host = config.Host(new Uri("rabbitmq://my-rabbit"), hst =>

               
{
                    hst
.Username("guest");
                    hst
.Password("guest");
               
});
               
                config
.UseRetry(x => x.Interval(5, new TimeSpan(0, 0, 30)));
                config
.UseTransaction(x =>
               
{
                    x
.Timeout = TimeSpan.FromSeconds(90);

                    x
.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;


               
});
                config
.ReceiveEndpoint(host, "testRetry", e =>
               
{
                    e
.Consumer<Consumer>();
               
});


           
});
           
            bus
.Start();




           
var sendToUri = new Uri("rabbitmq://my-rabbit/testRetry");
           
var endPoint = bus.GetSendEndpoint(sendToUri).Result;
            endPoint
.Send<Message>(new Message {Id = Guid.NewGuid()});
       
}


       
public class Consumer : IConsumer<Message>
       
{
           
public Task Consume(ConsumeContext<Message> context)
           
{
               
TransactionContext transactionContext;
                context
.TryGetPayload(out transactionContext);


               
// verify that I have a valid transaction. On the first retry, this line will throw an exception because the transaction has been disposed.
               
var isolationLevel = transactionContext.Transaction.IsolationLevel;


               
throw new Exception("throwing an exception to trigger a retry");
           
}
       
}


       
public class Message
       
{
           
public Guid Id { get; set; }
       
}
   
}

Chris Patterson

hindi pa nababasa,
Set 27, 2017, 6:20:52 PM9/27/17
para kaymasstrans...@googlegroups.com
Are you using a container for your consumer? Windsor? Ah, that sucks. It doesn't create a lifetime scope which pushes the transaction up. So, it's complicated but doable. Ugh, I'll have to think. 

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.

Adrian Wright

hindi pa nababasa,
Set 28, 2017, 8:21:03 AM9/28/17
para kaymasstransit-discuss
Yes, we use Ninject as the DI container. However, the simplified example that I shared in my last post does not use a container and it still experiences the problem. That might be the best place to start.
To post to this group, send email to masstrans...@googlegroups.com.
Tumugon sa lahat
Sumagot sa may-akda
Ipasa
0 bagong mensahe