Dynamic Types, Real tests!

Posted by nick.stielau on June 22, 2009

After developing, using, and babysitting a production Rails application for a few months now, I have come across this not-so-juicy tidbit: if you use a dynamically typed language, you better use some ‘real’ tests. This may seem obvious, but I think that this is one of the reasons that the Rails community has put so much into Test Driven Development. When you’re writing Java, you (and all the developers on your projects), have a bit of testing baked in to every expression of code; the first gauntlet of tests your code passes is the compilation.

Writing Ruby, however, you can monkey-patch and duck-punch to your hearts content. And maybe even you have a ton of unit tests for the business logic, regression tests and integration tests. You need some detailed unit tests to test any core features you are extending. Just because you can write something in a few expressive lines of code doesn’t mean there can’t be dozens of edge-cases. In a fast-paced work environment, my teams tend to rely on a small set of general tests and a solid QA pass, rather than marching in the face of the 80/20 rule and trying to test each little bit. Or at least that is how we did it. Now we’re gonna have unit tests for core extensions, no matter how trivial.

Because you might try to execute the “foo” method on the “bar” object, which is totally loco, and something won’t work, or it will error, and why would you ever try to run bar.foo? Fine. Sometimes, though, if you are extending functionality around generic or common methods (”each”, “flatten”, “to_s”, “nil?”, “blank?”) you can find some edge-cases that still work and produce passable output, until you start digging in.

Getting Things Done with code: address the real issue

Posted by nick.stielau on June 20, 2009

I haven't yet delved into the real practices surround Getting Things Done, but I have come across a principle that can be applied to a variety of different things. I call it "Remember Less." The basic idea is to put less pressure on your self by organizing and writing things down.

I found recently that this principle definitely applies to writing software. By writing clean, organized, self-documenting code and focusing on the real issue (rather than implementation details) there is less pressure on you as a developer to remember the intricacies of your codebase. This also decreases the need for you as a developer to communicate and document your code, and speeds up ramp-up time for new developers.

For example, when making a web form, it is better to check the type of request rather than to look for a specific variable that might indicate that the form was submitted.

This brings me to my next point, which is that nice frameworks decrease developer stress by providing convenient syntax and logical concept-models for interacting with the code workflow. For example, using Rails'

CODE:
  1. if request.post?
  2. #Do some POST stuff
  3. end

