I want to open several connections within a single transaction scope, so that each connection could see the changes done by the previous ones.
I need this for tests - real code writes to the database, and testing code verifies the data was actually inserted/updated. In the end I rollback transaction scope so that the real database is not affected.
This approach works fine in SQL Server, but doesn't seem to work in PostgreSQL (I use 9.3 with Npgsql provider), below is a small example.
Here's the helper to run arbitrary query within a transaction scope
private void RunQuery(string query, Action<IDataReader> process)
{
using (var connection = new NpgsqlConnection(Config.ConnectionString)) {
connection.Open();
connection.EnlistTransaction(Transaction.Current);
using (var command = connection.CreateCommand()) {
command.CommandText = query;
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
process(reader);
}
}
}
}
}
..and here's the test code - it inserts into users table and then checks whether the user was actually inserted:
using (var scope = new TransactionScope()) {
//"tested scenario"
int id = 0;
RunQuery("INSERT INTO users (name) VALUES ('foo') RETURNING id;", reader => {
id = (int)reader.GetValue(0);
});
//checking
int id2 = 0;
RunQuery("SELECT id, name FROM users WHERE id=" + id, reader => {
id2 = (int)reader.GetValue(0);
});
Assert.That(id2, Is.Not.EqualTo(0));
}
The test above fails on Postgres as id2 is always zero. I tried TransactionScope constructor with TransactionOptions.ReadUncommitted but it doesn't seem to help. Note that if I run this against SQL Server (change NpgsqlConnection to SqlConection, use SCOPE_IDENTITY to retrieve the id) then everything works just fine and id2 is not zero.
As you may expect, selects within the same connection work for Postgres, but I don't need that, my goal is to use multiple connections on a shared transaction scope. I also don't need multithreading, those connections happen sequentially.
First a disclaimer: while I know a bit about postgresql, I know very little about .NET.
I suspect you are conflating two related but separate concepts - that of Distributed Transactions and the level of transaction isolation that exists.
According to the .NET Documentation, EnlistTransaction adds the connection into a distributed transaction. A distributed transaction is described as follows
A distributed transaction is a transaction that affects several
resources. For a distributed transaction to commit, all participants
must guarantee that any change to data will be permanent. Changes must
persist despite system crashes or other unforeseen events. If even a
single participant fails to make this guarantee, the entire
transaction fails, and any changes to data within the scope of the
transaction are rolled back.
In a database, such transactions are implemented by a two-phase commit process amongst what are actually separate transactions in the database. All of the participating transactions are progressed to the end of the first phase by executing PREPARE TRANSACTION. Once they are all in this state, then they can be fully committed by executing COMMIT PREPARED. If any of them fails during PREPARE TRANSACTION, then they can all be rolled back by ROLLBACK PREPARED. This guarantees that either they are all committed, or they are all rolled back.
When using middleware such as that provided by .NET, you do not see any of these details: the framework handles the two-phase commit for you.
So, you might be wondering what this has to do with the fact that you are not seeing changes made in one part of this distributed transaction reflected in another. The answer is probably nothing. The two transactions are actually completely separate - in fact it is possible for them to be on completely separate databases.
What you are trying to achieve - to be able to see changes made in one transaction from another prior to commit - is related to the level of transaction isolation.
The bad news for you is that it sounds like the isolation level you would like to have is 'read uncommitted', which is not supported in postgresql.
Maybe you need to describe what you are trying to achieve, at a higher level - it is likely there is another (maybe better) way to achieve it.
Related
This question already has answers here:
How to Decide to use Database Transactions
(7 answers)
Closed 5 years ago.
I used to rarely use transactions until all of the sudden I was faced with a scenario that had a lot of db footprints, so I panicked and since then started to think about using transactions for any logic that might involve data manipulation statements (Insert, Update, Delete) and could result in unexpected disasters, exceptions.
Here is the model that I keep on using:
using (var db = new X_Entity())
{
using (var trans = db.Database.BeginTransaction())
{
try
{
#region Logic
// Logic that might include at least one data manipulation statement
// db.Insert(), db.Update, db.Delete
#endregion
db.SaveChanges();
trans.Commit();
}
catch (Exception exc)
{
trans.Rollback();
HandleExceptions(exc);
}
}
}
Say there is a single Update, insert or delete statement, should a transaction be used in this case? My understanding is that in the following cases transactions are not necessary to use:
Get/Select/Join statements...etc
When the data manipulation statement is not followed by error prone logic
Transactions should be used to prevent your database changes falling into invalid state. What an invalid state is will depend on your domain. It is used when you have more than one actions that would complete the changes. For get/select, there is no point in using Transactions because you are not altering the state of the data. Even if it fails, the data will be in a valid state.
A simple example is a amount transfer situation in a banking app. Lets say when you transfer $100 to your friend, for this action to be complete (in a very simple scenario)
$100 must be deducted from your bank account
$100 must be added to your recipients account
Without both of these steps succeeding (atomic) the data will go in invalid state. So to prevent these, you should use transaction/transaction scope. When you wrap both of the steps in a transaction, either they both will succeed or both will fail.
I'd like to ask a question. I've been trying to find some information regarding transactions with multiple connections, but I haven't been able to find any good source of information.
Now for what I'm trying to do. I have code that looks like this:
using (var Connection1 = m_Db.CreateConnection())
using (var Connection2 = m_Db.CreateConnection())
{
Connection1.DoRead(..., (IDataReader Reader) =>
{
// Do stuff
Connection2.DoWrite(...);
Connection2.DoRead(..., (IDataReader Reader) =>
{
// Do more stuff
using (var Connection3 = m_Db.CreateConnection())
{
Connection3.DoWrite(...);
Connection3.Commit(); // Is this even right?
}
});
});
Connection1.DoRead(..., (IDataReader) =>
{
// Do yet more stuff
});
Connection1.Commit();
Connection2.Commit();
}
Each CreateConnection creates a new transaction using MySqlConnection::BeginTransaction. The CreateConnection method creates a Connection object which wraps a MySqlConnection. The DoRead function executes some SQL, and disposes the IDataReader when done.
Every Connection will do a Rollback when disposed.
Now for some notes:
I have ONE server with multiple databases.
I am running MySql server with InnoDB databases.
I am doing both reads and writes to these databases.
For performance reasons and not to mess up the database, I am using transactions.
The code is (at least, for now) entirely serial. There are NO concurrent threads. All inserts and queries are done in serial fashion.
I use multiple connections to the database because a read or write is not allowed while another read is in progress (basically the reader object has not yet been disposed).
I basically want every connection to see all changes. So for example, after Connection 3 does some writes, Connection 1 should see those. But the data should be in the transaction and not written to the database (yet).
Now, as for my questions:
Does this work? Will everything ONLY be committed only once the last Commit function is called? Should I use another approach?
Is this right? Is my approach completely and utterly wrong and silly?
Any drawbacks? Especially regarding performance.
Thanks.
Welp, it seems no one knows. But that's okay.
For now, I just went with the method of just using one connection and reading all the results into a List>, then closing the reader, thereby avoiding the problem of having to use multiple connections.
Might there be performance problems? Maybe, but it's better than having to deal with uncertainty and deadlocks.
My question is simple. I have the below code and user is still inserted. When I check the database just after SaveOrUpdate (and before the rollback) I see user is already inserted. It's like flush mode and transaction not working. Where am I going wrong?
using (var session = sessionFactory.OpenSession())
{
session.FlushMode = FlushMode.Never;
using (var tran = session.BeginTransaction())
{
var user = CreateUser();
session.SaveOrUpdate(user);
tran.Rollback();
}
}
If we check "just after SaveOrUpdate" ... we are still inside of one transaction. Lot of stuff could happen before final decision (commit or rollback).
And one of the operation would be to decide if object should be Created or Updated. So in case, that the ID generator is set to native/identity (e.g. SQL server) - NHibernate must execute the INSERT to get the ID. Lot of operation could be postponed, but to receive the ID - there is no way to wait.
So most likely your ID need to be obtained from DB.. and that's why INSERT happens. BUT other stuff won't be written into DB, until Flush() is called... So, I wouldn't mark described behaviour as something special.
I felt some delay on Loading Contents while Using Transactions to Edit the contents,
(Testing this situation is a bit hard for me as I don't know how could be better to test it)
I have some doubts about Transactions usages:
There are some minor issues and things I should understand about Transactions
and these parts are related to this question :
When should we use Transactions in a Own-Made CMS ?
My-case-specific notes :
Should I use transactions on any CMS , While we have sprocs on Insert,Update,Retrieve, .... ?
Is the necessity of using transactions just when we are working on more tables than one ?
The Transaction strategy I used :
Adding Product Method ( Which uses add Product sproc ) :
TransactionOptions txOptions = new TransactionOptions();
using (TransactionScope txScope = new TransactionScope
(TransactionScopeOption.Required, txOptions))
{
try
{
connection.Open();
command.ExecuteNonQuery();
LastInserted = (int)pInsertedID.Value;
txScope.Complete();
}
catch (Exception ex)
{
logErrors.Warn(ex.Message);
}
finally
{
command.Dispose();
connection.Close();
}
Transactions may help to ensure consistency of the database. For example, if a stored procedure used to add a product inserts data in more than one table, and something fails along the way, a transaction might be helpful to rollback the whole operation, thus the database is free of half-baked products (e.g. lacking some critical info in related tables).
Transaction scopes (TransactionScope) are used to provide an ambient implicit transaction for whatever code runs inside a code block. These scopes may help to severely simplify the code, however, they also may add complexities in multithreaded environments (unfortunately, I don't know quite a lot about such cases).
Therefore, the code you provided would probably make sense to ensure database's consistency, especially if the command uses more than one table. It may add some performance overhead; however, you would be better off relying on gathered profiling data rather than any sort of feelings before conducting any optimizations (i.e. try to gather some quantitative data as to how slower things are under transactions). Modern database engines usually handle transactions quite efficiently; in my own experience, there were no transactions for removal due to their performance overhead.
I'm doing a multi-layer MVVM WPF application, which is connected to an Oracle database. I need some explanations about TransactionScope. Consider the following example:
using (TransactionScope ts = new TransactionScope())
{
...
bank.setID(BankName, Branch);
check.addCheck(check);
...
ts.Complete();
}
This code is for explanation only: bank.setID() updates a record, while addCheck actually inserts a record. I couldn't figure out how to test this. I wanted to execute the update and shutdown of the database before inserting with the second method, and then check to see if the update is rolled back. Is this already right? Am I on the right track ? Is this the purpose of TransactionScope?
Thanks in advance
Edit: I wasn't sure at first if you understood DB transactions, so here's a very short description:
TransactionScope is designed to wrap a database transaction with a mechanism that is exception safe.
You use it to wrap a set of operations that should be atomic with a transaction, so if you fail on one update, all of the DB actions within that transaction get rolled back.
You use it in a using block so that if your code throws an exception, the transaction gets rolled back, instead of committed to the database.
I wanted to execute the update and shutdown the database before inserting with the second method, and then check to see if the update is rolled back ... Am I on the right track ?
Yep. Your code should already handle it:
If the DB is shut down before addCheck does an insert, then the insert should throw an exception
Since an exception gets thrown, Complete should never get called
The finally block should automatically roll back the transaction when the (hidden) finally block is reached, and Complete hasn't been called
I couldn't figure out how to test this
If you would like to write a unit test for your code, then I wouldn't suggest the "take the DB offline in the middle of execution" approach.
Instead, I'd suggest making your DB logic flexible enough to point at different DBs. Then, either remove the table or some of the columns that addCheck inserts to. Try to set up your DB so that setID succeeds, but addCheck fails.
TransactionScope is well documented. MSDN it.
to test how it works, no need to take database offline. use this snippet:
using (TransactionScope ts = new TransactionScope())
{
try
{
...
bank.setID(BankName, Branch);
throw new System.InvalidOperationException("sht happens");
check.addCheck(check);
...
ts.Complete();
}
catch
{
//catch the exception
//ts.Complete() is not called, thus update/insert rollbacks
}
}