Upgrading large & complex sites

Feb 25, 2010

Upgrading Drupal websites from one major version to the next is painful. Most people solve the problem by sticking with the old version, and I've seen a lot of people give up after trying for many days. I've myself done a good amount of (successful) upgrades from Drupal 5 to Drupal 6 in the past year and decided to share my experience and hopefully help some people get through with it. My presentation Upgrading Drupal takes a look at what issues make those upgrades so damn problematic and what solutions we can implement to solve them. I'll be giving the presentation for the third time at DrupalCamp Spain this coming week-end, and I proposed it as a session for DrupalCon SF (please vote!).

The two main problems with upgrades are complexity and time: a lot of operations need to be performed in a specific order within a short period of time to minimize downtime. Any solution that addresses these two problems will need to be automated, and the best way to do this that I found involved a VCS, drush and some minimal shell scripting. The presentation is about the WHY's, and this article is about the HOW's, with more implementation details that wouldn't fit well in a presentation.

Prerequisites:

You will need to have your code stored in a VCS. All common ones (svn, git, bzr, etc) should work, which one you choose is up to you (note that some commands differ between each VCS, so you might need to adapt the script below). Import the existing code and create a branch called drupal5 or whatever is meaningful to you. Put your code for the new Drupal version in trunk.

You need to have drush installed on your system.

A database dump of the production site should be in a file like mysitedb-2010-02-23.sql. Your files directory is also updated with a copy from the production server (rsync is useful to do that).

Finally, the user account that you use to run the script should have all permissions on the database. This is one more reason why you should only run this script in a development environment.

The Script:

And now, the script. This would go in an executable script file in the main directory of your Drupal project. Of course you will need to update it to match your project.

#! /bin/bash

Make sure we start with the code from the old branch.

svn switch -q svn://myrepository/branches/drupal5 .

Reset the database.

mysqladmin drop mysitedb mysqladmin create mysitedb mysql mysitedb < mysitedb.sql

Clear caches and the session table to speed up the execution of the updates.

drush cache clear drush sql query "TRUNCATE TABLE {sessions}"

Run any remaining pending updates for the old branch.

drush updatedb

Disable contrib modules.

# This list can be generated with: drush statusmodules –pipe drush disable admin_menu bio cck filefield token

Switch to the new Drupal version.

svn switch -q svn://myrepository/trunk . # Remove contrib modules to make sure we only run core updates first. rm -r sites/all/modules drush updatedb

Restore contrib modules.

svn up sites/all/modules

Re-enable them (the list is based on the one above, updated for the modules available in the new version).

drush enable admin_menu content_profile cck filefield token drush updatedb

Sometimes some things need to be updated.

drush cache clear </code>

Because the script first resets your environment with the old database and code, you can run it as many times as you want. After executing the script, your local site should be the new version of your site. When you are ready you can take your site offline, copy the database and files directory to your development environment, run the script, and copy your new site to the production server and put it back online.

Tips:

  • When debugging such a script, it's helpful to have a transcript. You can get that by running the script using script -C upgrade.sh.
  • It generally helps to have a symlink from /usr/bin/drush to the drush executable, this will make sure that the drush command is available in shell scripts too.
  • Don't leave code from the old Drupal version with your new code, this can sometimes lead to nasty and unexpected bugs.
  • The order in which updates are executed matters, but Drupal is not always very smart in the way it orders the updates. drush updatedb also updates all existing modules no matter whether they are enabled or not, so if you want to keep modules from being updated at a certain point in the script just remove the module's files.
  • Some structures such as views and panels require some manual steps during the upgrade. Rather than upgrading them during the script, export them to code in your new branch using the features module. Once all your updated views are exported, you can get rid of the old ones by using drush uninstall views right after drush disable ... in your script.
  • In your custom update hooks, you might need to work with structures that don't have a proper api, like menus and blocks. The install profile API has a lot of helper function that make it easier to deal with these structures.

Last but not least, if you've been confronted with upgrading Drupal websites and have found other solutions, I would really like to hear about them.