is way easier to understand and conceptually consistent than a similar expression in PHP (okok, i'm comparing a language to a framework, bear with me):

CODE:
  1. if($_SERVER['REQUEST_METHOD'] == "POST")
  2. {
  3. // Do some POST stuff
  4. }

So go the extra mile and write good clean, self documenting code. Also, distill the problem down to it's most basic form, without getting confuse.d by the implementation, and you'll make it easier on yourself.

Conceptual Integrity

Posted by nick.stielau on May 18, 2009

Check it. This is a great quote from Brooks' "The Mythical Man-Month". Completely un-technical, and yet it could have helped prevent several technical mishaps my teams have coded themselves into. Moral integrity vs. conceptual integrity, now there's a question.

I contend that conceptual integrity is the most important con-
sideration in system design. It is better to have a system omit
certain anomalous features and improvements, but to reflect
one set of design ideas, than to have one that contains many
good but independent and uncoordinated ideas.

Frederick P. Brooks, Jr., The Mythical Man-Month: Essays on Software Engineering,
Addison-Wesley, 1975. Second edition 1995.

Install Nokogiri gem on RHEL 5.2

Posted by nick.stielau on May 12, 2009

CODE:
  1. sudo yum install -y libxslt libxslt-devel libxml libxml-devel libxml2 libmxl2-devel
  2. sudo gem install nokogiri

Do it!

Apple Mail rejects Gmail password at home, but not at work

Posted by nick.stielau on May 11, 2009

Yeah, that's right, and it finally got me peeved enough to do something about it. Digging around through google's documentation I found this google help page:

The POP server 'pop.gmail.com' rejected the password for user 'username.' Please re-enter your password, or cancel.

Verify your password and then clear the captcha. If you're a Google Apps user, visit https://www.google.com/a/yourdomain.com/UnlockCaptcha. Be sure to replace 'yourdomain.com' with your actual domain name.

Bing!

Using Subversion SCPlugin with an untrusted SSL certificate

Posted by nick.stielau on May 06, 2009

SCPlugin adds handy subversion tools to your OS X context menu. Its great for PM's, designers, moms (really? no) or anybody else who doesn't grok dot grok dot com the efficiency of the command line.

Or, at least, its great until you install it and it doesn't work for a... well it just doesn't work, something about "PROPFIND Server certificate verification failed: issuer is not trusted". Ok, granted I didn't update the self-signed SSL in a year, but its perfectly secure, and I'm not going to pay for this.

This little trick will get you going:

CODE:
  1. nick$ svn info https://my.repo.com/path_to_repo
  2. Error validating server certificate for 'https://my.repo.com:443':
  3.  - The certificate is not issued by a trusted authority. Use the
  4.    fingerprint to validate the certificate manually!
  5. Certificate information:
  6.  - Hostname: my.repo.com
  7.  - Valid: from Thu, 07 May 2009 08:23:36 GMT until Wed, 01 Feb 2012 08:23:36 GMT
  8.  - Issuer: dev, Somebody
  9.  - Fingerprint: 7d:ac:37:52:fe:e9:3f:df:c7:1f:b4:cd:5d:d6:58:0c:29:62:92:4c
  10. (R)eject, accept (t)emporarily or accept (p)ermanently? p

That helps, but also run that for root, which I think is what SCPlugin is using at some level:

CODE:
  1. nick$ sudo svn info https://my.repo.com/path_to_repo

Installing mysql gem on RHEL 5.2 Tikanga. This actually works.

Posted by nick.stielau on April 27, 2009

Ok, lets start at the beginning. Nice, freshish RHEL 5.2 box. Try to install mysql gem...

CODE:
  1. [nick@Dev1 ~]$ sudo gem install mysql
  2. Password:
  3. Building native extensions.  This could take a while...
  4. ERROR:  Error installing mysql:
  5.     ERROR: Failed to build gem native extension.
  6.  
  7. /usr/bin/ruby extconf.rb
  8. checking for mysql_query() in -lmysqlclient... no
  9. checking for main() in -lm... no
  10. checking for mysql_query() in -lmysqlclient... no
  11. checking for main() in -lz... no
  12. checking for mysql_query() in -lmysqlclient... no
  13. checking for main() in -lsocket... no
  14. checking for mysql_query() in -lmysqlclient... no
  15. checking for main() in -lnsl... no
  16. checking for mysql_query() in -lmysqlclient... no
  17. *** extconf.rb failed ***
  18. Could not create Makefile due to some reason, probably lack of
  19. necessary libraries and/or headers.  Check the mkmf.log file for more
  20. details.  You may need configuration options.
  21.  
  22. Provided configuration options:
  23.     --with-opt-dir
  24.     --without-opt-dir
  25.     --with-opt-include
  26.     --without-opt-include=${opt-dir}/include
  27.     --with-opt-lib
  28.     --without-opt-lib=${opt-dir}/lib
  29.     --with-make-prog
  30.     --without-make-prog
  31.     --srcdir=.
  32.     --curdir
  33.     --ruby=/usr/bin/ruby
  34.     --with-mysql-config
  35.     --without-mysql-config
  36.     --with-mysql-dir
  37.     --without-mysql-dir
  38.     --with-mysql-include
  39.     --without-mysql-include=${mysql-dir}/include
  40.     --with-mysql-lib
  41.     --without-mysql-lib=${mysql-dir}/lib
  42.     --with-mysqlclientlib
  43.     --without-mysqlclientlib
  44.     --with-mlib
  45.     --without-mlib
  46.     --with-mysqlclientlib
  47.     --without-mysqlclientlib
  48.     --with-zlib
  49.     --without-zlib
  50.     --with-mysqlclientlib
  51.     --without-mysqlclientlib
  52.     --with-socketlib
  53.     --without-socketlib
  54.     --with-mysqlclientlib
  55.     --without-mysqlclientlib
  56.     --with-nsllib
  57.     --without-nsllib
  58.     --with-mysqlclientlib
  59.     --without-mysqlclientlib
  60.  
  61.  
  62. Gem files will remain installed in /usr/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.
  63. Results logged to /usr/lib/ruby/gems/1.8/gems/mysql-2.7/gem_make.out

Yikes! Look familiar? Bummer. First step, brute force install everything I could think might be missing

CODE:
  1. sudo yum install gcc mysql-server mysql-devel ruby ruby-devel

Turns out, the gcc was having some issues:

CODE:
  1. [nick@Dev1 mysql-2.7]$ sudo yum install gcc
  2. Loading "rhnplugin" plugin
  3. Loading "security" plugin
  4. intensive-rhel-i386-serve 100% |=========================| 1.2 kB    00:00     
  5. rackspace-int-rhel-i386-s 100% |=========================| 1.2 kB    00:00     
  6. Excluding Packages in global exclude list
  7. Finished
  8. Setting up Install Process
  9. Parsing package install arguments
  10. Resolving Dependencies
  11. --> Running transaction check
  12. ---> Package gcc.i386 0:4.1.2-42.el5 set to be updated
  13. --> Processing Dependency: libgomp = 4.1.2-42.el5 for package: gcc
  14. --> Processing Dependency: cpp = 4.1.2-42.el5 for package: gcc
  15. --> Processing Dependency: libgomp.so.1 for package: gcc
  16. --> Processing Dependency: glibc-devel>= 2.2.90-12 for package: gcc
  17. --> Running transaction check
  18. ---> Package cpp.i386 0:4.1.2-42.el5 set to be updated
  19. ---> Package glibc-devel.i386 0:2.5-24.el5_2.2 set to be updated
  20. --> Processing Dependency: glibc-headers = 2.5-24.el5_2.2 for package: glibc-devel
  21. --> Processing Dependency: glibc-headers for package: glibc-devel
  22. ---> Package libgomp.i386 0:4.1.2-42.el5 set to be updated
  23. --> Running transaction check
  24. ---> Package glibc-headers.i386 0:2.5-24.el5_2.2 set to be updated
  25. --> Processing Dependency: kernel-headers>= 2.2.1 for package: glibc-headers
  26. --> Processing Dependency: kernel-headers for package: glibc-headers
  27. --> Finished Dependency Resolution
  28. Error: Missing Dependency: kernel-headers is needed by package glibc-headers
  29. Error: Missing Dependency: kernel-headers>= 2.2.1 is needed by package glibc-headers

Googling around a little, I found this helpful page which indicated that the default RHEL yum configuration excludes updates from the kernel. That is probably good practice in general, but right now it is preventing us from doing what "needs to be done."

Looking at my /etc/yum.conf file, indeed the kernal was excepted from updates:

CODE:
  1. [nick@Dev1 ~]$ cat /etc/yum.conf
  2. [main]
  3. cachedir=/var/cache/yum
  4. keepcache=0
  5. debuglevel=2
  6. logfile=/var/log/yum.log
  7. distroverpkg=redhat-release
  8. tolerant=1
  9. exactarch=1
  10. obsoletes=1
  11. gpgcheck=1
  12. plugins=1
  13.  
  14. # Note: yum-RHN-plugin doesn't honor this.
  15. metadata_expire=1h
  16. # Default.
  17. # installonly_limit = 3
  18. # PUT YOUR REPOS HERE OR IN separate files named file.repo
  19. # in /etc/yum.repos.d
  20. exclude=kernel*

So I commented out that line, yum installed gcc, and then off to the races. My googling also noted that passing the --with-mysql-config saves you some trouble, essentially letting it configure itself.

CODE:
  1. [nick@Dev1 ~]$ sudo gem install mysql -- --with-mysql-config=/usr/bin/mysql_config
  2. Building native extensions.  This could take a while...
  3. Successfully installed mysql-2.7
  4. 1 gem installed

Just in case, you might want to uncomment the kernal extension from your /etc/yum.conf file!

MapReduce: Powerful Elegance

Posted by nick.stielau on March 13, 2009

I've just started playing around with Hadoop, Apache's implementation of Google's MapReduce paradigm. I can feel the power that MapReduce brings to the table, but I have had trouble trying to describe it to other people. Here's a nice quote from google's whitepaper:

Most such calculations are conceptually straightforward. However, ... the issues of how to parallelize the computation, distribute the data, and handle failures conspire to obscure the original simple computation with large amounts of complex code...

So MapReduce doesn't help you solve really difficult problems, it effectively manages simple problems with a teeny dataset, or a ginormous one. Its as if it was equally effective for one pointy-haired boss to manage a team of 5 as it was a team of 5000. Combine that with Cloud Computing's ability to reduce the cost of acquiring new processing power, and you have quite a bit of power at your fingertips.

Relay log issues when starting MySQL replication slave

Posted by nick.stielau on March 06, 2009

I just ran into a little issue setting up a MySQL slave. I have done this a least a have dozen times before, and it is always pretty painless, but this time, I kept getting hung up on error that looked like this:

071118 16:44:10 [ERROR] Failed to open the relay log './-relay-bin.003525'
(relay_log_pos 22940879)
071118 16:44:10 [ERROR] Could not find target log during relay log initialization
071118 16:44:10 [ERROR] Failed to initialize the master info structure

Googling a little turned up this mysql forum,

http://forums.mysql.com/read.php?26,112490,223025#msg-223025

Which then led me to this mysql doc page
http://dev.mysql.com/doc/refman/5.0/en/replication-howto-additionalslaves.html

This was confusing, and didn't really seem like it got to the root of the problem. Why would one slave care about another, if they were both reading from the master? I think that article would more accurately be called "Making a slave of a slave" or some such, because "Introducing Additional Slaves to an Existing Replication Environment" does not convey that they are talking about a slave of a slave (which I think they are).

Anyway, long and short of it, I removed the 'master.info' 'mysqld-relay-bin.*' 'relay-log.info' 'relay-log-index.*' from the MySQL data directory, restarted mysql ('/etc/init.d/mysql restart'), and off to the races.

Sometimes, depending on the order you do edit the configs or restart mysql, you can get bad data in those files, and it can lead to crpytic errors. Remove those, and you can always start fresh.

Errno::EACCES permissions error using paperclip with JRuby on Joyent 1

Posted by nick.stielau on March 03, 2009

We ran into a permission error using the 'paperclip' plugin. It had something to do with moving a tmp file into the rails public directory. The odd thing was that it could create the appropriate directories in the rails public directory, but it couldn't copy the file.

Digging around the web, I found this group about this exact problem.

http://groups.google.com/group/paperclip-plugin/browse_thread/thread/4a0a97a08929d3a5

I found that I had all of the patches, but it still didn't work. Most of the people on the group were complaining it that it didn't work on Windows, and maybe had something to do with file handles, etc. We were trying to get this working in JRuby on a Joyent Solaris Accelerator, each of which can make things a little different than a MRI Rails app on Linux.

Following a tip from a user on the group, simply changing the way the move happens fixes this problem. I would like to fix the root cause, but I got it working for me, and (*open source sigh*) don't want to isolate the exact problem.

After that, i made some change in storage.rb#flush_writes.
It's a short workaround for
FileUtils.mv
Simply replace it with

FileUtils.cp(file.path, path(style))
FileUtils.rm(file.path)

Currently, it works for me. But please consider, it isn't checked
against a well written
test or any other platform.