-
Notifications
You must be signed in to change notification settings - Fork 337
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
Proposal: Implicit commit detection #1529
Comments
Some off-the-cuff thoughts: MySqlConnector already has "strict" transaction requirements, and IgnoreCommandTransaction could be used here. (Arguably, this is a bug in that implementation.) If the driver detects that the transaction was committed (due to the OK payload flag) the next command that specifies a (already-committed) A less invasive way might be to report the transaction status on a new property, similar to How would any of these approaches handle code like this: command.Transaction = myTrans;
command.CommandText = """
INSERT INTO `DemoData` VALUES ('first value');
CREATE TEMPORARY TABLE temp (value VARCHAR(20));
CREATE INDEX temp_value ON temp (value);
INSERT INTO `DemoData` VALUES ('second value');
""";
command.ExecuteNonQuery(); // transaction is committed halfway through |
From dotnet/runtime#95399:
That could be another way to represent this data (and there may be benefits to being consistent across ADO.NET providers). |
In this case, you would indeed not realise there was a problem until the next command is executed, and you discover you are no longer in a transaction. I think after reflecting on this a bit more, I'm questioning whether this is something that the driver should do, given you only find the transaction state after it's too late to report an error. Perhaps what instead I am looking for is the ability to build something like this into my application: var command = new MySqlConnection();
command.QueryExecuting += e =>
{
if (e.InTransaction && e.CommandText.Contains("DROP TABLE"))
throw new InvalidOperationException("Do not manipulate tables when in a transaction");
} I actually have a class that can parse queries and identify their keywords as part of my application, so I could just use this to ensure that developers don't manipulate a table inside a transaction, i.e. ALTER, CREATE INDEX and so on. Aside from the logging extensions, I couldn't find a way to inject events or middleware into the driver without totally wrapping it in facade classes. I don't like using logging as an extension point here because it is for the entire app domain, and I might want to disable this for some connections but not others. Furthermore, I could use We might even find a place in the library to add this as an optional middleware to opt in to this best practice. Another option could be something like ASP.NET's middleware: var command = new MySqlConnection();
command.AddMiddleware<PreventManipulationOfTablesInTransaction>() What are your thoughts on this? I'm happy to develop this myself if there would be appetite. |
You could use an
The built-in |
Overview
Problem statement
Let's take a look at an example of how you can corrupt your data by accident:
The correct thing for the developer to do here is to set up the table outside of the transaction scope, but if they miss that, they will silently commit the transaction, and every subsequent command is run outside of the transaction.
To the developer moving from SQL Server to MySQL, it looks like you are within a transaction, and you may not discover the truth for many years. This is just an example from a real bug I have seen, but there are other ways to implicitly commit a transaction in this way.
Solution overview
Fortunately, the MySQL protocol provides a way for us to find this problem. The
OkPayload
can be used to detect this problem after executing a query. You can look at theServerStatus
bitfield, which contains anInTransaction
flag.The
InTransaction
flag will be in theOkPacket
of every query in the transaction until the next query that is not in the transaction. To refer back to my earlier example:So, when the developer starts a transaction, the driver should check the
OkPayload
'sServerStatus
flags forInTransaction
, and if it finds that the transaction is no longer active despite not disposing of the transaction yet, an exception can be raised to notify the developer that the last command actually committed the transaction.Example API usage:
This behaviour should not be enabled by default. Even though it represents a bug in the developer's code, the unfortunate thing about receiving the transaction state in the OK packet means that the last
INSERT
statement was actually committed to the database, so enabling this behaviour as a default will definitely cause more problems than it solves.For discussion
The text was updated successfully, but these errors were encountered: