Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HasActiveTransaction in UnitOfWork class returns true even if the transaction for the current thread has been cleared #17

Closed
jalalr opened this issue Jan 9, 2014 · 28 comments
Assignees
Labels
Milestone

Comments

@jalalr
Copy link

jalalr commented Jan 9, 2014

The use of IsValueCreated is incorrect, because it will return true regardless if the value is null or not.

    public bool HasActiveTransaction()
    {
        return currentTransaction.IsValueCreated;
    }

The believe the method should check for null values.

    public bool HasActiveTransaction()
    {
        return currentTransaction.IsValueCreated && currentTransaction.Value!=null;
    }

I would also like to question why is the UnitOfWork registered as a singleton? which is creating the need for the ThreadLocalStroage complicating the implementation.

This issue occurs frequently in our WebAPI project which sends a message from a controller. We get null reference in the SQLMessageSender.

Stack trace:

NServiceBus.Unicast.Queuing.FailedToSendMessageException: Failed to send message to address: XXXXXX@XXXXXXX ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at NServiceBus.Transports.SQLServer.SqlServerMessageSender.Send(TransportMessage message, Address address) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 41
   --- End of inner exception stack trace ---
   at NServiceBus.Transports.SQLServer.SqlServerMessageSender.ThrowFailedToSendException(Address address, Exception ex) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 88
   at NServiceBus.Transports.SQLServer.SqlServerMessageSender.Send(TransportMessage message, Address address) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 80
   at NServiceBus.Unicast.UnicastBus.SendMessage(List`1 addresses, String correlationId, MessageIntentEnum messageIntent, Object[] messages) in :line 0
   at NServiceBus.Unicast.UnicastBus.SendMessage(Address address, String correlationId, MessageIntentEnum messageIntent, Object[] messages) in :line 0
   at NServiceBus.Unicast.UnicastBus.Send(Object[] messages) in :line 0
   at UCP.API.Controllers.VoiceMailController.Put(String locationCode, String phoneNumber, RequestInfo requestInfo, VoicemailModel model) in c:\DevGit\unifiedcommunications-platform\UCP\UCP.API\Controllers\VoicemailController.cs:line 21
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
@ghost ghost assigned johnsimons Jan 13, 2014
@andreasohlund
Copy link
Member

@johnsimons can you take a look at this one?

@andreasohlund
Copy link
Member

@ghost ghost assigned andreasohlund Jan 15, 2014
@andreasohlund
Copy link
Member

@jalalr how do we repro this? (what version of nsb.sqlserver and nsb.core are you on)

@jalalr
Copy link
Author

jalalr commented Jan 19, 2014

We are using NServiceBus.SqlServer 1.1.0 and NServiceBus 4.2.0

@johnsimons
Copy link
Member

Hi @jalalr, is it possible to send us a repro ?

@jalalr
Copy link
Author

jalalr commented Jan 22, 2014

Hi John,

Please see the following repo for code to reproduce the issue. However it is not an exact replica of the code we use, but it is conceptually the same.

https://bitbucket.org/jradwan/nservicebus-dtc-sample/src/cc1a4ba806c5f887cc14dfd9eb2477e5bd1fd2c6/NServiceBus.SqlServer.Test/

@johnsimons
Copy link
Member

Hi @jalalr,

Thanks for the repro.

I've looked at your code and can't really understand what you trying to do?

So your sample does:

class Program
{
    static void Main(string[] args)
    {
        var bus = Configure.With()
            .DefaultBuilder()
            .DefineEndpointName("NServiceBus.SqlServer.Test.Sender")
            .UseTransport<SqlServer>()
            .UnicastBus()
            .InMemoryFaultManagement()
            .InMemorySubscriptionStorage()
            .UseInMemoryTimeoutPersister()
            .SendOnly();

        var unitofWork = Configure.Instance.Builder.Build<Transports.SQLServer.UnitOfWork>();

        //First scope of work
        using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))
        {
            connection.Open();
            var transaction = connection.BeginTransaction();
            unitofWork.SetTransaction(transaction);
            //Do DB work
            bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
            transaction.Commit();
            unitofWork.ClearTransaction();
        }

        //Second scope of work
        //This simulates using the same thread to send a message across differnet scope of work
        //I expected the transport to create a new connection to send this message
        bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
    }
}

So are you trying to do the bus.Send + some Sql operation in the same ambient transaction?

@johnsimons
Copy link
Member

@jalalr

You shouldn't need to use Transports.SQLServer.UnitOfWork, that should be an internal class //cc @andreasohlund

@johnsimons
Copy link
Member

@andreasohlund

This seem to be a valid scenario.
@jalalr has a send only endpoint and wants to ensure the bus.Send is enlisted in the native SqlTransaction, no DTC

var bus = Configure.With()
    .DefaultBuilder()
    .SendOnly();

using (var connection = new SqlConnection(connString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
        //Do some sql operation here and then do a Send
        bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
        transaction.Commit();
    }
}

As far as I can tell there is no way for us to find out if there is an existing SqlTransaction from the Send unless we expose something?
It feels like we need a bus.EnlistSendsWith(sqlTransaction)

@johnsimons
Copy link
Member

@jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation + bus.Send in a TransactionScope, eg:

using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
    using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))
    {
        connection.Open();

        // Do sql operation

        bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
    }

    scope.Complete();
}

And because you are sharing the same connection string DTC won't upgrade to a 2PC 😄

@andreasohlund
Copy link
Member

Yes the transaction scope is the way to go, that way ado.net will enlist in
the same tx. If connstrings are equal == no dtc, if not == DTC will be used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons notifications@github.comwrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation + bus.Sendin a
TransactionScope, eg:

using (var scope = new TransactionScope(TransactionScopeOption.Required)){
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))
{
connection.Open();

    // Do sql operation


    bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}


scope.Complete();}

And because you are sharing the same connection string DTC won't upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-32997573
.

@johnsimons
Copy link
Member

But this is still an issue if user wants to use native ado transactions and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

Yes the transaction scope is the way to go, that way ado.net will enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC will be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation +
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new
SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC won't upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33002748
.

@andreasohlund
Copy link
Member

I don't see the issue, native ado can't span multiple databases anyway?

On Wed, Jan 22, 2014 at 10:09 AM, John Simons notifications@github.comwrote:

But this is still an issue if user wants to use native ado transactions
and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

Yes the transaction scope is the way to go, that way ado.net will
enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC will be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <notifications@github.com<javascript:_e({},
'cvml', 'notifications@github.com');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation +
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new

SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC won't
upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33002748>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33003955
.

@johnsimons
Copy link
Member

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.
Btw, no DTC means the MSDTC service is stopped.

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

I don't see the issue, native ado can't span multiple databases anyway?

On Wed, Jan 22, 2014 at 10:09 AM, John Simons <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');>>wrote:

But this is still an issue if user wants to use native ado transactions
and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');>>

wrote:

Yes the transaction scope is the way to go, that way ado.net will
enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC will be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');><javascript:_e({},

'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation +
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new

SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC won't
upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33002748>

.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33003955>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33004402
.

@andreasohlund
Copy link
Member

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.

That is exactly my point:

If user want to avoid DTC they have to use the same DB

Solution: Wrap in a transaction scope (it wont enlist since the db is the
same)

If users are ok with DTC and/or want multiple DB's

Solution: Wrap in a transaction scope

Does this make sense?

On Wed, Jan 22, 2014 at 10:19 AM, John Simons notifications@github.comwrote:

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.
Btw, no DTC means the MSDTC service is stopped.

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

I don't see the issue, native ado can't span multiple databases anyway?

On Wed, Jan 22, 2014 at 10:09 AM, John Simons <notifications@github.com<javascript:_e({},
'cvml', 'notifications@github.com');>>wrote:

But this is still an issue if user wants to use native ado
transactions
and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund <
notifications@github.com<javascript:_e({}, 'cvml', '
notifications@github.com');>>

wrote:

Yes the transaction scope is the way to go, that way ado.net will
enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC will
be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <
notifications@github.com<javascript:_e({}, 'cvml', '
notifications@github.com');><javascript:_e({},

'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation +
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new

SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC won't
upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33002748>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33003955>

.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33004402>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33004650
.

@johnsimons
Copy link
Member

So does this work with the MSDTC service stopped? I thought the service
still had to be on?

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.

That is exactly my point:

If user want to avoid DTC they have to use the same DB

Solution: Wrap in a transaction scope (it wont enlist since the db is the
same)

If users are ok with DTC and/or want multiple DB's

Solution: Wrap in a transaction scope

Does this make sense?

On Wed, Jan 22, 2014 at 10:19 AM, John Simons <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');>>wrote:

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.
Btw, no DTC means the MSDTC service is stopped.

On Wednesday, 22 January 2014, Andreas Öhlund <notifications@github.com<javascript:_e({}, 'cvml', 'notifications@github.com');>>

wrote:

I don't see the issue, native ado can't span multiple databases
anyway?

On Wed, Jan 22, 2014 at 10:09 AM, John Simons <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({},
'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>wrote:

But this is still an issue if user wants to use native ado
transactions
and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({}, 'cvml', '
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>

wrote:

Yes the transaction scope is the way to go, that way ado.net will
enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC
will
be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({}, 'cvml', '
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');><javascript:_e({},

'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');> <javascript:_e({}, 'cvml',
'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation +
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new

SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC won't
upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33002748>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33003955>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33004402>

.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33004650>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33005256
.

@andreasohlund
Copy link
Member

Since it wont escalate I'm 99% sure it will work. We need to test!

On Wed, Jan 22, 2014 at 10:37 AM, John Simons notifications@github.comwrote:

So does this work with the MSDTC service stopped? I thought the service
still had to be on?

On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
wrote:

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.

That is exactly my point:

If user want to avoid DTC they have to use the same DB

Solution: Wrap in a transaction scope (it wont enlist since the db is
the
same)

If users are ok with DTC and/or want multiple DB's

Solution: Wrap in a transaction scope

Does this make sense?

On Wed, Jan 22, 2014 at 10:19 AM, John Simons <notifications@github.com<javascript:_e({},
'cvml', 'notifications@github.com');>>wrote:

But it can span multiple tables same db.
This is exactly the same as the no DTC feature.
Btw, no DTC means the MSDTC service is stopped.

On Wednesday, 22 January 2014, Andreas Öhlund <
notifications@github.com<javascript:_e({}, 'cvml', '
notifications@github.com');>>

wrote:

I don't see the issue, native ado can't span multiple databases
anyway?

On Wed, Jan 22, 2014 at 10:09 AM, John Simons <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({},
'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>wrote:

But this is still an issue if user wants to use native ado
transactions
and
have MSDTC service off.

On Wednesday, 22 January 2014, Andreas Öhlund <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({}, 'cvml', '
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>>

wrote:

Yes the transaction scope is the way to go, that way ado.netwill
enlist
in
the same tx. If connstrings are equal == no dtc, if not == DTC
will
be
used

On Wed, Jan 22, 2014 at 7:52 AM, John Simons <
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');><javascript:_e({}, 'cvml', '
notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');><javascript:_e({},

'cvml', 'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');> <javascript:_e({}, 'cvml',
'notifications@github.com <javascript:_e({}, 'cvml',
'notifications@github.com');>');>');>>wrote:

@jalalr https://github.com/jalalr can you use DTC ?

If you can, then all you need to do is wrap your sql operation
+
bus.Sendin a
TransactionScope, eg:

using (var scope = new
TransactionScope(TransactionScopeOption.Required)){
using (var connection = new

SqlConnection(ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))

{
connection.Open();

// Do sql operation

bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
}

scope.Complete();}

And because you are sharing the same connection string DTC
won't
upgrade
to a 2PC [image: 😄]


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-32997573>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33002748>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33003955>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33004402>

.


Reply to this email directly or view it on GitHub<

https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33004650>

.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33005256>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33005904
.

@eriksteinebach
Copy link
Contributor

I have tested this case. If DTC is required, it promotes from a native transaction to a distributed transaction. If MSDTC is disabled an exception will be thrown.

Note: connectionstring must be a 100% case sensitive match

@andreasohlund
Copy link
Member

Can you confirm that the DTC service can be off as long as no escalation
happen? (pretty sure I've confirmed this my self a while back)

On Wed, Jan 22, 2014 at 10:48 AM, eriksteinebach
notifications@github.comwrote:

I have tested this case. If DTC is required, it promotes from a native
transaction to a distributed transaction. If MSDTC is disabled an exception
will be thrown.

Note: connectionstring must be a 100% case sensitive match


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33006580
.

@eriksteinebach
Copy link
Contributor

Yes, I'm 99,9% sure that was a scenario I tested for my SQL multidatabase support POC: #15

@johnsimons
Copy link
Member

Cool, in that case we can close this.

@jalalr
Copy link
Author

jalalr commented Jan 22, 2014

Hi guys,

Thanks for investigating the issue.

@johnsimons, yes I do have a send only endpoint and want to ensure the bus.Send is enlisted in the native SqlTransaction without needing DTC.

However your suggestion with the transaction scope causes DTC escalation. I made sure DTC is disabled and stopped. I ran the code below and got the follow exception.

 using (var scope = new TransactionScope(TransactionScopeOption.Required))
            {
                //First scope of work
                using (
                    var connection =
                        new SqlConnection(
                            ConfigurationManager.ConnectionStrings["NServiceBus/Transport"].ConnectionString))
                {
                    connection.Open();
                    //Do DB work
                    bus.Send("NServiceBus.SqlServer.Test", new TestMessage());
                }

                scope.Complete();
            }
NServiceBus.Unicast.Queuing.FailedToSendMessageException was unhandled
  HResult=-2146233088
  Message=Failed to send message to address: NServiceBus.SqlServer.Test@BTG-TPC34
  Source=NServiceBus.Transports.SQLServer
  StackTrace:
       at NServiceBus.Transports.SQLServer.SqlServerMessageSender.ThrowFailedToSendException(Address address, Exception ex) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 90
       at NServiceBus.Transports.SQLServer.SqlServerMessageSender.Send(TransportMessage message, Address address) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 76
       at NServiceBus.Unicast.UnicastBus.SendMessage(List`1 addresses, String correlationId, MessageIntentEnum messageIntent, Object[] messages) in :line 0
       at NServiceBus.Unicast.UnicastBus.SendMessage(Address address, String correlationId, MessageIntentEnum messageIntent, Object[] messages) in :line 0
       at NServiceBus.Unicast.UnicastBus.SendMessage(String destination, String correlationId, MessageIntentEnum messageIntent, Object[] messages) in :line 0
       at NServiceBus.Unicast.UnicastBus.Send(String destination, Object message) in :line 0
       at NServiceBus.Test.Sender.Program.Main(String[] args) in c:\Users\jradwan\Documents\Visual Studio 2012\Projects\NServiceBus.SqlServer.Test\NServiceBus.SqlServer.Test.Sender\Program.cs:line 35
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: System.Data.SqlClient.SqlException
       HResult=-2146232060
       Message=MSDTC on server 'BTG-TPC34' is unavailable.
       Source=.Net SqlClient Data Provider
       ErrorCode=-2146232060
       Class=16
       LineNumber=1
       Number=8501
       Procedure=""
       Server=localhost
       State=2
       StackTrace:
            at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
            at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
            at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
            at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
            at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
            at System.Data.SqlClient.SqlDataReader.get_MetaData()
            at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
            at System.Data.SqlClient.TdsParser.GetDTCAddress(Int32 timeout, TdsParserStateObject stateObj)
            at System.Data.SqlClient.SqlInternalConnectionTds.GetDTCAddress()
            at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
            at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
            at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
            at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
            at System.Data.ProviderBase.DbConnectionPool.PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction)
            at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
            at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
            at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
            at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
            at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
            at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
            at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
            at System.Data.SqlClient.SqlConnection.Open()
            at NServiceBus.Transports.SQLServer.SqlServerMessageSender.Send(TransportMessage message, Address address) in c:\BuildAgent\work\3e0c79eb07d58469\src\NServiceBus.SqlServer\SqlServerMessageSender.cs:line 54
       InnerException: 

