How are Migration Scripts applied in Rails 2.1 and higher? - ruby-on-rails

I'm not a Rails developer (currently) so please forgive my ignorance on this.
One thing I've always liked about Rails is migrations and how it fills a need that's common across all languages and platforms. With that said, I am curious to understand what a certain scenario would result with the changes made in 2.1.
Rails 2.1 and higher, from what I can tell, made two changes to the migrations logic. The first was to use timestamp based script names when generated in order to reduce the probability of 2 developers working on the same file at the same time before adding the file to source control. So instead of 002_test.rb, it is now 20090729123456_test.rb when the script is generated.
The second item was that the Schema_Info table was replaced with the Schema_Migrations table that presented a list of migrations and not just the latest version number.
Looking through the Rails source, I noticed that it took the "current version" of the schema as the max version found in the Schema_Migration table.
Here's the scenario I'm trying to figure out:
Developer A generates a new script: 20090729120000_test.rb.
Developer B generates a new script: 20090729130000_test.rb.
Developer B migrates his script to the database first by not specifying the version number and assuming that Developer A's script isn't added yet.
What happens when Developer A adds his script and tries to migrate to the latest version since his script version (based on the time stamp) is less than the currently applied version now?

I'm not positive, but I believe that he would have to do a "rake db:rollback" to undo the Developer B migration, then run "rake db:migrate" to do both of them in the proper order. Of course, if two developers are working independently on tables that require no integration with one another (as this case shows, since Developer B didn't have to wait for Developer A to run his migration), developer A can simply add one to the timestamp of Developer B's migration and it will be in proper order once again.

The short answer is: don't worry about it.
rake db:migrate will attempt to run any migrations that are not found in the schema_migrations table. It doesn't matter if there are newer migrations that have already been run.
If B is dependent on A and must be run in that order, then you might have a problem, but that's an issue between the developers.

Related

Entity Framework bug? "Context changed" Error, even when not

