Rails transactions - mysql

I am trying to use ActiveRecord::Base.transaction. I figured that rollback doesn't work by default using Rails 1.2.6 and mysql 5.0. Playing with it a little bit more I found out that autocommit is not set to 0 in mysql connection.
Questions:
1) How do I disable autocommit in rails for all connections?
2) Will it have some negative impact on the other code that doesn't have to be transactional?

If you have a mix of code that needs explicit transactions and code that can rely on autocommit, perhaps you shouldn't disable autocommit for all connections. You're on the right track wondering if this will impact other code. If you disable autocommit, but the other code doesn't know it has to commit to have its work committed, then that'll be a problem. Uncommitted work is rolled back when the connection closes.
You should be aware that the default storage engine for MySQL is MyISAM, which doesn't support transactions at all. When you make changes to a table that uses MyISAM, the changes are effectively committed immediately, regardless of your explicit requests to begin and finish transactions, and regardless of the state of autocommit. So you won't be able to roll back no matter what, unless you created your tables using the InnoDB storage engine (or other transaction-safe storage engines such as BDB).
It's unnecessary to disable autocommit mode to use transactions in MySQL. Simply begin a transaction explicitly. The following SQL statements will be part of a transaction until you commit or rollback that transaction, regardless of the value of autocommit on your current connection.
http://dev.mysql.com/doc/refman/5.0/en/commit.html says:
With START TRANSACTION, autocommit
remains disabled until you end the
transaction with COMMIT or ROLLBACK.
The autocommit mode then reverts to
its previous state.

You don't have to disable autocommit to use transactions. When you START a transaction autocommit setting doesn't make any difference, you have to COMMIT or ROLLBACK explicitly. Moreover disabling autocommit will impact your non-transactional code.

Right answer. I was doing DROP TABLE IF EXISTS in my code in the beginning of the transactions. MySQL seems to have a problem with DROP statements within a transaction:
http://bugs.mysql.com/bug.php?id=989
Funny enough that I've guessed correct work-around for the problem sending "SET aucotommit = 0" before the DROP statement.
Thank you for your help

Not that I have any specifically not transactional code - it is mostly all ActiveRecord objects, I just want to have rollback behavior if one of my methods fails.
I will explore more , you seem to be right - I can confirm your answer in mysql console. However in my Rails application I have to do connection.execute("set autocommit = 0") to get rollback working.

Related

MySQL transactions in InnoDB

I am an MySQL newbie who's learning about transactions, and I'm using the InnoDB engine.
In the MySQL reference manual, I see that they ask to set autocommit to 0 before starting a transaction, but in both ways (setting it to either 0 or 1) I see the same behavior: the transaction is validated after commit and invalidated with Rollback. What is the difference between setting autocommit to 0 or to 1??
There are 3 modes for transactions in InnoDB:
autocommit=1 (or ON): Each statement is a transaction. (See Marc's answer)
autocommit=0 (or OFF): You must eventually issue COMMIT, else changes will be lost. (I see this modes a too error-prone to ever use.)
BEGIN (or START TRANSACTION) ... COMMIT (or ROLLBACK): This explicitly spells out the extent of the transaction. autocommit is ignored. I consider this to be 'best practice'
If autocommit is on, then every query you issue effectively runs like this:
start transaction;
...do a query ...
commit;
start transaction;
... do another query ...
commit
etc...
with autocommit off, there's no automatic transaction, and you start it yourself, which makes the code run like this:
start transaction
...do a query ...
...do another query ...
... etc...
commit;
If you only ever issue single command queries, then there's not much of a difference in behaviors. it's only when you start issuing multiple sequential queries that the new behavior really kicks in.
I think if you use MyISAM storage engine you must set autocommit to 0 but with InnoDB ints not necessary because start transaction sets autocommit to 0 LINK

How do I determine if I have uncommitted writes in a MySQL transaction?

I'm working with a very complex codebase and wish to do some introspection of how it works with MySQL. (Note that I'm using InnoDB).
Specifically, in a method I'm writing, I wish to determine if there are any outstanding (non-committed) writes in its open db connection.
Is there any MySQL command - or other means - to detect if there are uncommitted writes? (or phrased another way, determine if COMMIT will make no modifications).
This is a pretty interesting question. I don't think there is a definite way to determine if issuing commit will or will not make a difference in the session you are running.
You can see transactions with show innodb status or show engine innodb status but I don't think you can issue commit on those transactions.
INNODB_TRX table in information_schema will show currently executing transations: https://dev.mysql.com/doc/refman/5.5/en/innodb-trx-table.html and again there's not much you can do to force-commit them. You can roll them back by killing the associated process.
If you are running a transaction using START TRANSACTION in a stored procedure, you can handle commit and rollback manually. You can even set autocommit to 0 to control when to rollback and when to commit.

Does setting autocommit=0 within a transaction do anything?

I am reviewing code by another developer. He has a commit that explicitly adds set autocommit=0 at the start of a MySQL transaction. This is causing problems for other non-transactional queries in the session.
Does adding set autocommit=0 within a transaction do anything for that transaction? I assume it doesn't, since transactions implicitly do this anyway.
[The only rationale I can come up with for this commit is perhaps the db once used MyISAM (versus the current InnoDB), and this was maybe a way to mimic transactions in the former?]
If a transaction is active, it is not affected by SET autocommit = 0.
Otherwise, if the former value of autocommit was 1, SET autocommit = 0 starts a new transaction.
If you are using MariaDB and you are in doubt, you can check the in_transaction variable.
From the MySQL Reference:
After disabling autocommit mode by setting the autocommit variable to
zero, changes to transaction-safe tables (such as those for InnoDB or
NDB) are not made permanent immediately. You must use COMMIT to store
your changes to disk or ROLLBACK to ignore the changes.
Setting autocommit=0 will modify the entire session.
Transactions and non-transactional commands will need to explictly committed to affect the DB.