@eriksteinebach
Copy link
Contributor

I checked my POC. What I tested was the scenario of receiving and writing to a database in a native transaction. That works fine with DTC disabled. But when I add a send in there to the same endpoint as the received message I can reproduce the issue. It should work in a native transaction, but ado.net wants to promote. Not sure why, maybe the above issue?

@johnsimons
Copy link
Member

I thought the issue was that even though the transaction is not
distributed, MSDTC service still needs to be running because the
ado.netchecks for it.

On Thursday, 23 January 2014, eriksteinebach notifications@github.com
wrote:

I checked my POC. What I tested was the scenario of receiving and writing
to a database in a native transaction. That works fine with DTC disabled.
But when I add a send in there to the same endpoint as the received message
I can reproduce the issue. It should work in a native transaction, but
ado.net wants to promote. Not sure why.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33119724
.

@andreasohlund
Copy link
Member

Sounds vierd that is works for a receive + read and not a receive + write?

(a receive is actually a read + delete
https://github.com/Particular/NServiceBus.SqlServer/blob/develop/src/NServiceBus.SqlServer/SqlServerPollingDequeueStrategy.cs#L381
)

On Thu, Jan 23, 2014 at 1:38 PM, John Simons notifications@github.comwrote:

I thought the issue was that even though the transaction is not
distributed, MSDTC service still needs to be running because the
ado.netchecks for it.

