-
Notifications
You must be signed in to change notification settings - Fork 10.9k
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
[Idea] Wrapping migrations in transactions? #302
Comments
We totally should. And the database has a transaction method which I use a lot. Seems like an easy pull request. FYI, usage is: DB::transaction(function($connection)
{
$connection->table('foo')->where('bar', 'baz'); // etc
}); |
Oh, I wasn't aware of that helper, thanks :-) |
Unfortunately, wrapping migrations in a transaction doesn't really solve this problem. Some operations can't be rolled back, even from a transaction, such as a table creation, etc. So you can still end up in a broken state, which stinks. If anyone has any alternative ideas, let me know. |
(from the migrator itself) could we wrap it in a transaction, catch ANY exceptions and then run the down() method on any exception? In the down method we're going to delete our created tables On 13/02/2013, at 10:06 AM, Taylor Otwell notifications@github.com wrote:
|
I have run into issues like this a fair bit. What @bencorlett said should work? Or another alternative is wrapping in the transaction for what supports them and what doesn't manually log what is done and reverse it manually after doing the normal db transaction rollback? |
👍 @Robbo- & @bencorlett |
Calling |
Yeah, there is still no really solid solution to this. My best advice is to do a single operation per migration so that your migrations stay very granular. |
FYI (Hope this doesnt repoen the issue but im posting this here because others may find it useful), PostgreSQL supports Transactional DDL statements - http://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis |
What about to enable transaction just for DB layers, which support REAL transactions? included DDL? @taylorotwell @conradkleinespel |
@HonzaMac Hey ! I haven't been working with Laravel for some time, so I'm afraid I won't have any valuable input on migration atomicity. I do hope this will get fixed eventually though. @taylorotwell 's suggestion on making migrations more granular seems like a good workaround though. |
Disclaimer: I very possibly do not know what I'm talking about While it is true that some operations can't be rolled back (thus making transactions alone not universally useful), this could be solved if migrations were made by defining 2-tuples representing an "up" operation and its corresponding "down" operation. This would allow us to roll back whatever did not fail before the failed "up" operation. Example transformation:
This will always go bad because we can't run either methods afterwards (down expects both tables to exist, and up expects both tables to not exist), nor can we use transactions to avoid it. My proposed solution:
First closure is up, second closure is down. Now we can roll back the first submigration after the second submigration fails, because the down closure specifies how to do so. |
@JoelSanchez interesting approach... but if you're going to go to the trouble of writing two separate functions for two Also, this doesn't really solve the problem in an elegant way. Even when only one table is being acted on (one Ideally, the migrator keeps track of the statements that are run and ONLY rolls those back if the migration doesn't complete. The trick is knowing what 'rolling back' means for each command - it may not simply be the apparent opposite of the command being used; the migrator needs to know the previous schema state to roll back predictably. |
Just imagine, that someone will use raw SQL query and all sub-migrations are not the well designed concept. All normal programmer want is just to write one up, and one down. No IF EXISTS, and no other detections of the current state. |
@HonzaMac I don't think that's very likely. I believe a better migrator is needed that compares current state to desired state and calculates the changes necessary. That could then calculate changes in both directions. This would change migrations from 'a set of commands that achieve the change you want' to 'the desired structure'. |
I think for the time being, the main issue is that the documentation does not mention that transactions are not transactional and that using only a single command per migration is therefore advisable. In my opinion, if the DB does not support this transactional, it is not the job of laravel. But I also think the developer should have the possibility to code it manually by catching exceptions and coding the rollback. I tried this, but it does not work:
I expected that I could catch the exception that occurs and roll back what I did so far. But the catch gets never called, even though an |
@crazy4chrissi That's because the Exception isn't thrown at the point where the closure you pass to Perhaps try wrapping your entire try {
Schema::table('posts', function(Blueprint $table){ ... });
}
catch(Exception $e) {
} |
I found, that in version 5.3 and 5.4 from commit 7a35d72 are transactions for databases with transactional DDL supported (PostgresSQL)! Thanks @ameliaikeda for that! |
Version number for js css
Add a timeout option to the build command
I've created a coding pattern to roll back failed migrations: https://blog.tomhanderson.com/2021/09/transactional-laravel-migrations.html |
It seems like at the moment, migrations aren't wrapped in transactions. This means if you try to run a migration that breaks half way through because you forgot some foreign key or whatever, you end up with half the database updated. Which is kind of a pain.
I can't think of a reason why this wouldn't be merged, but since it isn't in the core, I'm thinking there may actually be a reason. Could anyone tell me more about this please?
It would end the need for this kind of boilerplate code in migration files:
The text was updated successfully, but these errors were encountered: