When I use "Select ... for update" query code stops without exception or any freeze. It just won't go to the next instruction. It work fine for a regular select query. Any idea why ?
string selectQuery = "select * from table_name where id = 1 FOR UPDATE"; // Code stops on data fill
string selectQueryNoLock = "select * from table_name where id = 1"; // Code execute normally
string connectionString = "fake";
OracleConnection oracleConnection = new OracleConnection(connectionString);
Console.WriteLine("Open Connection");
oracleConnection.Open();
OracleTransaction oracleTransaction = oracleConnection.BeginTransaction();
OracleCommand oracleCommand = new OracleCommand(selectQuery, oracleConnection);
oracleCommand.Transaction = oracleTransaction;
OracleDataAdapter dataAdapter = new OracleDataAdapter(oracleCommand);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet);
// This line is never reached!
Console.WriteLine("Press any key...");
A standard select ... for update will wait indefinitely if another transaction is currently holding a lock on that row. In normal apps, this is not really a problem, as transactions should be short-lived.
However, if an indefinite wait is not the behavior that you want, and you would rather be told if you are unable to acquire a lock after a certain amount of time, you can specify a timeout like this:
select ... for update wait 10
... which will return an error if the timeout elapses and you are unable to acquire the lock.
Alternatively, if you don't want to wait at all, you can also do:
select ... for update nowait
... which will immediately return an error if you can't acquire the lock right away.
Related
I was getting quite often the following error:
Npgsql.NpgsqlException: 'The connection pool has been exhausted, either raise MaxPoolSize (currently 100) or Timeout (currently 15 seconds)'
Then I looked for possible causes and solutions in here and found out that I should be applying the using statement. So I reviewed all my code and did that.
However, I keep getting that error while testing a button that gets information from my database, does some calculation and writes the results in a few textboxes. It usually crashes at the fifth-ish time I click it. A piece of the code follows:
private void CalcTemp(Cable cable)
{
string sqlString = "Server=172.19.2.40; Port=5432; User Id=postgres; Password=password; Database=PROLIG;";
using (NpgsqlConnection sqlCon = new NpgsqlConnection(sqlString))
{
string cmdString = #"SELECT tempamb, elevmaxonan, elevmaxonaf, elevmaxonaf2, topoil1_2, topoil1_4, especial1factor, especial1topoil,
especial2factor, especial2topoil, especial3factor, especial3topoil, especial4factor, especial4topoil,
especial5factor, especial5topoil, especial6factor, especial6topoil FROM correntes WHERE prolig_ofs_id = #id;";
NpgsqlCommand sqlCmd = new NpgsqlCommand(cmdString, sqlCon);
sqlCmd.Parameters.AddWithValue("id", StartOF.MyOF.id);
NpgsqlDataAdapter sqlDa = new NpgsqlDataAdapter(sqlCmd);
DataTable dt = new DataTable();
sqlDa.Fill(dt);
//does calculation
}
Any thoughts on why this is happening and how to fix it?
Thanks a lot.
Just add using to your command creation:
using (NpgsqlCommand sqlCmd = new NpgsqlCommand(cmdString, sqlCon))
{
Disposing all objects which implement IDisposable is a good practice.
Since your command is not disposed of in time, your connection is not closed and returned to the pool.
It is a lot of stuff on dispose which must be executed directly (or by using).
protected override void Dispose(bool disposing)
{
if (State == CommandState.Disposed)
return;
if (disposing)
{
// Note: we only actually perform cleanup here if called from Dispose() (disposing=true), and not
// if called from a finalizer (disposing=false). This is because we cannot perform any SQL
// operations from the finalizer (connection may be in use by someone else).
// We can implement a queue-based solution that will perform cleanup during the next possible
// window, but this isn't trivial (should not occur in transactions because of possible exceptions,
// etc.).
if (_prepared == PrepareStatus.Prepared)
_connector.ExecuteBlind("DEALLOCATE " + _planName);
}
Transaction = null;
Connection = null;
State = CommandState.Disposed;
base.Dispose(disposing);
}
So you need the following code:
private void CalcTemp(Cable cable)
{
string sqlString = "Server=172.19.2.40; Port=5432; User Id=postgres; Password=password; Database=PROLIG;";
using (NpgsqlConnection sqlCon = new NpgsqlConnection(sqlString))
{
string cmdString = #"SELECT * FROM correntes WHERE prolig_ofs_id = #id;";
using (NpgsqlCommand sqlCmd = new NpgsqlCommand(cmdString, sqlCon))
{
sqlCmd.Parameters.AddWithValue("id", StartOF.MyOF.id);
NpgsqlDataAdapter sqlDa = new NpgsqlDataAdapter(sqlCmd);
DataTable dt = new DataTable();
sqlDa.Fill(dt);
//does calculation
} //end using command (calls dispose on command, even if exception happens)
} //end using connection (calls dispose on connection object, even if exception happens)
}
Next advice - do not use data tables in case of large amount of data. Use DataReader instead.
I want to know if there's a way to hook into a currently running transaction and have stuff be done when that transaction completes.
Currently I'm in the process of implementing an EventPublisher that uses MassTransit/RabbitMQ to publish messages, but I want to only have those messages be published when a TransactionScope is getting completed.
I would check inside the EventPublisher.PublishEvent() method if there's currently a transaction running, if no, then fire off the messages, if yes, then collect the messages and wait for the transaction to complete to send them off.
var ep = container.GetInstance<IEventPublisher>();
using(var scope = new TransactionScope())
{
... do some stuff
SaveEntity(entity);
ep.PublishEvent(new EntitySaved(entity.Id));
... do some more stuff ...
UpdateEntity(differentEntity);
ep.PublishEvent(new EntityUpdated(differentEntity.Id));
... do even more stuff ...
ep.PublishEvent(new UnrelatedMessage(someData));
scope.Complete(); // <- only want the actual sending off to RabbitMQ to happen here.
}
I found this TransactionCompleted Event here https://learn.microsoft.com/en-us/dotnet/api/system.transactions.transaction.transactioncompleted
But it seems to only be fired after the scope.Complete() bits are over and done.
I could use this to check if the transaction status is completed and then like actually fire off those messages I collected during the transaction. But my problem is that the connection to RabbitMQ could be down. And then I wouldn't be able to send those messages, but all the work above has been done already, but I never was able to send off those messages.
What I actually want is to somehow hook into the bit where the transaction is currently completing and during that process I fire off my messages and if that fails I can throw an exception and have that still running transaction go out the window.
Maybe there's a way to do that with MassTransit, but the documentation isn't really forthcoming there.
Here's some example code showing the problem.
internal class Program
{
private static void Main(string[] args)
{
const string connectionString = "Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True";
var sql = #"insert into [SomeTable] (
[Id]
,[Name]
,[Index]
,[RelationId]
) values (#param1, #param2, #param3, #param4) ";
using (var scope = new TransactionScope())
{
Transaction.Current.TransactionCompleted += CurrentOnTransactionCompleted;
using (var con = new SqlConnection(connectionString))
{
con.Open();
using (var cmd = new SqlCommand(sql, con))
{
cmd.Parameters.Add("#param1", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid();
cmd.Parameters.Add("#param2", SqlDbType.NVarChar, 128).Value = "Blah";
cmd.Parameters.Add("#param3", SqlDbType.SmallInt).Value = 1;
cmd.Parameters.Add("#param4", SqlDbType.UniqueIdentifier).Value = Guid.Parse("a401866d-3bdd-48a4-a78b-d40864c8471b");
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
}
scope.Complete();
}
}
private static void CurrentOnTransactionCompleted(object sender, TransactionEventArgs e)
{
// I want to do stuff here but if this stuff fails I need the whole transaction to roll back.
... do some stuff that can fail ...
e.Transaction.Rollback(new Exception("Bad transaction!"));
// or
throw new Exception("Bad transaction!");
}
}
I was looking in the wrong place. After looking through microsoft/referencesource I found what I was looking for in TransactionContext.cs.
What you need is to hook up an IEnlistmentNotification which is described here: https://learn.microsoft.com/en-us/dotnet/api/system.transactions.transaction.enlistvolatile
I need to put my code inside the Prepare method and if it fails call ForceRollback.
I have just a general type of question. If I have a C# application that calls a SQL Server stored procedure, and the C# application times out, does the procedure call on the server continue running to it's completion?
No. Below is a reproduction. When the timeout occurs the running process will be killed, halting it immediately. If you do not have a transaction specified, work that has been done in the stored procedure prior to the timeout will be persisted. Similarly, if the connection to the server is severed by some outside force, SQL Server will kill the running process.
using (var conn = new SqlConnection(#"Data Source=.;Initial Catalog=Test;Integrated Security=True"))
{
conn.Open();
using (var setupTable = new SqlCommand(#"
IF NOT EXISTS (
SELECT *
FROM
sys.schemas s
INNER JOIN sys.tables t ON
t.[schema_id] = s.[schema_id]
WHERE
s.name = 'dbo' AND
T.name = 'TimeoutTest')
BEGIN
CREATE TABLE dbo.TimeoutTest
(
ID int IDENTITY(1,1) PRIMARY KEY,
CreateDate datetime DEFAULT(getdate())
);
END
-- remove any rows from previous runs
TRUNCATE TABLE dbo.TimeoutTest;", conn))
{
setupTable.ExecuteNonQuery();
}
using (var checkProcExists = new SqlCommand(#"
SELECT COUNT(*)
FROM
sys.schemas s
INNER JOIN sys.procedures p ON
p.[schema_id] = s.[schema_id]
WHERE
s.name = 'dbo' AND
p.name = 'AddTimeoutTestRows';", conn))
{
bool procExists = ((int)checkProcExists.ExecuteScalar()) == 1;
if (!procExists)
{
using (var setupProc = new SqlCommand(#"
CREATE PROC dbo.AddTimeoutTestRows
AS
BEGIN
DECLARE #stop_time datetime;
SET #stop_time = DATEADD(minute, 1, getdate());
WHILE getdate() < #stop_time
BEGIN
INSERT INTO dbo.TimeoutTest DEFAULT VALUES;
-- wait 10 seconds between inserts
WAITFOR DELAY '0:00:10';
END
END", conn))
{
setupProc.ExecuteNonQuery();
}
}
}
bool commandTimedOut = false;
try
{
using (var longExecution = new SqlCommand("EXEC dbo.AddTimeoutTestRows;", conn))
{
// The time in seconds to wait for the command to execute.
// Explicitly setting the timeout to 30 seconds for clarity.
longExecution.CommandTimeout = 30;
longExecution.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
if (ex.Message.Contains("Timeout"))
{
commandTimedOut = true;
}
else
{
throw;
}
}
Console.WriteLine(commandTimedOut.ToString());
// Wait for an extra 30 seconds to let any execution on the server add more rows.
Thread.Sleep(30000);
using (var checkTableCount = new SqlCommand(#"
SELECT COUNT(*)
FROM
dbo.TimeoutTest t;", conn))
{
// Only expecting 3, but should be 6 if server continued on without us.
int rowCount = (int)checkTableCount.ExecuteScalar();
Console.WriteLine(rowCount.ToString("#,##0"));
}
}
Console.ReadLine();
produces the following output
True
3
even though running the stored procedure from Management Studio will add 6 rows in the one minute time frame.
Short answer is Yes .... here is some info to back up my claim
There are actually several places where a application can 'time out' but one is a Command Execution time out ...
command execution time out - This property is the cumulative time-out for all network reads during command execution or processing of the results. A time-out can still occur after the first row is returned, and does not include user processing time, only network read time.
A Command is not going to rollback on its own if it times out. This will need a transaction around the code if it times out.
if a timeout can occur when rows are being returned than means a timeout can occur anytime C# is not going to tell SQL Server to stop running the command. Things can be done about that such as wrapping the command in a transaction
Source: https://blogs.msdn.microsoft.com/mattn/2008/08/29/sqlclient-timeouts-revealed/ ... and experience
If you are using the SQlCommand Class, once the app times out , the query execution will be rolled back.
My thoughts about that are that it's all about the connection opened by the call to the procedure.
If your code is executed within a using block or if it is garbage collected then i think that the execution of the SP will be rolled back.
Conn = new SqlConnection(ConnStr);
Conn.Open();
myCommand = new SqlCommand();
myCommand.CommandTimeout = 180000;
myCommand.Connection = Conn;
myCommand.CommandType = System.Data.CommandType.StoredProcedure;
I have a loop that runs some ExecuteNonQuery commands. Occasionally it returns the error message:
ExecuteNonQuery requires an open and available connection. The connections
current state is closed.
Here is my code:
private void MyTimerEventHandler(object src, ElapsedEventArgs a)
{
sqlCon = new SqlConnection("server=" + appConfig.sqlServer + ";Trusted_Connection=yes;database=testdb;connection timeout=30;");
sqlCon.Open();
foreach (TagManager tm in tagManagerList)
{
foreach (Tag tag in tm.getTags())
{
SqlCommand insCmd = new SqlCommand("INSERT INTO tag_values (tag_id, value, time) Values (#tagId, #tagValue, #tagTime);", sqlCon);
insCmd.Parameters.Add(new SqlParameter("#tagId", tag.tagNameId));
insCmd.Parameters.Add(new SqlParameter("#tagValue", tag.value));
insCmd.Parameters.Add(new SqlParameter("#tagTime", tag.time));
insCmd.ExecuteNonQuery();
}
}
sqlCon.Close();
}
This code executes in the event handler of a Timer that runs every 15 seconds. The timer is kept alive by GC.KeepAlive() if that makes any difference.
Create a new connection object for each timer callback:
private void MyTimerEventHandler(object src, ElapsedEventArgs a)
{
SqlConnection sqlCon = new SqlConnection( [...]
It is normally a bad idea to reuse a connection. In your case you can run into a race condition if that connection is being used in another thread.
Creating new connections should not affect performance as they are pulled from the Connection pool.
You can check if you connection still open before execute query
if (sqlCon.State == System.Data.ConnectionState.Open)
{
insCmd.ExecuteNonQuery();
}
else
{
sqlCon.Open();
insCmd.ExecuteNonQuery();
}
}
You do not you prepare the insert statement by using the StringBuilder and pass it as text to SQL Command to send once over the SQL SERVER to insert. In this case for all 600 loops , you will need to connect to DB once . Just a thought
I have a web page that has coding structured somewhat as follows:
SqlConnection conX =new SqlConnection(blablabla);
conX.Open();
SqlTransaction tran=conX.BeginTransaction();
try{
SqlCommand cmdInsert =new SqlCommand("INSERT INTO Table1(ColX,ColY) VALUES #x,#y",conX);
cmdInsert.Transaction=tran;
cmdInsert.ExecuteNonQuery();
SqlCommand cmdSelect=new SqlCOmmand("SELECT * FROM Table1",conX);
cmdSelect.Transaction=tran;
SqlDataReader dtr=cmdSelect.ExecuteReader();
//read stuff from dtr
dtr.Close();
cmdInsert=new SqlCommand("UPDATE Table2 set ColA=#a",conX);
cmdInsert.Transaction=tran;
cmdInsert.ExecuteNonQuery();
//display MiscMessage
tran.Commit();
//display SuccessMessage
}
catch(Exception x)
{
tran.Rollback();
//display x.Message
}
finally
{
conX.Close();
}
So, everything seems to work until MiscMessage. Then, after a while (maybe 15-ish seconds?) x.Message pops up, saying that:
"Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding."
So something wrong with my trans.Commit()? The database is not updated so I assume the trans.Rollback works...
I have read that deadlocks can cause timeouts...is this problem cause by my SELECT statement selecting from Table1, which is being used by the first INSERT statement?
If so, what should I do? If that ain't the problem, what is?
Because you show MiscMessage, your transaction still open, then timeout expires. Commit transaction,then show message.