On Thursday, 23 January 2014, eriksteinebach notifications@github.com
wrote:

I checked my POC. What I tested was the scenario of receiving and
writing
to a database in a native transaction. That works fine with DTC
disabled.
But when I add a send in there to the same endpoint as the received
message
I can reproduce the issue. It should work in a native transaction, but
ado.net wants to promote. Not sure why.


Reply to this email directly or view it on GitHub<
https://github.com/Particular/NServiceBus.SqlServer/issues/17#issuecomment-33119724>

.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-33120209
.

@andreasohlund
Copy link
Member

Did some research and it seems escalation wont happen if "the connections are not open at the same time"

http://stackoverflow.com/a/1693270/236004

Since we keep the connection open during receive I believe the escalation will happen when a second connection is opened. To fix this we need to share the connection or have the users use our native transaction. Does this make sense?

To verify this we can tell the sqltransport to only use native transactions and since we then share that TX the send should not escalate since we're using the same TX + conn?

@johnsimons
Copy link
Member

To fix this we need to share the connection or have the users use our native transaction

We need both:

@johnsimons johnsimons modified the milestones: 1.2.0, 1.1.1 Mar 20, 2014
@johnsimons
Copy link
Member

The original issue raised is now fixed as part of #15

@johnsimons johnsimons added the bug label Mar 21, 2014
@johnsimons johnsimons changed the title Bug with UnitOfWork HasActiveTransaction in UnitOfWork class returns true even if the transaction for the current thread has been cleared Mar 21, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants