+ All Categories
Transcript
Page 1: Building Cloud Castles

Ben Scofield / @bscofield

and Finding Firmer FoundationsBuilding Cloud Castles

West End Ruby / 2 Feb 2011

Page 2: Building Cloud Castles

flickr: natlockwood

WORK IN PROGRESSthis presentation is a

Page 3: Building Cloud Castles
Page 4: Building Cloud Castles
Page 5: Building Cloud Castles

flickr: turtlemom_nancy

THE CLOUD

Page 6: Building Cloud Castles

flickr: sizemoresr

IS FAR AWAY

Page 7: Building Cloud Castles

LIMITED ACCESS

Page 8: Building Cloud Castles

DIAGNOSIS

Page 9: Building Cloud Castles

$ ssh [email protected] production.server #1 SMP Sat Dec 5 16:04:55 UTC 2009 i686

To access official Ubuntu documentation, please visit:http://help.ubuntu.com/Last login: Fri Jan 28 16:33:49 2011 from local.hostdeploy@production:~$ cd /var/log/apache2deploy@production:/var/log/apache2$ tail error.log[Sun Jan 23 06:25:02 2011] [notice] Apache/2.2.12 (Ubuntu) Phusion_Passenger/2.2.11... [Tue Jan 25 15:21:42 2011] [error] [client 118.129.166.97] Invalid URI in request G...[Fri Jan 28 12:01:50 2011] [error] [client 85.132.70.133] client sent HTTP/1.1 requ...[Sun Jan 30 06:25:06 2011] [notice] SIGUSR1 received. Doing graceful restart

Page 10: Building Cloud Castles

http://hoptoadapp.com

Page 11: Building Cloud Castles

http://newrelic.com

Page 12: Building Cloud Castles

http://newrelic.com - application dashboard

Page 13: Building Cloud Castles

REPAIR

Page 14: Building Cloud Castles

$ ssh [email protected] production.server #1 SMP Sat Dec 5 16:04:55 UTC 2009 i686

To access official Ubuntu documentation, please visit:http://help.ubuntu.com/Last login: Fri Jan 28 16:33:49 2011 from local.hostdeploy@production:~$ cd /var/www/app/current/deploy@production:/var/www/app/current$ rails console productionLoading production environment (Rails 3.0.3)>> Article.count => 112>> Article.where(:problem => true).update_attributes(:problem => false)

Page 15: Building Cloud Castles

require 'test_helper'

class ArticleTest < ActiveSupport::TestCase context 'Broken articles' do setup do 5.times.do { Factory :broken_article } end

should 'be fixable' do assert_equal 5, Article.where(:problem => true).count Article.fix_problem_articles assert_equal 0, Article.where(:problem => true).count end endend

class Article def self.fix_problem_articles where(:problem => true).update_attributes(:problem => false) endend

Page 16: Building Cloud Castles
Page 17: Building Cloud Castles

flickr: turtlemom_nancy

THE CLOUD

Page 18: Building Cloud Castles

flickr: 93921318@N00

IS UNRELIABLE

Page 19: Building Cloud Castles

FILESYSTEMS

Page 20: Building Cloud Castles

class Comic < ActiveRecord::Base has_attached_file :cover, :styles => { :thumb => "80x120>", :medium => "300x450>" }end

$ cd public/system$ ls /covers10/ 12/ 53/ 81/$ ls /covers/10/medium/ original/ thumb/$ ls /covers/10/mediumbatman-450.png

Page 21: Building Cloud Castles

class Comic < ActiveRecord::Base has_attached_file :cover, :storage => s3, :s3_credentials => { :access_key_id => ENV['s3_key'], :secret_access_key => ENV['s3_secret'] }, :bucket => 'comicsapp', :url => ":s3_path_url", :s3_headers => { 'Expires' => 1.year.from_now.httpdate }, :styles => { :thumb => "80x120>", :medium => "300x450>" }end

Page 22: Building Cloud Castles

class Comic < ActiveRecord::Base has_attached_file :cover, :storage => s3, :s3_credentials => { :access_key_id => ENV['s3_key'], :secret_access_key => ENV['s3_secret'] }, :bucket => 'comicsapp', :url => ":s3_path_url", :s3_headers => { 'Expires' => 1.year.from_now.httpdate }, :styles => { :thumb => "80x120>", :medium => "300x450>" }end

Page 23: Building Cloud Castles

module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('/tmp') endend

Page 24: Building Cloud Castles

module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('./tmp') endend

Page 25: Building Cloud Castles

module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('./tmp') endend

Page 26: Building Cloud Castles
Page 27: Building Cloud Castles

flickr: turtlemom_nancy