MySQL transactions implicit commit

I'm starting out with MySQL trnsactions and I have a doubt:
In the documentation it says:
Beginning a transaction causes any pending transaction to be
committed. See Section 13.3.3, “Statements That Cause an Implicit
Commit”, for more information.
I have more or less 5 users on the same web application ( It is a local application for testing ) and all of them share the same MySQL user to interact with the database.
My question is: If I use transactions in the code and two of them start a transaction ( because of inserting, updating or something ) Could it be that the transactions interfere with each other?
I see in the statements that cause an implicit commit Includes starting a transaction. Being a local application It's fast and hard to tell if there is something wrong going on there, every query turns out as expected but I still have the doubt.
The implicit commit occurs within a session.
So for instance you start a transaction, do some updates and then forget to close the transaction and start a new one. Then the first transaction will implicitely committed.
However, other connections to the database will not be affected by that; they have their own transactions.
You say that 5 users use the same db user. That is okay. But in order to have them perform separate operations they should not use the same connection/session.
With MySQl by default each connection has autocommit turned on. That is, each connection will commit each query immediately. For an InnoDb table each transaction is therefore atomic - it completes entirely and without interference.
For updates that require several operations you can use a transaction by using a START TRANSACTION query. Any outstanding transactions will be committed, but this won't be a problem because mostly they will have been committed anyway.
All the updates performed until a COMMIT query is received are guaranteed to be completed entirely and without interference or, in the case of a ROLLBACK, none are applied.
Other transations from other connections see a consistent view of the database while this is going on.
This property is ACID compliance (Atomicity, Consistency, Isolation, Durability) You should be fine with an InnoDB table.
Other table types may implement different levels of ACID compliance. If you have a need to use one you should check it carefully.
This is a much simplified veiw of transaction handling. There is more detail on the MySQL web site here and you can read about ACID compliance here

Recovery after wrong MySQL update query?

I made a wrong update query in my table.
I forgot to make an id field in the WHERE clause.
So that updated all my rows.
How to recover that?
I didn't have a backup....
There are two lessons to be learned here:
Backup data
Perform UPDATE/DELETE statements within a transaction, so you can use ROLLBACK if things don't go as planned
Being aware of the transaction (autocommit, explicit and implicit) handling for your database can save you from having to restore data from a backup.
Transactions control data manipulation statement(s) to ensure they are atomic. Being "atomic" means the transaction either occurs, or it does not. The only way to signal the completion of the transaction to database is by using either a COMMIT or ROLLBACK statement (per ANSI-92, which sadly did not include syntax for creating/beginning a transaction so it is vendor specific). COMMIT applies the changes (if any) made within the transaction. ROLLBACK disregards whatever actions took place within the transaction - highly desirable when an UPDATE/DELETE statement does something unintended.
Typically individual DML (Insert, Update, Delete) statements are performed in an autocommit transaction - they are committed as soon as the statement successfully completes. Which means there's no opportunity to roll back the database to the state prior to the statement having been run in cases like yours. When something goes wrong, the only restoration option available is to reconstruct the data from a backup (providing one exists). In MySQL, autocommit is on by default for InnoDB - MyISAM doesn't support transactions. It can be disabled by using:
SET autocommit = 0
An explicit transaction is when statement(s) are wrapped within an explicitly defined transaction code block - for MySQL, that's START TRANSACTION. It also requires an explicitly made COMMIT or ROLLBACK statement at the end of the transaction. Nested transactions is beyond the scope of this topic.
Implicit transactions are slightly different from explicit ones. Implicit transactions do not require explicity defining a transaction. However, like explicit transactions they require a COMMIT or ROLLBACK statement to be supplied.
Conclusion
Explicit transactions are the most ideal solution - they require a statement, COMMIT or ROLLBACK, to finalize the transaction, and what is happening is clearly stated for others to read should there be a need. Implicit transactions are OK if working with the database interactively, but COMMIT statements should only be specified once results have been tested & thoroughly determined to be valid.
That means you should use:
SET autocommit = 0;
START TRANSACTION;
UPDATE ...;
...and only use COMMIT; when the results are correct.
That said, UPDATE and DELETE statements typically only return the number of rows affected, not specific details. Convert such statements into SELECT statements & review the results to ensure correctness prior to attempting the UPDATE/DELETE statement.
Addendum
DDL (Data Definition Language) statements are automatically committed - they do not require a COMMIT statement. IE: Table, index, stored procedure, database, and view creation or alteration statements.
Sorry man, but the chances of restoring an overwritten MySQL database are usually close to zero. Different from deleting a file, overwriting a record actually and physically overwrites the existing data in most cases.
To be prepared if anything comes up here, you should stop your MySQL server, and make a copy of the physical directory containing the database so nothing can get overwritten further: A simple copy+paste of the data folder to a different location should do.
But don't get your hopes up - I think there's nothing that can be done really.
You may want to set up a frequent database backup for the future. There are many solutions around; one of the simplest, most reliable and easiest to automate (using at or cron in Linux, or the task scheduler in Windows) is MySQL's own mysqldump.
Sorry to say that, but there is no way to restore the old field values without a backup.
Don't shoot the messenger...
Do you have binlogs enabled? You can recover by accessing the binlogs.