MySQL ruby gem install problem on Mac OS X Leopard

For those of you using MySQL database server on Mac OS X Leopard while developing for Ruby or Ruby on Rails, you might have run into an issue with the mysql rubygem installing OK, but not actually loading properly in irb, nor completing unit tests successfully.

If you’ve seen errors such as:

$ irb
>> require 'mysql'
LoadError: dlopen(/Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle, 9): Library not loaded: /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib
Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
Reason: image not found - /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
from /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:32:in `require'
from (irb):1

Or perhaps you’ve tried to run test.rb from the mysql ruby library install and ran into an error like this:

$ ruby test.rb
./mysql.bundle: dlopen(./mysql.bundle, 9): Library not loaded: /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib (LoadError)
Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/mysql.bundle
Reason: image not found - ./mysql.bundle from test.rb:5

The fix to the mysql ruby library gem installation was provided by jhclouse on the RailsForum site. I’ve edited the fix to work with Mac OS X Leopard’s pre-installed version of Ruby:

sudo install_name_tool -change /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib /usr/local/mysql/lib/libmysqlclient.15.dylib /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle

The only difference between jhclouse’s fix and the one here is the location of the installed mysql rubygem. On OS X Leopard, ruby gems are installed in the /Library directory rather than /usr/local/lib.

Run the fix again in the main mysql ruby gem directory as there is another mysql.bundle file that needs fixing. This bundle file is used by the unit test file test.rb.

sudo install_name_tool -change /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib /usr/local/mysql/lib/libmysqlclient.15.dylib /Library/Ruby/Gems/1.8/gems/mysql-2.7/mysql.bundle

To run the unit tests for the mysql ruby gem you need to supply some command line arguments to test.rb:

ruby test.rb -- localhost [username] [password]

Replace [username] [password] with valid credentials for your MySQL server and you should have a fairly successful run of the unit tests for the mysql ruby gem.

Now that you’ve got the mysql C bindings for ruby installed, lets double check that it’s actually being used.

If you have a rails project, start with webrick (script/server) and load up your test site in a browser. Do something within your site that you know touches the database.

Now find the process_id for ruby by running ‘top’ in the command line. Top will return a list of all process names and their process ids. Find ruby amongst the list. For me it happened to be 3343.

Then we use that process_id in a magical program called ‘lsof’:

$ top
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
3372 top 5.3% 0:01.39 1 18 29 824K 188K 1416K 18M
3343 ruby 0.3% 0:06.77 2 19 247 60M 188K 64M 85M

$ lsof -p 3343 | grep mysql

ruby 3343 ben txt REG 14,2 86288 813474 /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
ruby 3343 ben txt REG 14,2 1967788 624481 /usr/local/mysql-5.0.45-osx10.4-i686/lib/libmysqlclient.15.dylib

lsof spits out the “list of open files” that are in use by a certain process, in this case the running ruby process that is serving our Rails site. In the above listing lsof shows that the freshly installed mysql ruby gem (mysql-2.7) that provides native C bindings for ruby to use when accessing MySQL, is currently being used. Sweet, we’re all good.

So is all this trouble really worth it? Why not just use the built in ruby libraries to interface with MySQL? If your Rails site touches a database, and most likely it does, you’ll see a 2-5x decrease in rendering time due to database calls. We’re only talking milliseconds, but it’s still a 2-5x boost in efficiency so it’s definitely worth it if you’re optimizing your Rails site. Whether it should be at the top of your tweaking to-do list depends on how optimized everything else is.

My Rails site in the testing environment was constantly generating on average about 30% of its page rendering time due to database activity. The following is hardly a definitive test, but is printed here simply for interests sake.

For an initial site load with the MySQL query cache reset, my home page rendering time looked like this before the C MySQL ruby binding installation:

Completed in 1.47017 (0 reqs/sec) | Rendering: 0.10060 (6%) | DB: 1.18992 (80%) | 200 OK [http://localhost.com/]

After, resetting the query cache in MySQL, clearing the browser cache and restarting webrick, the initial site home page load after installing the C bindings for MySQL via the ruby gem:

Completed in 0.70598 (1 reqs/sec) | Rendering: 0.08854 (12%) | DB: 0.56506 (80%) | 200 OK [http://localhost.com/]

The time to load was cut in half and the only thing that changed was the way ActiveRecord accesses the MySQL database.

Please note that if you’ve been using Mac Ports, this fix may need tweaking on your part. The problem and the fix are pretty straight forward once you know about the install_name_tool program. The mysql ruby gem is simply looking in the wrong place for a MySQL library that it needs to interface with. If you look closely at the first two directory paths you’ll notice that the first has an extra “/mysql” in between “/lib” and “/libmysqlclient.15.dylib”. Remove that extra “/mysql” folder from the directory path and you’re golden.


Posted

in

by

Comments

11 responses to “MySQL ruby gem install problem on Mac OS X Leopard”

  1. nuc Avatar
    nuc

    You’re absolutely amazing!!!!!!!!!

    Thank you 🙂

  2. Greg H Avatar
    Greg H

    Hi – I don’t suppose anyone has a suggestion for this error I’m getting…still can’t seem to get the mysql gem installed 🙁

    Macintosh-2:myequity greg$ sudo env ARCHFLAGS=”-arch i386″ gem install mysql — –with-mysql-dir=/usr/local/mysql –with-mysql-lib=/usr/local/mysql/lib –with-mysql-include=/usr/local/mysql/include -V

    WARNING: RubyGems 1.2+ index not found for:

    RubyGems will revert to legacy indexes degrading performance.
    Updating metadata for 69 gems from http://gems.rubyforge.org/
    ……………………………………………………………
    complete
    Building native extensions. This could take a while…
    ERROR: Error installing mysql:
    ERROR: Failed to build gem native extension.

    /opt/local/bin/ruby extconf.rb install mysql — –with-mysql-dir=/usr/local/mysql –with-mysql-lib=/usr/local/mysql/lib –with-mysql-include=/usr/local/mysql/include -V
    checking for mysql_query() in -lmysqlclient… no
    checking for main() in -lm… yes
    checking for mysql_query() in -lmysqlclient… no
    checking for main() in -lz… yes
    checking for mysql_query() in -lmysqlclient… yes
    checking for mysql_ssl_set()… yes
    checking for mysql.h… yes
    creating Makefile

    make
    /usr/bin/gcc-4.0 -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.3.0 -I. -DHAVE_MYSQL_SSL_SET -DHAVE_MYSQL_H -I/usr/local/mysql/include -I/usr/local/mysql/include -I/opt/local/include -fno-common -O2 -fno-common -pipe -fno-common -c mysql.c
    mysql.c: In function ‘Init_mysql’:
    mysql.c:2015: error: ‘ulong’ undeclared (first use in this function)
    mysql.c:2015: error: (Each undeclared identifier is reported only once
    mysql.c:2015: error: for each function it appears in.)
    mysql.c:2015: error: syntax error before numeric constant
    mysql.c:2018: error: syntax error before numeric constant
    make: *** [mysql.o] Error 1

    Gem files will remain installed in /opt/local/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.
    Results logged to /opt/local/lib/ruby/gems/1.8/gems/mysql-2.7/gem_make.out
    Macintosh-2:myequity greg$

  3. Shaun Avatar
    Shaun

    Thanks a lot for this, it helped me solve the the last in a long chain of problems I’ve been encountering.

  4. […] reading this post, and futzing around, I got it to work. Here was the fix: sudo install_name_tool -change […]

  5. Sarah Avatar

    This is awesome!! Thank you so much for posting this!! 🙂

  6. Harry Avatar

    Great post!! Thanks for sharing an informative and interesting article. Keep it up!

  7. Squeeze Page Creator Avatar

    Wow! You’ve just fixed my problem! I having the same issue with my MySQL ruby gem and I applied your fix. This works for me perfectly. Thanks!

  8. Craig Ulliott Avatar

    If you’re using RVM you will need to specify the location of the ruby headers

    env ARCHFLAGS=”-arch x86_64″ gem install mysql — –with-mysql-config=/usr/local/mysql/bin/mysql_config –with-ruby-include=/Users/craig/.rvm/src/ruby-1.9.2-p180/

Leave a Reply

Your email address will not be published. Required fields are marked *