THE CLOUD

Page 28: Building Cloud Castles

flickr: lensonlife

IS HOSTILE

Page 29: Building Cloud Castles

EXTERNAL SERVICES

Page 30: Building Cloud Castles

class Searcher def self.search(term) Article.where(['content ILIKE ?', "%#{term}%"]) endend

Page 31: Building Cloud Castles

class Searcher cattr_accessor :index

def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end

def self.search(term) raw = self.index.search(term, :function => 1) results = raw['results'].to_a

article_ids = results.map {|result| result['docid'] }

unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact endend

Page 32: Building Cloud Castles

class Searcher cattr_accessor :index

def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end

def self.search(term) results = begin raw = self.index.search(term, :function => 1) raw['results'].to_a rescue URI::InvalidURIError # An IndexTank error occurred search_by_sql(term)['results'] end

article_ids = results.map {|result| result['docid'] }

unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact end

def self.search_by_sql(term) {'results' => Article.where(['content ILIKE ?', "%#{term}%"]). map {|a| {'docid' => a.id}}} endend

Page 33: Building Cloud Castles

class Searcher cattr_accessor :index

def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end

def self.search(term) results = begin raw = self.index.search(term, :function => 1) raw['results'].to_a rescue URI::InvalidURIError # An IndexTank error occurred search_by_sql(term)['results'] end

article_ids = results.map {|result| result['docid'] }

unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact end

def self.search_by_sql(term) {'results' => Article.where(['content ILIKE ?', "%#{term}%"]). map {|a| {'docid' => a.id}}} endend

Page 34: Building Cloud Castles
Page 35: Building Cloud Castles

flickr: turtlemom_nancy

THE CLOUD

Page 36: Building Cloud Castles

flickr: 3sth3r

IS RECYCLED

Page 37: Building Cloud Castles

CACHING

Page 38: Building Cloud Castles

class BooksController < ApplicationController caches_page :index def index @books = Book.all endend

Page 39: Building Cloud Castles

class BooksController < ApplicationController caches_action :index def index @books = Book.all endend

module CardCatalog class Application < Rails::Application # ... ActionController::Base.cache_store = :mem_cache_store, "memcache_host" endend

Page 40: Building Cloud Castles

class BooksController < ApplicationController caches_action :index def index @books = Book.all endend

module CardCatalog class Application < Rails::Application # ... ActionController::Base.cache_store = :mem_cache_store, "memcache_host" endend

Page 41: Building Cloud Castles

class BooksController < ApplicationController def index response.headers['Cache-Control'] = 'public, max-age=300' @books = Book.all endend

Page 42: Building Cloud Castles

class BooksController < ApplicationController def index response.headers['Cache-Control'] = 'public, max-age=300' @books = Book.all endend

Page 43: Building Cloud Castles
Page 44: Building Cloud Castles

flickr: turtlemom_nancy

THE CLOUD

Page 45: Building Cloud Castles

flickr: dev07

IS MADE OF TINY PARTS

Page 46: Building Cloud Castles

THINKING SMALL

Page 47: Building Cloud Castles

App

Page 48: Building Cloud Castles

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

Page 49: Building Cloud Castles

$ heroku createCreating empty-sword-187....... donehttp://empty-sword-187.heroku.com/ | [email protected]:empty-sword-187.gitGit remote heroku added$ git push heroku masterCounting objects: 5, done.Delta compression using up to 4 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 285 bytes, done.Total 3 (delta 2), reused 0 (delta 0)

-----> Heroku receiving push-----> Rails app detected-----> Detected use of caches_page Installing caches_page_via_http plugin... done-----> Detected Rails is not set to serve static_assets Installing rails3_serve_static_assets... done-----> Gemfile detected, running Bundler version 1.0.7 Unresolved dependencies detected; Installing... Using --without development:test Fetching source index for http://rubygems.org/ ...

Page 50: Building Cloud Castles

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

Page 51: Building Cloud Castles

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

App

Page 52: Building Cloud Castles

HTTP and REST

Page 53: Building Cloud Castles
Page 54: Building Cloud Castles
Page 55: Building Cloud Castles

PATTERNS and VIRTUES

Page 56: Building Cloud Castles

SINGLE RESPONSIBILITY PRINCIPLE

Page 57: Building Cloud Castles

HUMILITY

Page 58: Building Cloud Castles

LAZINESS

Page 59: Building Cloud Castles

PARANOIA

Page 60: Building Cloud Castles

Ben Scofield / @bscofield

Thanks!

West End Ruby / 2 Feb 2011

http://spkr8.com/t/5491http://benscofield.com

http://heroku.com


Top Related