Posted by Nick Sieger
Sat, 24 Jun 2006 15:15:00 GMT
Is anyone doing “real” projects in Rails? Obie’s talk is the answer
to this frequently received question.
Twix, the first Rails project at Thoughtworks was an internal tool
that functioned as an introduction to the technology and began the
evangelization process for Rails within Thoughtworks.
Apparently DHH and Roy (the owner of Thoughtworks) hit it off when DHH
said to Roy in a meeting, “I want to change the world.”
The Deere project started as a supply-chain application built on Java
that took 6 weeks to get up and running. Rails was sneaked in as a
tool to help business owners generate sample data for the main
application. The interface was simple, and made it fun for the users.
It generated quite a stir, with one of the customers exclaiming that
it was better than the app they already had!
This led to Deere COA (Certificate of Analysis). There was a
sticky-note, user-centered design, task analysis session that helped
to drive what form the application would take. The resulting
implementation took about two calendar weeks, eight person weeks.
Obie’s comment at this point was that the amount of time was on the
order how long it usually took the client just to decide that they
wanted to do something.
Later, as maintenance of the tool was being handed off to some
internal developers, Obie gave a brief presentation on Rails to some
of the client stakeholders. When he returned to finish training on
the technology three months later, he found that a product manager
(somewhat non-techie) had built a time tracking application during his
two-week family leave that subsequently received a lot of attention at
the office. A project manager was asking him to help fix a bug with
the application that had already been deployed and in use for three
months, and the company wanted to roll it out for use to several
thousand people at the company.
The story about the Kiosk project highlighted that agile and XP
methods fit so well with Rails. Obie described one case where they
brought their usability expert in for a review and Rails allowed them
to make changes to the UIs as fast as the usability person could point
them out.
Note: Q & A is paraphrased.
- How can you transform a client’s thinking about technology and
acceptance of Ruby and Rails so that when you leave, they own it?
Find a common thread of command-line, text-based development, or
experience with similar languages. Most skilled programmers can
pick up Rails in days or weeks. Your miles may vary but it goes
with the amount of talent you’re working with.
- Comment on RadRails. Code completion is overrated, it keeps you
from solving the problems in your head because the editor is feeding
alternatives to you.
- Comment on deployment. The reason you can’t do Ruby on Rails is
that it’s not a supported technology in our data center. Politics,
status quo, not important, blah, blah. We ended up doing the app
anyway, and the data center people said, “sure you can do it, we’ll
give you a VPS”.
Posted in ruby, rails | Tags railsconf | no comments | no trackbacks
Posted by Nick Sieger
Fri, 23 Jun 2006 21:43:00 GMT
Scaffolding is not a swiss army knife, it’s more like a rusty spoon.
It’ll give you tetanus! Scaffolding leads to brain atrophy and
laziness. Leads to bad code, bad UI, etc. Instead, start from
scratch. script/generate
is not inherently bad, it can write
necessary, tedious code for you.
script/generate controller kitten create view put_to_sleep
This gives you:
class KittenController < ApplicationController
def create
end
def view
end
def put_to_sleep
end
end
Amy proceeds to break down a from-scratch, minimalist set of views, to
underscore the point that it’s not too hard to not use scaffolding.
I’d agree, it was so straightforward that unfortunately I’m going to
omit the details here. Rolling your own without scaffolding is
highly recommended.
Some of the items that you generally will experience in real world
apps but do not come in scaffolding are widgets for maintaining
relationships. Select lists based on a belongs_to
, checkbox groups
for has_many
are two examples.
Amy breaks these down as well and it turns out they’re pretty easy to
do. One snippet of code for updating checkboxes was succinct and
surprisingly readable, and made judicious use of the ruby array’s -
behavior to add newly checked and remove newly unchecked items
quickly.
Amy promised to post snippets and slides later on her blog, so go over
to slash7 for more.
Posted in ruby, rails | Tags railsconf | no comments | no trackbacks
Posted by Nick Sieger
Fri, 23 Jun 2006 20:40:00 GMT
Stefan starts by citing a factor of 4-5 improvement in performance in
Rails over the last year.
Performance, broken down
- Latency -- how fast
- Throughput -- how many
- Utilization -- how idle is the cpu
- Cost efficiency -- performance per unit cost
For completeness calculate the min, max, mean and standard deviation
of these metrics and use the deviation as your guide for how reliable
the data is.
Tools
- Log files (level >
Logger::DEBUG
)
- Rails Analyzer Tools (Eric Hodel)
- Benchmarker (
script/benchmarker
)
- DB vendor tools
- Apache bench (
ab
or ab2
)
- httperf
- railsbench (Stefan Kaes)
Railsbench
Railsbench measuress raw performance of rails request processing.
It’s configured using config/benchmarks.yml
and
config/benchmarks.rb
. These files let you control which requests
get benchmarked, whether to create a new session when benchmarking
them, etc.
Profiling Tools
At this point Stefan gave an overview of RPV, which appears to be a
nifty tool that lets you get typical hotspot tree views of where time
is spent in code. It currently only runs on Windows.
Top Rails Performance Problems
- slow helper methods
- complicated routes
- associations -- navigating and eager loading vs. proxy loading
- retrieving too much data from the DB
- slow session storage (e.g., ActiveRecord store)
Stefan says that in his experience, DB performance is generally not a
big factor or bottleneck. Instantiating ActiveRecord objects is
expensive, though.
Session containers
- In memory -- if you server crashes...oops. Also doesn’t scale.
- File system -- easy to set up, scales with NFS, but slower than...
- ActiveRecordStore -- easy to set up since it comes with Rails, but
much slower than...
- SQLSessionStore -- which uses the same table structure as
ActiveRecordStore, but was written by Stefan to overcome performance
issues with ActiveRecordStore. Setup is more involved.
- memcached -- slightly faster than SQLSessionStore, scales best, but
setup is also more involved.
- DrbStore -- distributed ruby store
Caching
- Full pages -- fastest, complete pages are served on the
filesystem. Web server bypasses appserver for rendering. If you
have private pages, you can’t use it.
- Actions -- pages are cached after an action is rendered. The
user ID can be used as part of the storage key.
- Fragments -- fragments can be cached in memory, on the file system,
in a DrbStore, or in memcached. Memcached scales the best but
doesn’t support expiring fragments by regular expression.
ActionController
- Stefan recommends avoiding components, and replacing them with
helpers or partials. He has not found a use for them.
ActionView
- Don’t create unnecessary instance variables in the controller;
creating them in the view with
instance_variable_set
and accessing
with instance_variable_get
is slow.
Helpers
- pluralize -- don’t use the inflector if you don’t need to, it’s expensive.
- linkto and urlfor are among the slowest helpers, since they need
to use routes. Instead, if you have page with lots of links, you
might consider hard-coding the links. This reduces the amount of GC
by up to 50% and the GC time down by a few percentage points (11.3%
to 8.7% of total processing time).
ActiveRecord
- use the
:include
option to prefetch associations, it avoids extra
onesy-twosy SQL statements.
- use piggy-backing plugin for
has_one
or belongs_to
relationships
-- allows you to retrieve extra attributes from additional tables in
the same fetch query.
- Field values are retrieved from the DB mostly as strings, so type
conversion happens on each access, which can be slow.
Language-level and miscellaneous issues
- Method calls are the slowest -- don’t needlessly create method
abstractions
- Short-circuit intermediate results to improve performance
- Cache results in instance variables or class variables
- Don’t call
ObjectSpace.each_object
on each request
Ruby Memory Management
- designed for batch scripts, not long-running servers.
- no generational garbage collection.
- this is suboptimal for Rails because ASTs are stored on the heap
(biggest portion of non-garbage for Rails apps), and get
processed/traversed more often than they need to be
- Railsbench includes a patch to allow one to recompile Ruby
and tweak the garbage collector.
Rails Template Optimizer
- Stefan has started a project to “compile” templates.
- The idea is to cache results of some ERb scriptlets and essentially
“compile” or replace the template with one that has more expressions
expanded or inlined.
- Code forthcoming; I assume you can stay tuned to Rails
Express for news.
Questions
- There was a question on JRuby -- Stefan replied that it would
certainly solve GC issues, but he doesn’t know if it’s in a state to
be able to benchmark Rails requests.
- What are your recommendations for a web server. I don’t have
any.
- Is horizontal or vertical scaling better? I don’t know, I’ve
been focused on making single requests go fast, so I don’t have
enough experience.
Posted in ruby, rails | Tags railsconf | 3 comments | no trackbacks
Posted by Nick Sieger
Fri, 23 Jun 2006 16:45:00 GMT
Introduction to Capistrano
Mike Clark has been Java-free for 15 months and 16 days. He’s here to
talk about deployment with Capistrano.
First off, props to Jamis! There are several hundred people in the
room and it’s a tribute to him.
Capistrano is about making it easy to deploy web applications. “Cap”,
the short name has quickly become jargon in the Rails community.
“Stop wasting time, just cap it!”
Capistrano is built to scale. From a single machine all the way up to
clusters, it is intended to make application deployment as simple as
the push of a button. That’s priceless for reducing friction as your
project nears its first production live date, as well as subsequent
maintenance releases.
Configuring Capistrano - create a recipe
Set the application name
set :application, "depot"
Set the repository
set :repository, "http://svn.yourhost.com/#{application}/trunk"
Roles
role :app, "app01.example.com", "app02.example.com"
role :web, "web01.example.com", "web02.example.com"
role :db, "db.example.com", :primary => true
Deployment Root
set :deploy_to, "/Library/Rails/#{application}"
Setup
Run cap setup
once to setup the deployment directory structure on
all the roles you’ve configured:
depot
`- releases
`- shared
`- log
`- system
First time deploy
Run cap update_code symlink
. This checks out the code, adds a
“current” symlink to the code on the remote machine that points to the
timestamped releases directory where the code was checked out. You
can then manually start your web server(s) if you wish.
New Release
Run cap deploy
. The newly committed code to your repository gets
pulled, a new release created, and it also restarts fcgi processes.
Rollback release
Run cap rollback
. The “current” symlink gets updated to the previous release,
and the fcgi processes get restarted.
Scheduled downtime
Run cap disable_web
. A maintenance screen is put up during the
maintenance period. This is great when you’re in firefighting mode
and you don’t want to think, just get the page up there. To enable
again, use cap enable_web
.
Customization
Here are examples of several useful, real-world tasks. The fourth
task shows aggregation of tasks together.
desc "locate ruby"
task :which_ruby, :roles => [:app] do
puts "You're running:"
run "which ruby"
end
task :current_revision do
run "echo Current rev is #{revision}"
run "echo Current rev is at #{releast_path}"
end
task :uptime, :roles => [:app, :web, :db] do
run "uptime"
end
task :status do
which_ruby
current_revision
uptime
end
Channels and streams
Mike showed a log file tailing task here. I failed to capture the full task code, so you’re on your own here.
Hooks
task :before_deploy, :roles => [:app] do
cleanup
end
task :after_update_code, :roles => [:app] do
production_config = "path/to/database.yml"
run "cp #{production_config} #{current_path}/config/database.yml"
end
Multiple configs
if where == "production"
set :user, "ops_gal"
else
set :user, "dev_guy"
end
Task libraries
You can creat a file full of extra, reusable tasks and simply
require
them in. The files can be published and installed as gems
or placed anywhere on your load path.
Capistrano isn’t just for Rails
It’s great at shuttling files around and executing remote commands on
the server. Mike describes some automation James Duncan Davidson did
to replicate new servers into a cluster with the push of a button.
Assumptions
Capistrano, out of the box comes with some assumptions:
- Remote servers talk POSIX
- Same deploy structure and password on each machine
- Web application uses FastCGI with Apache or Lighty
It looks like you can bend some of these assumptions if you are
willing to write your own custom deployment tasks.
Take Home
Capistrano provides easy, consistent, worry-free deployment of new
releases. The friction to deploying your app just got simpler, so you
can go enjoy your life!
Notes and Questions
- Can you use SSH public keys to authenticate? Yes, this is the most
typical scenario.
- You can use
sudo
to execute remote commands -- this is built-in.
- Can you/would you want to check out subsets of files on each server,
rather than pulling all the code everywhere? So far most of our
stuff has been on the same server, so this hasn’t been an issue. It
seems like you would need to override deployment tasks for some
roles.
- Sometimes I want to deploy a lot, but what if the app is big, lots
of media files? It can take a longer time to pull all the
code. One solution I’ve seen is to check out once and rsync
everywhere else. I haven’t had to do that myself.
- Who can I talk to for promoting my own extensions into Capistrano.
You probably don’t. Instead, publish your extensions, blog
them, and see if it takes.
deploy_with_migrations
-- use with caution as it’s not as easy to
rollback, and not all migrations can be rolled back cleanly.
Posted in ruby, rails | Tags railsconf | no comments | no trackbacks
Posted by Nick Sieger
Fri, 23 Jun 2006 15:12:00 GMT
Dave Thomas, Professional Cassandra
Intro
Dave starts by dissecting a google search and pointing out how the sponsored links sidebar contains a wealth of paid ads for various topics:
- Hosting
- Consulting
- Commercial IDEs and tools
- Alternatives to RoR!
Moving on to rubyforge, Dave examines some download stats, and points out that there have been 536835 downloads of Rails through gem install rails
Dave examines some Google trends mapping of ruby on rails vs the following, showing RoR catching up on some and overtaking others:
- websphere
- jboss
- tapestry
- spring framework
- zend
3 Problems for Rails
Dave proceeds to give his own, tailored for Rails, version of David Hilbert’s famous “23 unsolved problems”...except due to time constraints, can only discuss three.
PDI
An acronym, PDI, has emerged on the Rails core team as a way to encourage people to contribute. Please Do Investigate
The challenge is to get new features to be proved out in the community before taking it into the core, the Rails core team is only 12 and has limited bandwidth, and that is the idea behind PDI
The three problem areas are:
- Data integration
- CRUD
- Deployment
Data integrations
Dave mentions that he and David “disagree mildly” on the point of using constraints in the database.
- automatic validation based on schema
- work with foreign keys - make it easy to define in a migration
- add a “belongs_to” relationship if a foreign key constraint is detected
- primary key support, e.g., non-integer keys, particularly in migrations, and composite primary keys
The argument here is that real-world applications, and legacy databases today require these features, and Rails needs to be able to have answers to those questions if it wants to be viable in that area.
Dave is asking for distributed transactions now. Should we be pushing Rails into such an enterprisey direction? If you really need XA transactions, shouldn’t you be banished to a java world?
Non-database models would be a fabulous addition to rails. ActiveRecord::Base currently assumes that it’s talking to a sinle relational datastore. If we could integrate multiple disparate data soures, it would make integrations, REST data sources, and mashups much easier. Microsoft is doing this with LINQ today, and if Rails wants to keep up, it will need this.
Real-world CRUD
Dave is arguing that we should beef up scaffolding and make it useful. Scaffolding was the star of the initial 10 minute Rails video that started everything, but yet it’s the ugliest web 1.0 interface ever! No AJAX, no automatic, DRY, in-browser validation. But should we, can we really genericize these interfaces? Maybe a scaffolding widget/component system? It can and probably will happen, but at the momet I’m inclined to think that this will complicate things. Do we want Rails view to turn into Wicket widgets or ASP.NET components? It’s a completely different way of writing web programs. We’ll have to see how it plays out.
Deployment
Dave argues for improved deployment. Yes we have Capistrano, and it’s arguably the best web deployment mechanism in existence, but the web of knowledge required to deploy a Rails application is too large, varied and unwieldy.
Capistrano is a push model, ideal for small shops where the developers manage and maintain everything, but it’s not the real world. Larger shops have separate development and admin departments. They have stricter requirements for where files go, pre
and post-deployment hooks, roles, passwords, security, etc. Capistrano currently falls down a bit in this environment.
Dave discusses cap --deploy-on
: a hypothetical extension to Capistrano that would allow staged deployment to multiple servers, and the possibility to introduce workflow between developers and administrators. What if ISPs had a standardized infrastructure that would allow hobbyists to issue a cap --deploy-on
and instantly have their apps running up on TextDrive, DreamHost, etc. To go further, what if we could deploy from rubygems? gem deploy <name> --on cap://my.isp.com
. Typo installation would literally be a single command.
Closing
All developers need to be happy, so let’s make their lives easier!
Posted in ruby, rails | Tags railsconf | no comments | no trackbacks
Posted by Nick Sieger
Fri, 23 Jun 2006 14:10:00 GMT
I got lucky with a last minute ticket and found a way to get to the First International Rails Conference in Chicago.
That’s me, hope to see you this weekend!
Posted in ruby, rails | Tags railsconf | no comments | no trackbacks