Monday, March 16, 2009

rake invoke versus execute: a huge difference

Those more familiar with the intricacies of rake might already know this, but Rake::Task['taskname'].invoke and Rake::Task['taskname'].execute are very very different in how they behave w.r.t. dependencies and chaining.

First, here's a background example explaining how dependencies aren't run when using execute, but are always run when using invoke.

http://chrisroos.co.uk/blog/2007-12-06-ruby-rake-invoke-vs-execute

I additionally discovered that a task called using invoke will only run the *first* time you invoke it, not a second, third, or fourth time.

In my particular case, I wrote a rake task to stop and start mysql on mac osx, and then used it within a number of other tasks. Each task, when I tested it individually ran fine, but when I tried to chain the series of tasks together, everything went horribly awry, and it looked as if mysql wasn't properly restarting. I went digging for why, and discovered that because I was using invoke on my task, the mysql stop and mysql start task was only running when I called it from the first of the chained tasks. Switching to execute resolved this problem.

Here's the example that demonstrates; note that the code for both invoke and execute tests try to run the stop/start sequence twice (stop/start/stop/start). In practice though, the first test with invoke only runs the stop/start sequence once, while the second with execute runs the stop/start sequence twice.


desc 'stop mysql on macosx, requires sudo'
task :stop_mysql_macosx do
puts "Attempting to stop mysql..."
`sudo /Library/StartupItems/MySQLCOM/MySQLCOM stop`
sleep 2
mysql_process=`ps aux | grep mysql | egrep -v "grep|rake"`
raise "mysql did not stop properly, got mysql_process #{mysql_process}" if !(mysql_process == "")
puts "Successfully stopped mysql."
end

desc 'start mysql on macosx, requires sudo'
task :start_mysql_macosx do
puts "Attempting to start mysql..."
`sudo /Library/StartupItems/MySQLCOM/MySQLCOM start`
sleep 2
mysql_process=`ps aux | grep mysql | egrep -v "grep|rake"`
raise "mysql did not start properly" if (mysql_process == "")
puts "Successfully started mysql."
end

desc 'test start/stop mysql macosx using invoke'
task :test_start_stop_mysql_macosx_using_invoke do
Rake::Task['db:stop_mysql_macosx'].invoke
Rake::Task['db:start_mysql_macosx'].invoke
Rake::Task['db:stop_mysql_macosx'].invoke
Rake::Task['db:start_mysql_macosx'].invoke
end

desc 'test start/stop mysql macosx using execute'
task :test_start_stop_mysql_macosx_using_execute do
Rake::Task['db:stop_mysql_macosx'].execute
Rake::Task['db:start_mysql_macosx'].execute
Rake::Task['db:stop_mysql_macosx'].execute
Rake::Task['db:start_mysql_macosx'].execute
end


~/tmz/aardvark/trunk/dragonfly > sudo rake db:test_start_stop_mysql_macosx_using_invoke
(in /Users/nathan/tmz/aardvark/trunk/dragonfly)
Attempting to stop mysql...
Successfully stopped mysql.
Attempting to start mysql...
Successfully started mysql.
~/tmz/aardvark/trunk/dragonfly > sudo rake db:test_start_stop_mysql_macosx_using_execute
(in /Users/nathan/tmz/aardvark/trunk/dragonfly)
Attempting to stop mysql...
Successfully stopped mysql.
Attempting to start mysql...
Successfully started mysql.
Attempting to stop mysql...
Successfully stopped mysql.
Attempting to start mysql...
Successfully started mysql.

2 comments:

  1. the rake task method 'reenable' can flag a task to be invoked more than once.
    http://ruby-doc.org/stdlib-1.9.3/libdoc/rake/rdoc/Rake/Task.html#method-i-reenable

    ReplyDelete