drush

Git bisect saves the day

I have been working on a private project for quite some time using git. Yesterday, I noticed that one of the views on the site was taking around 10 seconds to generate instead of less than a second like it used to. I scanned through the recent commits, scratched my head, and messed with a bunch of stuff, but to no avail. I reverted to a commit from over a month ago just for kicks and sure enough the view rendered quickly again. So I decided it was time to learn how to use git bisect.

From git documentation:

Find by binary search the change that introduced a bug

I had read in passing the general idea behind git bisect and that you could use a script that returned pass or fail to automate the process. After reading through the man page I confirmed that a script may be used and simply needs to return exits status code 0 for pass and 1 for fail. So I figured I could write a script that manually executes the view and check to see if the amount of time required was over a certain threshold. Interestingly it seems the view executes much faster using the script than inside a normal page request. Thus the threshold used is much lower then one might expect.

I created two files since I wanted to use drush php-script and follow the documentation's recommendation by placing the scripts outside the repository. The first script is a wrapper that simply changes to the directory in which Drupal is installed and then executes the drush command.

git_bisect.sh

#!/bin/bash
cd /path/to/drupal
drush php-script ~/check_view.php

check_view.php

<?php
drupal_flush_all_caches
();

$view views_get_view('MY_CUSTOM_VIEW');
$view->set_arguments(array(3)); // Test data.
$start microtime(TRUE);
$view->execute();
$stop microtime(TRUE);

// 0: pass, 1: fail
$diff $stop $start;
$status $diff 0.3 0// Threshold.

var_dump($diff);
var_dump($status);

// If exit(0) is called drush still views it as abnormal shutdown and sets code
// to non-zero so only call when we want abnormal shutdown.
if ($status != 0) {
  exit(
1);
}
?>

My case was a bit more complex since code beyond a certain point was incompatible since I had to backup a related module to work with old revisions.

078d60ad18b73ec356436a7ea30528c95c9c4844 (bad)
3f1cfca83821a6b2d694cf228e5d8af3db20922f (good)

I ran the following inside the repository directory.

git bisect start 078d60ad18b73ec356436a7ea30528c95c9c4844 3f1cfca83821a6b2d694cf228e5d8af3db20922f --
git bisect run ~/git_bisect.sh

I ended up with the following result (-- indicates where I scrubbed data for privacy).

running /home/boombatower/git_bisect.sh
float(0.43191289901733)
int(1)
Drush command terminated abnormally due to an unrecoverable error.
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[50dcca7e9cec514c2bcc24156cd8b4622eb2cd3e] -- message --
running /home/boombatower/git_bisect.sh
float(0.53287100791931)
int(1)
Drush command terminated abnormally due to an unrecoverable error.
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[37e793d693a75a55470e6a92f5e3f30649ee2214] -- message --
running /home/boombatower/git_bisect.sh
float(0.18644404411316)
int(0)
Bisecting: 1 revision left to test after this (roughly 1 step)
[77ddb40b26fe2436cd7a15549109ffa9095d6995] -- message --
running /home/boombatower/git_bisect.sh
float(0.51487994194031)
int(1)
Drush command terminated abnormally due to an unrecoverable error.
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[08d99c98f4b9d837775db47770bc125727d93dc6] -- message --
running /home/boombatower/git_bisect.sh
float(0.1781919002533)
int(0)
77ddb40b26fe2436cd7a15549109ffa9095d6995 is the first bad commit
commit 77ddb40b26fe2436cd7a15549109ffa9095d6995
Author: --
Date:   --
 
    -- message --
 
:040000 040000 a4d3a8cb990d0eff7a7dd87c941a3f12b768feaf 10ab97d60a9b03a56862828e0f9f66cf7f4ef6b4 M      --
:100644 100644 7254a3027edfb35e10b948a9dcd994a9fbdd44a3 0a8ef37931373929328fe6458bf1f595549d265a M      --
bisect run success

Sure enough the "first bad commit" was indeed the commit that caused the performance issue. Very cool!

Drush, Drush Make, other Drupal packages, and development setup for openSUSE

I have recently added drush and drush make packages to my openSUSE repository. For more information or to report bugs on the packages please visit their respective project pages: drush and drush_make.

To install the packages you can use the one-click installers provided by the build service or manually add my repository and install the packages as shown bellow.

su
zypper ar http://download.opensuse.org/repositories/home:/boombatower/openSUSE_11.[2 or 3]/ home:boombatower
zypper in drush drush_make

Also note my existing Drupal packages: drupal-dev and drupal-vhosts, as well as the LAMP Drupal one-click pattern. The latter package (drupal-vhosts) is very useful in setting up a multi-drupal version, multi-subdomain work environment.

To use simply install and run the command to point the virtual hosts to the directory containing your Drupal code.

su
zypper in drupal-vhosts
drupal-vhost /path/to/main/software/directory

Either edit the hosts file directory or use YaST -> Network Services -> Hostnames to add an entry for every Drupal version you wish to run (package currently supports 6, 7, and 8). The relevant lines from my /etc/hosts file are as follows.

127.0.0.1       d7x.loc 
127.0.0.1       d6x.loc 

For my setup I use /home/boombatower/software for all my code with Drupal cores in drupal-7 and drupal-6 directories respectively. If you want to have subdomains for your sites just add more entries to /etc/hosts and use the respective Drupal sites directories.

Personally, I then create symbolic links to all my modules so that the code resides in the root of the software directory, but can be used by any respective site. This makes the paths to modules and what not much shorter and easier to reference from multiple specific sub-sites and what not. For example to link pathauto to the all modules directory for Drupal 7 I would execute the following.

ln -s ~/software/pathauto ~/software/drupal-7/sites/all/modules

Or from within the sites/all/modules directory as I tend to do.

ln -s ~/software/pathauto .

Also note, to enable mod_rewrite and get clean URLs to work simply go to YaST -> System -> /etc/sysconfig Editor then Network -> WWW -> Apache 2 -> APACHE_MODULES and add rewrite to the end of the line. You can do so manually of course as well.

In order for the virtual host changes and apache module addition to take effect you will need to restart apache and for the /etc/hosts changes you need to restart the network which you can do with the following commands run as root.

rcapache2 restart
rcnetwork restart

The end result of all this work is beautiful URLs like: http://d7x.loc/node/1, http://foo.d7x.loc/user, and http://d6x.loc/.

I also create a similar structure within MySQL. First, I set an easy to remember MySQL root password since there really no reason for it not to be easy to remember and it is helpful when having to enter it a lot.

mysqladmin -u root password EASY_TO_REMEMBER_PASSWORD

Next setup a drupal user in MySQL and give the user all permissions to d7x* and d6x* named databases which allows us to use a single user for all our drupal sites (much easier to remember login info) without having to update privileges all the time. I name my databases the same as virtual hosts, so for d7x.loc I would have d7x as the database name and for foo.d7x.loc I would have d7x-foo.

CREATE USER 'drupal'@'localhost' IDENTIFIED BY  'EASY_TO_REMEMBER_PASSWORD';
GRANT USAGE ON * . * TO  'drupal'@'localhost' IDENTIFIED BY  'SAME_EASY_TO_REMEMBER_PASSWORD' ;
 
GRANT ALL PRIVILEGES ON  `d7x%` . * TO  'drupal'@'localhost';
GRANT ALL PRIVILEGES ON  `d6x%` . * TO  'drupal'@'localhost';

Anytime you want to add a database for a new site simply run the following.

CREATE DATABASE  `DATABASE_NAME` ;

Enjoy your fancy development environment!

Subscribe to RSS - drush