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.
Related
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);
How do you go about changing column names and types in your rails app? Do you create a new migration to make the changes, or do you rollback, edit your migration file, and then migrate again?
What's the "proper" way to do this in Rails?
It sort of depends on when this happened in your development cycle, If you recently made the change and haven't pushed it out into a public repo, then you indeed might want to do the rollback thing and then edit the migration files and migrate again, just to keep things clean. But if it's a change to a migration that's a few migrations back then you should create a new migration that changes the rows and columns to the "new" old values.
Well, to undo a migration you typically want to roll it back:
bundle exec rake db:rollback
Where VERSION= can also be specified. If you wanted to change it to something entirely new, you would make a new migration. Typically you shouldn't be touching old migrations at all.
Rails gives you the choice of creating a migration and changing it a various of ways. So there is no "ruby developers" technique of choice.
However, every time you create a migration, a file is created, and it represents the history of your development. There are a lot of cases that a simpler file should be uselfull, like a code that other ruby beginners would have to look at and modify. On other cases may be necessary to an advanced developer understand changes and improvements made on the code, specially if it is a code running for a long time (some years maybe).
As I understand it the point of migrations is so you can revert the database back to a known state during the last stages of development.
Right now I'm still "fleshing" out my first Rails app and I'm wondering if its ok to roll up my migrations into bigger ones rather than dozens of changes.
The point of migrations is that you basically have a log of database changes, so then other developers can know what changes have been made, or to make sure your production environment gets the same changes you made during development.
As for your question: sure. If you create a new model, and then after a few minutes decide "this column could just be a string instead of text", roll back your migration, and change the column and then migrate again. No need to create a new migration.
Unless you've already committed the previous migration to source control that may have been fetched by other developers, or you've already applied the migration on the production server. Then you should use a new migration.
As an addendum to rspeicher, I limit the constraint to whether a migration has been released, not to whether it has been made available to other developers. If it's still pre-release, then the development team can be informed of any need to run migrations for any updates of the master code repository by using post-fetch hooks for the SCM being used. This is true of any configuration management change, not just migrations. For example, changing an implementation of something in the initializers folder may have no effect on a running instance of script/server in development mode. This is a ultimately a necessary mechanism for most teams in most technologies as well as for some configurations of continuous integration. Or, you need excellent communications channels in the team to make sure that everyone knows that a configuration change and restart is necessary.
In Rails, migrations have a down method by default for reverting a migration. In what scenario would I ever want to revert a migration, though?
Some thoughts:
Whether in development or production, I always have a snapshot of my database to go back to, before I even run the migrations. Especially for migrations which perform data conversion, I find in most cases that reverting a snapshot is even faster than reverting a migration. (So I would never do it in a rush!)
If a migration were to fail, it would either:
fail with an exception on a non-transactional database, and thus leave the database broken, or
fail with an exception and roll back the transaction, and thus there would be no need to revert otherwise.
If the changes made are in production (or late in development), and later turn out to be a mistake, I would fix my mistake in a new migration. I would not revert the old one. In development, I'd simply delete the migration.
I also find that the down method introduces extra code in which I repeat myself, and thus may introduce new bugs. This is against the DRY principle.
So I'm curious about the pros, because I can't think of any.
In development, it is easy and fast to incrementally "improve" migrations by using the down method automatically. Eg
Create a migration and migrate to it
Realize you need to make a change
Migrate to the ver prior to your new migration by using db:migrate with a version
Improve/fix your migration
Rerun the migration task
Your method of taking snapshots works fine. But rails includes the same effect auto-magically using the "down" migration techniques. Works with all db's, tastes great
Added:
For production, I agree that a down migration shouldn't be needed. But sometimes mistakes happen and you need to roll back. The down migration path gives you a first, and quick opportunity to fix things in an emergency situation during an upgrade that goes wrong.
-- it is much faster to try a down migration in an emergency than to restore the db using a checkpoint.
The "down" migration used for DB Rollbacks is in place so that every action has an equal and opposite action. It takes the onus off of the developer to maintain database snapshots, and allows them to use code to achieve the same ends. As Larry K said,
they're good for situations like so:
Add a database column called 'resubmitted', it's a boolean.
Product owner says they can resubmit multiple times, so change that column needs to be an int
Now, if you're 10 or 15 migrations deep, it's easier to just write a new one instead of losing all the dev data in the new tables/columns by doing a rollback. However, if you've just written that migration, it's cleaner and less cluttered to roll back, change the migration, and re-run it.
The other extremely useful feature of rollbacks is this:
Developer 1 has his own Dev DB. He writes a migration and runs it.
Developer 1 commits his migration to source control
Developer 2 has her own Dev DB. She writes a migration and runs it.
Developer 2 updates from source control
Developer 2 tries to run migrations but can't, as her local DB says "the latest migration has already been run", since her migration (the latest one), has technically already been run. Now she needs to rollback, then do a db:migrate to get all migrations in her local DB.
The idea of running a down migration in production horrifies me. Back when the preferred way to rollback all migrations was rake db:migrate VERSION=0 I would do this all the time in development. However, then I got paranoid that because it was committed to muscle memory I would accidentally type this on a production server when I meant to simply migrate.
Because of this paranoia, I add the following to all of my down methods.
def self.down
if Rails.env.production?
raise ActiveRecord::IrreversibleMigration
else
drop_table :foo_bars
end
end
This way, it still works in development, but I can't accidentally nuke my production database from orbit while half-asleep at 2:00 AM.
When a migration hasn't had the expected result, it is better to roll back and rewrite it than to keep the failed migration in the code.
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.