Moving to Git - Semantic Attributes

Posted by Lance Ivy Tue, 06 May 2008 00:02:00 GMT

I’m finally moving over to Git. First up: my Semantic Attributes plugin. One of these days I’ll do a proper release/announce about what I’ve been up to, but for now you can simply check out the project history.

cainlevy/semantic-attributes

Posted in  | Tags ,

Plugin Testing with a Database

Posted by Lance Ivy Mon, 17 Mar 2008 22:08:00 GMT

When I develop a plugin that extends ActiveRecord, I’m really not content to stub out all the database access. I feel that tests should be reasonably complete, and to me that means not making a lot of assumptions about what’s going on underneath the hood of the supporting framework (in this case, ActiveRecord). But at the same time, I hate depending on an application in order to develop and test a plugin.

So I figured out how to package database access into a plugin (thanks to rick olson’s acts_as_paranoid testing). The highlights:

  • uses an in-memory SQLite3 database
  • loads from a database schema
  • a collection of ActiveRecord models designed to compactly represent all the different associations
  • provides basic fixtures for all models
  • maintains a separate test log
  • does not assume that you’re testing your plugin

I’ve adapted the method for a couple plugins so far, and today decided to take the next step and create my own plugin template with all these goodies. I didn’t/won’t make it a generator, but I did package it up to share (MIT-style). Simply unzip this template somewhere, delete whatever internals are simply cruft for your purposes, and search-and-replace the string {template} with your own plugin name to get started.

Attachment

Posted in , ,  | Tags , , , , ,  | no comments

Rails Caching: Sweepers, Controllers, and Models

Posted by Lance Ivy Tue, 04 Mar 2008 22:42:00 GMT

When you start implementing caching mechanisms into your Rails application, you’re going to have to figure out Rails’ Sweepers. These sweepers confused me for a bit and I’d like to offer clarification for anyone else that might have similarly misunderstood the documentation.

From the documentation:
Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change. They do this by being half-observers, half-filters and implementing callbacks for both roles.

The second sentence is what confused me until just today when, while investigating an odd exception email, I dug into the source code. But once I understood what sweepers actually did, the first sentence made even less sense than the second. Further, the example in the documentation is even more misleading. Oh well.

Here’s the scoop: sweepers observe both your models and your controllers. They’re not half-this and half-that, they’re both. You can define model callbacks (after_save, after_update, etc.), and you can also define before/after callbacks for any controller action (e.g. after_list_create).

Here’s the rest of the scoop: if you only define model callbacks, you don’t need to call the cache_sweeper method on your individual controllers. In fact, you may not want to. You can instead add your sweeper directly to config.active_record.observers in your config/environment.rb. There are only two reasons you’d want to call cache_sweeper to register your sweeper: first, if you want to add callbacks to a controller’s actions; and second, if you need access to the current controller in your sweeper’s model-callback methods (justifiable when you need the controller to calculate fragment cache paths, for example). But in the second case, you want to call cache_sweeper from your ApplicationController. Why? Because otherwise you’ll end up observing models that might change from controllers you didn’t explicitely add the cache_sweeper line to, and will eventually rely on a controller variable that doesn’t exist.

Summary:

  • Sweepers may implement all ActiveRecord::Observer callbacks. To make these work, you could simply add your sweeper to config.active_record.observers.
  • Sweepers may create before/after callbacks for any controller action. To make these work, you need to use the cache_sweeper method on the controllers in question.
  • If your sweepers implement model callbacks but need to refer to the controller for any reason, you want to add the cache_sweeper method to the ApplicationController.

Posted in  | Tags , , , , , , ,  | 2 comments

Association Preloading in Rails Edge

Posted by Lance Ivy Thu, 28 Feb 2008 01:30:00 GMT

I try and watch the Rails changesets for anything interesting. About a month ago I saw [8672], an implementation of ticket 9640. This sweet patch (and subsequent bug-fixes, etc.) introduced a new strategy for eager loading associated records from the database. It’s a strategy I started trying to hack for a past client, except done much better.

The basic idea is that instead of creating a monstrous SQL join to eager load all those associations you plan to utilize in your view, ActiveRecord will run the main query and then run follow-up queries to load all the :includes you specified.

There are pros and cons to the strategy, of course. Not the least of which is the fact that sometimes you need those :includes in order to execute your SQL conditions and ordering clauses. The patch fixed this problem by scanning the conditions and order SQL for any references to the associated tables, then falling back to the join strategy if necessary.

Personally, I think you should be able to specify a :preload option apart from the :includes option. But until that gets patched into edge, there’s a dead-simple way to do manual preloading:

manual preloading
class Post < ActiveRecord::Base
  # note that preload_associations() is a protected
  # method, so either create a custom finder or use
  # the send() bypass
  def find_with_comments
    returning find(:all,
      :include => :status,
      :conditions => "status.name = 'live'") do |results|
      # will run two extra queries to retrieve and load the
      # comments for the posts in the result set, then find
      # the authors of those comments.
      preload_associations(results, {:comments => :authors})
    end
  end
end

This is a really contrived example, but it shows how you can execute a join (with the :status association) then preload a couple extra associations by hand. Easy-peasy.

Posted in  | Tags , ,  | 2 comments

MediaTemple Grid Service Benchmarks

Posted by Lance Ivy Tue, 18 Dec 2007 23:27:00 GMT

I’ve been working on a deployment on the MediaTemple grid service, which on the surface strikes me as a very smart way to manage Rails apps. Putting shared hosting on a grid layer mitigates the performance spikes associated with of sharing a box and the overall limitation of the box’s resources, and putting the Mongrel threads in their own environment with dedicated resources shields your Rails apps from the weirdness of shared environments. Cool.

Been having some trouble with disk speeds, though. Capistrano deployment is just dead slow. So I decided to do some benchmarks on basic deployment-related commands to get an idea of the magnitude of the problem.

Benchmarks

`time -p svn co -q http://subversion.host/path/to/repo test_dir`
  Desktop Grid
real 34.45 155.97
user 2.41 1.57
sys 1.18 2.89
`time -p rm -rf test_dir`
  Desktop Grid
real 0.22 15.58
user 0.02 0.02
sys 0.20 0.58

Conclusions

I’m not going to assume that just because file writes and deletes are slow, all disk reads are slow. But there’s one thing I do feel justified in concluding: it’s a problem with the filesystem or the disk performance. This isn’t deductive reasoning, but this is how I figure:

  1. Subversion checkouts have less slowdown than removing files.
  2. Subversion checkouts are a combination of network travel (my repository is at Assembla) and CPU time (to set up working copy) and basic file operations (adding files).
  3. Removing files is just basic file operations.

Therefore, basic file operations are the bottleneck, not network latency or CPU cycles. Of course, since this is a managed system I can only guess.

Keeping Perspective

I am not a Linux benchmarking expert. I have no idea how my desktop specs compare vs the grid servers. Any grid service is going to have some slowdown from the abstraction, just like any VPS.

But the biggest thing to remember? These benchmarks probably don’t matter for Mongrel performance or database access. Remember that in MediaTemple’s grid, Mongrels have their own container and the database may very well be in different environments as well. With a managed system like this, it’s just hard to tell.

Tags ,  | 3 comments

BenchmarkForRails Gets Log Analysis

Posted by Lance Ivy Sat, 15 Dec 2007 00:54:00 GMT

My last round of efforts on the BenchmarkForRails plugin has been on reporting. It’d be a shame to have all those benchmarks and not be able to really learn something from them.

The first step was to introduce a one-line log output for production mode. I decided to leave the development mode output in the multi-line format for readability, but one-line output makes for much easier parsing.

You’ll have to forgive me for not having a full logfile to really demonstrate how the reports work, but this should at least provide a taste:

> rake log:analyze order_by=averages
resource path                            total time  frequency  average  
---------------------------------------  ----------  ---------  -------  
GET /sites/slimtimer/suggestions/search  0.8838      3          0.2946   
GET /sites/slimtimer/suggestions         0.6777      3          0.2259   
GET /                                    0.7627      27         0.0282

Posted in ,  | Tags  | 1 comment

Older posts: 1 2 3 4 ... 6