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 Open Source | Tags git, semanticattributes
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 Ruby on Rails, Open Source, Releases | Tags activerecord, database, fixtures, plugins, rubyonrails, testing | no comments
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 Ruby on Rails | Tags activerecord, caching, callbacks, controllers, models, observers, rubyonrails, sweepers | 2 comments
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
def find_with_comments
returning find(:all,
:include => :status,
:conditions => "status.name = 'live'") do |results|
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 Ruby on Rails | Tags eagerloading, preloading, rubyonrails | 2 comments
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:
- Subversion checkouts have less slowdown than removing files.
- 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).
- 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 benchmarking, mediatemple | 3 comments
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 Ruby on Rails, Open Source | Tags benchmarkforrails | 1 comment