I have gotten myself into an odd Groundhog Day scenario with an MVC application of mine.
Unless I apply my workaround (Later in this question) each time I debug the application I'm presented with this error:
The model backing the 'UsersContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database (http://go.microsoft.com/fwlink/?LinkId=238269).
I have not changed the model.
My workaround workflow is:
Add-Migration WHATEVERNAME (Makes a blank migration)
Delete this migration.
Run: Update-Database
Recompile & Run (Now without error)
Notes:
The __MigrationHistory hashes of the latest migration match in both script and in the database.
I have my MVC application & EF project as separate projects.
I have tried creating an -IgnoreChanges migration, to see if applying this would mitigate the issue. It did not.
This is quite frustrating, how would I solve this issue permanently?
Note: Automatic migrations are not suitable for my scenario.
Well, it's almost impossible to understand what's wrong without knowing much more detail. So all I can do is give you some clues of what you could try.
Stopping and restarting the app should not cause the DB to get out of date. Is it only when debugging? Have you tried running the app without debugging? Then recycle the app pool and running the app again.
Do you have any weird post-build step that will overwrite some DLL in your "bin" folder?
Is your app doing something that changes the database schema, thereby invalidating it when you next start up? Run SQL profiler to check what is happening to the DB when your app starts up.
Migrate back to the first version of your schema, and then back again (backup your DB first):
update-database -TargetMigration:0 -verbose
then
update-database -verbose
Temporarily comment out the bulk of your app to try to isolate the cause.
Create a brand new app with EF configured in the same way, copy the connection string and see if it happens for that. If not, then there must be something different. If yes, then show us your EF settings.
Hopefully something here that could give you an idea at least. Good luck!
Enabling migrations sets up the whole migration system. But to enable automatic migrations you have to include -EnableAutomaticMigrations which simply adds the line
AutomaticMigrationsEnabled = true;
into the newly generated Configurations.cs file.
In conjunction with the database initializer, development turnaround is more streamlined because you no longer have to type add-migration and update-database every time you make a change. That will happen automatically now. However, that’s not enough, if you want column removals you have to also perform step 3, where automatic data loss is supported.
When you are ready to release software (internally or externally) where you need strict version control and to upgrade databases on site, you should remove automatic migrations and add a manual migration point.
This can happen when updating to EF6 which made schema changes to the _MigrationHistory table (https://msdn.microsoft.com/en-us/data/jj591621)
The EF6 version has a new column ContextKey so the migration is probably trying to add that column.
I'm guessing if you scaffold it will just be making those changes - or perhaps there's something you changed a long time ago that wasn't 'picked up' yet for some reason.
OR if you just don't want to deal with it right now you can disable migrations temporarily.
System.Data.Entity.Database.SetInitializer<UsersContext>(null);

Rails Migration Order and Git

Since doing migrations with rails + git is type of a pain, a new thorn has sprung..
Before I am doing any harm to my prod DB, would the following situation cause havoc? If so, how would I handle it?
I am working a long-term feature in a separate branch (feature/long-term). This feature is an overhaul of a lot of components and it will take awhile to complete. This feature has new migrations, which were migrated to the localhost DB.
meanwhile, I need to fix/add a migration to the prod system via another branch (feature/quick-fix). This has a migration file with date later than the feature/long-term migration.
The migrations of the quick-fix and the long-term have nothing to do with each other, they do not collide and work on separate tables. It doesn't matter what order they are run.
If I merge feature/quick-fix to master and db:migrate and in a few days/weeks merge feature/long-term the migration files order would be the long-term first.
Would this affect the DB in some way? (the prod DB is important, so I don't want to reset)
What you described is a very common development workflow (especially so in teams with more members) and it's perfectly safe for your production DB.
Rails, as of version 2.1, is smart enough to keep a list of all migrations ever run, instead of just the latest migration version run. This information is stored on a separate table aptly named schema_migrations.
So, if you push a new migration today, say 20140527_quick_fix.rb, and a month after that you push a new (but with an older timestamp) one 20140101_long_term_feature.rb, Rails will still know that the latter was never run in your production environment so during rake db:migrate it will process it, as you would expect. The newest won't be run again of course as the Rails would know that it has already been processed.
From the official documentation:
Rails versions 2.0 and prior used to create a table called schema_info when using migrations. This table contained the version of the schema as of the last applied migration.
Starting with Rails 2.1, the schema_info table is (automatically) replaced by the schema_migrations table, which contains the version numbers of all the migrations applied.
As a result, it is now possible to add migration files that are numbered lower than the current schema version: when migrating up, those never-applied “interleaved” migrations will be automatically applied, and when migrating down, never-applied “interleaved” migrations will be skipped.

Ruby on Rails Database Deployment with Gerrit

I'm considering using Ruby on Rails for my next project. Understanding the deployment of a rails website is easy enough to understand (sounds like I'll be using Phusion Passenger)
But now I'm trying to figure out the database. I see a lot about "database migrations", which allow me to update the database using ruby code. I also see that I'm allowed to create both an up and down variant of these migrations.
However, I can only fathom how this works cleanly in a single direction. Imagine if I suddenly say "The color column cannot be null". So, the up will make it required and give all NULL entries a default value. But what will the down do? If you care about it being identical to how it started, you can't just set the default values back to NULL.
This doesn't really matter much for releases to production. That will likely just be done in a single direction (in the up direction). However, I want to use Gerrit for code reviews as well as setting up a bot to run a build before allowing check-ins...
So how could that work? From one code review to the next, the build server will check out the new set of code, and run the migrations? But when this happens, it won't even retain the migration code from before, so how could it run the down steps? As an simpler example, I do not see how I could check out an old version of the code and "db migrate" backwards.
Yes, you can't check out an old version of the code and then run a down migration from a newer version of the code. You would need to run the down migration before rolling back to the older code.
There are many, many cases where a down migration is just not practical or possible. That's not necessarily a bad thing. It just means that you have defined a 'point of no return', where you won't be able to restore your database to an earlier state.
Migrations like creating a table or adding a column are easily reversed by simply destroying that table or removing that column. However, if you are doing something more complex, such as adding default values or moving data around, then you can tell Rails that it's not possible to reverse this migration:
def down
raise ActiveRecord::IrreversibleMigration
end
I would recommend that Gerrit should never assume anything about the database. It should start with a fresh database each time a new version is deployed, and run db:migrate to run all your migrations. You can use gems like factory_girl to populate your app with demo data for testing purposes.

Rails migration change sequence or order

I wrote a few migrations for my Rails 3 app, but I would like to change the order of the migrations. How can I change the migration order or sequence? Is it as simple as renaming the migration file with what appears to be the timestamp?
I know this is an odd question, but basically, I made a mess of my migrations and removed some old migrations and now I need to drop a table before creating a new one. I also know I can include the drop statement in the create-the-new-table migration, but I'm curious to know how to reorder migrations.
Yes, it runs the migrations which have not been run in the order of the prefix. In earlier versions of rails, maybe 2.1 or 2.2, they used to be numbered starting with 01, but they switched to timestamps.
There is a table which keeps track of which migrations have run. The intentions is, multiple developers my have added migrations, and checked them in version control later. So, there may be a migration which has not run, but is numbered before the highest numbered migration which has run.
If you change the migration sequence, (and I have) it's better to first down version before all the migrations you are re-sequencing. Use the VERSION option with db:migrate. If the highest numbered migration that you want to keep (not run the down) is 20120318143249, call it this way.
rake db:migrate VERSION=20120318143249
I often run the down on a migration, and re-run it until I get the details of the migration to my satisfaction. Sometimes I re-order them, when I want to work on one of them, and I want it to be the last.
Yes, the prefix on the filename is what determines the order of execution. However, keep in mind this will only change your current system if you wipe your DB/start over.
I was having a similar issue because the migrations were not happening in the right order.
After reading Mitch's answered I decided to rename the migrations to sequence the dates the way I needed.
After this fix I ran rails db:migrate and it worked.
Not the most elegant solution but it worked so I thought I'd share it.

Why does new Rails db migration file start with datestamp instead of sequence number?

Whenever I use script/generate to generate a new scaffold for a change to my Rails database, the new migration file is prepended by a datestamp (e.g. 200903140912_create_users.rb) instead of a sequence number (e.g. 004_create_users.rb).
I then have to manually change the file name to fit in with the rest of the migration files.
Does anyone know how to fix this?
System: Mac OS X Leopard 10.5.6
Rails: v2.2.2
Ruby: v1.8.6
This was introduced in Rails 2.1. According to the migrations docs, you can revert it by setting config.active_record.timestamped_migrations to false in config/environment.rb.
I'm not sure why they made the decision, but I can tell you how it's made my life easier. On a team it was common for two people to create migrations at roughly the same time. If the last production migration was 007 then both of the new ones would be 008. The second person to commit would have a headache on their hands trying to sort it out, and the timestamps make that conflict a lot less likely.
The decision was made because when people worked together on the same project they would often try to create a migration with their new changes. This would lead to the issue where two people were working on the same project making separate changes but both generating a migration with the same number. The Rails core team decided to change it to a UTC timestamp since it's way less likely (but still possible!) that two (or more) developers would be creating a migration in the same second, rather than the same sequence.
It is also worth mentioning that using the UTC timestamp helps with sequence that migrations are run when the developers might be in separate time zones.

Resources