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
Comments
@johnsimons can you take a look at this one? |
Reported by another user here -https://groups.google.com/forum/#!msg/particularsoftware/dOQGJaxxUKM/lnXI6j1LJFAJ |
@jalalr how do we repro this? (what version of nsb.sqlserver and nsb.core are you on) |
We are using NServiceBus.SqlServer 1.1.0 and NServiceBus 4.2.0 |
Hi @jalalr, is it possible to send us a repro ? |
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. |
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 |
You shouldn't need to use |
This seem to be a valid scenario. 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 |
@jalalr can you use DTC ? If you can, then all you need to do is wrap your sql operation + 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 😄 |
Yes the transaction scope is the way to go, that way ado.net will enlist in On Wed, Jan 22, 2014 at 7:52 AM, John Simons notifications@github.comwrote:
|
But this is still an issue if user wants to use native ado transactions and On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
|
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 it can span multiple tables same db. On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
|
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 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:
|
So does this work with the MSDTC service stopped? I thought the service On Wednesday, 22 January 2014, Andreas Öhlund notifications@github.com
|
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:
|
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 |
Can you confirm that the DTC service can be off as long as no escalation On Wed, Jan 22, 2014 at 10:48 AM, eriksteinebach
|
Yes, I'm 99,9% sure that was a scenario I tested for my SQL multidatabase support POC: #15 |
Cool, in that case we can close this. |
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.
|
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? |
I thought the issue was that even though the transaction is not On Thursday, 23 January 2014, eriksteinebach notifications@github.com
|
Sounds vierd that is works for a receive + read and not a receive + write? (a receive is actually a read + delete On Thu, Jan 23, 2014 at 1:38 PM, John Simons notifications@github.comwrote:
|
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? |
We need both:
|
The original issue raised is now fixed as part of #15 |
The use of IsValueCreated is incorrect, because it will return true regardless if the value is null or not.
The believe the method should check for null values.
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:
The text was updated successfully, but these errors were encountered: