mysql: working around implicit transaction commits? - mysql

I wrote a tool for our project, for applying sql update files that were committed, to the DB. Whenever run (on deployment), it calculates the list of update files which need to be applied, and applies them iniside a transaction.
Recently I became aware of an issue: mysql would implicitly commit a transaction, whenever DDL statements (like create) are executed. http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
This is an issue for me, as sometimes an sql update file contains several statements, which as I understand will result in committing the transaction in the middle of executing the update file. This is a problem, because whenever a subsequent update will fail (which happens from time to time) I want to be able to rollback the transaction, or at least track which update files where applied (completely) and which were not.
Is there a way around the implicit transactions issue? I.e. is there a way to rollback a sequence of DDL statements whenever one of them fail?
Any other suggestions how I can handle the issue?
Thanks
Gidi

No. MySQL does not support transactional DDL. You either need to separate your DDL statements from DML statements, or perhaps try to use migration tool like RuckUsing

Related

Liquibase commits changeset partially

Using liquibase v3.6.3 on MySQL. If I understood correctly, every CHANGESET is run into a SQL transaction by default. However, seems to me that transactions are being commites in a CHANGE basis. When running this script
databaseChangeLog:
- changeSet:
id: changeset-
changes:
- renameTable:
oldTableName: old_table
newTableName: new_table
- addColumn:
columns:
- column:
name: test_column_name
type: varchar(255)
tableName: other_table
If the addColumn tag fails because some SQL exception (i.e constraint check or other), then the databasechangelog table won't be updated, which I don't expect to, as the changeset failed. However, the firs statement DID pass and my table is now called new_table.
Of course, if I correct the problem causing the second one to fail and retry the update, it will fail because old_table doesn't exist anymore.
I'm aware of this paragraph in the liquibase documentation
Liquibase attempts to execute each changeSet in a transaction that is
committed at the end, or rolled back if there is an error. Some
databases will auto-commit statements which interferes with this
transaction setup and could lead to an unexpected database state.
Therefore, it is usually best to have just one change per changeSet
unless there is a group of non-auto-committing changes that you want
applied as a transaction such as inserting data.
https://www.liquibase.org/documentation/changeset.html
but I don't really understand it. Auto-commit means auto commiting A TRANSACTION. If all the changeset is wrapped in a transaction, why are there only some changes passing? Should liquibase rollback the whole transaction?
Any best practices for this? Can't we manually set transactions in liquibase?
It is not Liquibase that is committing a changeset partially.
I have worked with many databases and a basic concept for all the databases I used, is that a transaction combines data modifications (DML) only.
DDL is never part of a transaction. It is always executed immediately and an open transaction is automatically committed before it is executed.
The reason for that is that the rollback command of a database can handle data modifications only. It can't handle DDL. And if a rollback is not possible anymore then keeping the transaction open becomes useless.
So, Liquibase does create a transaction and commits all changes at the end as the doucmentation states. But that works only if the changeset contains DML only, no DDL.
And because of that DDL and DML should never be mixed in one changeset and every DDL statement should be in a separate changeset. Otherwise there is no way that Liquibase can prevent a changeset from partially succeeding and causing trouble when trying to rollback.
Mysql (and many other relational databases) has implicit commit concept. Most of database trigger the commit implicitly (just like you call COMMIT yourself) to end the current active transaction before(or after) executing the DDL statements.
Liquibase tries to apply specified changes of one changeset under single transaction. In your case, there are two changes and both are DDL statements (RENAME TABLE and ALTER TABLE), under one change set. Both statements will trigger the implicit commit which would leave the database inconsistent state if later statement fails.
More information on mysql implicit commit on their website including the comprehensive list of SQL statements which trigger the implicit commits.
Hope it helps.

Percona XtraDB Cluster multi-node writing and unexpected deadlocks outside of transaction?

I am having trouble finding an answer to this using google or Stack Overflow, so perhaps people familiar with Percona XtraDB can help answer this. I fully understand how unexpected deadlocks can occur as outlined in this article, and the solution is to make sure you wrap your transactions with retry logic so you can restart them if they fail. We already do that.
https://www.percona.com/blog/2012/08/17/percona-xtradb-cluster-multi-node-writing-and-unexpected-deadlocks/
My questions is about normal updates that occur outside of a transaction in auto commit mode. Normally if you are writing only to a single SQL DB and perform an update, you get a last in wins scenario so whoever executes the statement last, is golden. Any other data is lost so if two updates occur at the same time, one of them will take hold and the others data is essentially lost.
Now what happens in a multi master environment with the same thing? The difference in cluster mode with multi master is that the deadlock can occur at the point where the commit happens as opposed to when the lock is first taken on the table. So in auto commit mode, the data will get written to the DB but then it could fail when it tries to commit that to the other nodes in the cluster if something else modified the exact same record at the same time. Clearly the simply solution is to re-execute the update again and it would seem to me that the database itself should be able to handle this, since it is a single statement in auto commit mode?
So is that what happens in this scenario, or do I need to start wrapping all my update code in retry handling as well and retry it myself when this fails?
Autocommit is still a transaction; a single statement transaction. Your single statement is just wrapped up in BEGIN/COMMIT for you. I believe your logic is inverted. In PXC, the rule is "commit first wins". If you start a manual transaction on node1 (ie: autocommit=0; BEGIN;) and UPDATE id=1 and don't commit then on node2 you autocommit an update to the same row, that will succeed on node2 and succeed on node1. When you commit the manual UPDATE, you will get a deadlock error. This is correct behavior.
It doesn't matter if autocommit or not; whichever commits first wins and the other transaction must re-try. This is the reason why we don't recommend writing to multiple nodes in PXC.
Yes, if you want to write to multiple nodes, you need to adjust your code to "try-catch-retry" handle this error case.

Transactional DDL workflow for MySQL

I was a little surprised to discover that DDL statements (alter table, create index etc) implicitly commit the current transaction in MySQL. Coming from MS SQL Server, the ability to do database alterations in a transaction locally (that was then rolled back) was an important part of my workflow. For continuous integration, the rollback was used if the migration hiccuped for any reason, so that at least we did not leave the database in a half-migrated state.
How do people solve these two problems when using MySQL with migrations and continuous integration?
DDL statements cause an implicit commit and there is nothing you can do about it. There is no way to stop this behaviour.
Which DDL statements have this behaviour changes over time so you need to check for your version.
5.1 http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html
5.5 http://dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
5.6 http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html
When we are just extending the schema, new tables/columns/views/procs/etc, that will not affect existing code then automation is OK, just check for errors and fix them.
When they will affect existing code then you need to devise a strategy on a case by case basis. Since there is no rollback you need your own backout plan and you need to test it thoroughly.
Since it is case-by-case there is not a lot that I can offer in the way of help for your particular situation.
One possibility is doing DDL changes in a non-destructive-manner, which would include:
split logic in DDL/DCL (+1 to reverse all) and DML
run only the DDL/DCL script adding columns, new tables, ..
depending on result:
on success, apply the DML changes,
on fail, apply reverse DDL/DCL script removing the stuff you wanted to add in second step (obviously with some errors "does not exist" depending on how far step 1 got)
remove what is not needed anymore, drop old columns/tables

Restore Truncated Data In Mysql [duplicate]

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.

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.