DICAS DE PERFORMANCE
WEB
Fabio Akita @AkitaOnRails
Back EndBrowser
Front End
Render
External
Services
9 DICAS
SLIDESHARE.NET/
AKITAONRAILS
MONITORING
1.4 MILHOES DEVISITANTES
UNICOS!!
Ruby 2.1
Ruby 2.2
CACHING
def	
  sitemap	
  
	
  	
  sleep	
  3	
  
	
  	
  @posts	
  =	
  Post.select([	
  :id,	
  :slug,	
  :updated_at,	
  :published_at	
  ]).published	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.xml	
  {	
  render	
  layout:	
  false	
  }	
  
	
  	
  end	
  
end
Processing	
  by	
  ArchivesController#sitemap	
  as	
  XML	
  
	
  	
  Rendered	
  archives/sitemap.xml.builder	
  (785.5ms)	
  
Completed	
  200	
  OK	
  in	
  3879.4ms	
  (Views:	
  770.4ms	
  |	
  ActiveRecord:	
  18.7ms	
  |	
  Solr:	
  0.0ms)
def	
  sitemap	
  
	
  	
  @posts	
  =	
  cache('sitemap',	
  :expires_in	
  =>	
  12.hours)	
  {	
  
	
  	
  	
  	
  sleep	
  3	
  
	
  	
  	
  	
  Post.select([	
  :id,	
  :slug,	
  :updated_at,	
  :published_at	
  ]).published	
  
	
  	
  }	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.xml	
  {	
  render	
  layout:	
  false	
  }	
  
	
  	
  end	
  
end
Processing	
  by	
  ArchivesController#sitemap	
  as	
  XML	
  
	
  	
  Rendered	
  archives/sitemap.xml.builder	
  (763.3ms)	
  
Completed	
  200	
  OK	
  in	
  842.4ms	
  (Views:	
  754.2ms	
  |	
  ActiveRecord:	
  13.3ms	
  |	
  Solr:	
  0.0ms)
def	
  sitemap	
  
	
  	
  @posts	
  =	
  cache('sitemap',	
  :expires_in	
  =>	
  12.hours)	
  {	
  
	
  	
  	
  	
  Post.select([	
  :id,	
  :slug,	
  :updated_at,	
  :published_at	
  ]).published	
  
	
  	
  }	
  
	
  	
  if	
  stale?(last_modified:	
  @posts.first.updated_at.utc,	
  
	
  	
  	
  	
  etag:	
  "posts/#{@posts.count}-­‐#{@posts.first.updated_at.utc}")	
  
	
  	
  	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  	
  	
  format.xml	
  {	
  render	
  layout:	
  false	
  }	
  
	
  	
  	
  	
  end	
  
	
  	
  end	
  
end
Processing	
  by	
  ArchivesController#sitemap	
  as	
  XML	
  
Completed	
  304	
  Not	
  Modified	
  in	
  6.2ms	
  (ActiveRecord:	
  1.7ms)
2s!
198ms!
ASSET PIPELINE
}
}
UPLOADS
Browser
1s
BackendBrowser
1s
Image
Magick
BackendBrowser
1s
S3
Image
Magick
BackendBrowser
1s
S3
Image
Magick
BackendBrowser
1s 2 segundos
Browser
1s
Browser
1s
S3
BackendBrowser
1s
S3
BackendBrowser
1s 200 ms
S3
BackendBrowser
1s 200 ms
S3
Image
Magick
700 ms
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  config/cloudinary.yml	
  
production:	
  
	
  	
  cloud_name:	
  "sample"	
  
	
  	
  api_key:	
  "874837483274837"	
  
	
  	
  api_secret:	
  "a676b67565c6767a6767d6767f676fe1"
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  config/cloudinary.yml	
  
production:	
  
	
  	
  cloud_name:	
  "sample"	
  
	
  	
  api_key:	
  "874837483274837"	
  
	
  	
  api_secret:	
  "a676b67565c6767a6767d6767f676fe1"#	
  app/models/user.rb	
  
class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  attr_accessible	
  :name	
  
	
  	
  	
  
	
  	
  has_attachment	
  	
  :avatar	
  
	
  	
  has_attachments	
  :photos,	
  maximum:	
  3	
  
end
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  config/cloudinary.yml	
  
production:	
  
	
  	
  cloud_name:	
  "sample"	
  
	
  	
  api_key:	
  "874837483274837"	
  
	
  	
  api_secret:	
  "a676b67565c6767a6767d6767f676fe1"#	
  app/models/user.rb	
  
class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  attr_accessible	
  :name	
  
	
  	
  	
  
	
  	
  has_attachment	
  	
  :avatar	
  
	
  	
  has_attachments	
  :photos,	
  maximum:	
  3	
  
end
#	
  app/views/users/_form.html.slim	
  
=	
  form_for(@user)	
  do	
  |user_form|	
  
	
  	
  	
  	
  =	
  user_form.text_field(:name)	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[avatar]',	
  
	
  	
  	
  	
  	
  	
  @user,	
  :avatar	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[photos]’,	
  
	
  	
  	
  	
  	
  	
  @user,	
  :photos	
  
	
  	
  	
  	
  =	
  user_form.submit("Save")
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  config/cloudinary.yml	
  
production:	
  
	
  	
  cloud_name:	
  "sample"	
  
	
  	
  api_key:	
  "874837483274837"	
  
	
  	
  api_secret:	
  "a676b67565c6767a6767d6767f676fe1"#	
  app/models/user.rb	
  
class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  attr_accessible	
  :name	
  
	
  	
  	
  
	
  	
  has_attachment	
  	
  :avatar	
  
	
  	
  has_attachments	
  :photos,	
  maximum:	
  3	
  
end
#	
  app/views/users/_form.html.slim	
  
=	
  form_for(@user)	
  do	
  |user_form|	
  
	
  	
  	
  	
  =	
  user_form.text_field(:name)	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[avatar]',	
  
	
  	
  	
  	
  	
  	
  @user,	
  :avatar	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[photos]’,	
  
	
  	
  	
  	
  	
  	
  @user,	
  :photos	
  
	
  	
  	
  	
  =	
  user_form.submit("Save")
#	
  app/controllers/users_controller.rb	
  
...	
  
	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(params[:user])	
  
	
  	
  	
  	
  @user.save!	
  
	
  	
  end
#	
  app/views/users/show.html.slim	
  
-­‐	
  if	
  user.avatar.present?	
  
	
  	
  =	
  cl_image_tag(user.avatar.path,	
  width:	
  80,	
  height:	
  100,	
  crop:	
  :thumb,	
  gravity:	
  :face)	
  
	
  	
  -­‐	
  user.photos.each	
  do	
  |photo|	
  
	
  	
  	
  	
  =	
  cl_image_tag(photo.path,	
  size:	
  '70x50',	
  crop:	
  :fill,	
  radius:	
  20)
#	
  Gemfile	
  
gem	
  'cloudinary'	
  
gem	
  'attachinary'
#	
  config/cloudinary.yml	
  
production:	
  
	
  	
  cloud_name:	
  "sample"	
  
	
  	
  api_key:	
  "874837483274837"	
  
	
  	
  api_secret:	
  "a676b67565c6767a6767d6767f676fe1"#	
  app/models/user.rb	
  
class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  attr_accessible	
  :name	
  
	
  	
  	
  
	
  	
  has_attachment	
  	
  :avatar	
  
	
  	
  has_attachments	
  :photos,	
  maximum:	
  3	
  
end
#	
  app/views/users/_form.html.slim	
  
=	
  form_for(@user)	
  do	
  |user_form|	
  
	
  	
  	
  	
  =	
  user_form.text_field(:name)	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[avatar]',	
  
	
  	
  	
  	
  	
  	
  @user,	
  :avatar	
  
	
  	
  	
  	
  =	
  attachinary_file_field_tag	
  ‘user[photos]’,	
  
	
  	
  	
  	
  	
  	
  @user,	
  :photos	
  
	
  	
  	
  	
  =	
  user_form.submit("Save")
#	
  app/controllers/users_controller.rb	
  
...	
  
	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(params[:user])	
  
	
  	
  	
  	
  @user.save!	
  
	
  	
  end
CDN
AssetsRailsBrowser
1s 5 segundos
Assets
RailsBrowser
1s 200 ms
4 segundos
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://www.mydomain.com/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://www.mydomain.com/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://www.mydomain.com/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://www.mydomain.com/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://d3vam04na8c92l.cloudfront.net/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
http://www.mydomain.com/stylesheets/application-6502e5a88f02b90aeb32c2dd21ea37ab.css
#	
  config/environments/production.rb	
  
config.action_controller.asset_host	
  =	
  "d3vam04na8c92l.cloudfront.net"
<%=	
  stylesheet_link_tag	
  	
  	
  	
  "application",	
  :media	
  =>	
  "all"	
  %>	
  
<%=	
  javascript_include_tag	
  "application"	
  %>	
  
<%=	
  image_tag("rails.png")	
  %>
<link	
  href="http://d3vam04na8c92l.cloudfront.net/stylesheets/application-­‐6502e5a88f02b90aeb32c2dd21ea37ab.css"	
  rel="stylesheet"	
  />
DATABASE
#	
  Gemfile	
  
gem	
  "lol_dba",	
  group:	
  :development	
  
#	
  no	
  Terminal	
  
bundle	
  install	
  
bundle	
  exec	
  rake	
  db:find_indexes
#	
  app/controllers/posts_controller.rb	
  
def	
  index	
  
	
  	
  @posts	
  =	
  Post.all	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.html	
  #	
  index.html.erb	
  
	
  	
  	
  	
  format.xml	
  	
  {	
  render	
  :xml	
  =>	
  @posts	
  }	
  
	
  	
  end	
  
end
#	
  app/controllers/posts_controller.rb	
  
def	
  index	
  
	
  	
  @posts	
  =	
  Post.all	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.html	
  #	
  index.html.erb	
  
	
  	
  	
  	
  format.xml	
  	
  {	
  render	
  :xml	
  =>	
  @posts	
  }	
  
	
  	
  end	
  
end
<%	
  @posts.each	
  do	
  |post|	
  %>	
  
	
  	
  <tr>	
  
	
  	
  	
  	
  <td><%=	
  post.name	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  post.comments.size	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Show',	
  post	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Edit',	
  edit_post_path(post)	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Destroy',	
  post,	
  confirm:	
  'Are	
  you	
  sure?',	
  method:	
  :delete	
  %></td>	
  
	
  	
  </tr>	
  
<%	
  end	
  %>
#	
  app/controllers/posts_controller.rb	
  
def	
  index	
  
	
  	
  @posts	
  =	
  Post.all	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.html	
  #	
  index.html.erb	
  
	
  	
  	
  	
  format.xml	
  	
  {	
  render	
  :xml	
  =>	
  @posts	
  }	
  
	
  	
  end	
  
end
<%	
  @posts.each	
  do	
  |post|	
  %>	
  
	
  	
  <tr>	
  
	
  	
  	
  	
  <td><%=	
  post.name	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  post.comments.size	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Show',	
  post	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Edit',	
  edit_post_path(post)	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Destroy',	
  post,	
  confirm:	
  'Are	
  you	
  sure?',	
  method:	
  :delete	
  %></td>	
  
	
  	
  </tr>	
  
<%	
  end	
  %>
Started	
  GET	
  "/posts/"	
  for	
  192.168.47.2	
  at	
  2015-­‐03-­‐23	
  18:31:11	
  +0000	
  
Cannot	
  render	
  console	
  from	
  192.168.47.2!	
  Allowed	
  networks:	
  127.0.0.1,	
  ::1,	
  127.0.0.0/127.255.255.255	
  
	
  	
  ActiveRecord::SchemaMigration	
  Load	
  (0.1ms)	
  	
  SELECT	
  "schema_migrations".*	
  FROM	
  "schema_migrations"	
  
Processing	
  by	
  PostsController#index	
  as	
  HTML	
  
	
  	
  Post	
  Load	
  (0.2ms)	
  	
  SELECT	
  "posts".*	
  FROM	
  "posts"	
  
	
  	
  Comment	
  Load	
  (0.2ms)	
  	
  SELECT	
  "comments".*	
  FROM	
  "comments"	
  WHERE	
  "comments"."post_id"	
  =	
  ?	
  	
  [["post_id",	
  1]]	
  
	
  	
  Comment	
  Load	
  (0.1ms)	
  	
  SELECT	
  "comments".*	
  FROM	
  "comments"	
  WHERE	
  "comments"."post_id"	
  =	
  ?	
  	
  [["post_id",	
  2]]	
  
	
  	
  Rendered	
  posts/index.html.erb	
  within	
  layouts/application	
  (25.3ms)	
  
Completed	
  200	
  OK	
  in	
  679ms	
  (Views:	
  666.4ms	
  |	
  ActiveRecord:	
  1.0ms)
#	
  app/controllers/posts_controller.rb	
  
def	
  index	
  
	
  	
  @posts	
  =	
  Post.all	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.html	
  #	
  index.html.erb	
  
	
  	
  	
  	
  format.xml	
  	
  {	
  render	
  :xml	
  =>	
  @posts	
  }	
  
	
  	
  end	
  
end
<%	
  @posts.each	
  do	
  |post|	
  %>	
  
	
  	
  <tr>	
  
	
  	
  	
  	
  <td><%=	
  post.name	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  post.comments.size	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Show',	
  post	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Edit',	
  edit_post_path(post)	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Destroy',	
  post,	
  confirm:	
  'Are	
  you	
  sure?',	
  method:	
  :delete	
  %></td>	
  
	
  	
  </tr>	
  
<%	
  end	
  %>
Started	
  GET	
  "/posts/"	
  for	
  192.168.47.2	
  at	
  2015-­‐03-­‐23	
  18:31:11	
  +0000	
  
Cannot	
  render	
  console	
  from	
  192.168.47.2!	
  Allowed	
  networks:	
  127.0.0.1,	
  ::1,	
  127.0.0.0/127.255.255.255	
  
	
  	
  ActiveRecord::SchemaMigration	
  Load	
  (0.1ms)	
  	
  SELECT	
  "schema_migrations".*	
  FROM	
  "schema_migrations"	
  
Processing	
  by	
  PostsController#index	
  as	
  HTML	
  
	
  	
  Post	
  Load	
  (0.2ms)	
  	
  SELECT	
  "posts".*	
  FROM	
  "posts"	
  
	
  	
  Comment	
  Load	
  (0.2ms)	
  	
  SELECT	
  "comments".*	
  FROM	
  "comments"	
  WHERE	
  "comments"."post_id"	
  =	
  ?	
  	
  [["post_id",	
  1]]	
  
	
  	
  Comment	
  Load	
  (0.1ms)	
  	
  SELECT	
  "comments".*	
  FROM	
  "comments"	
  WHERE	
  "comments"."post_id"	
  =	
  ?	
  	
  [["post_id",	
  2]]	
  
	
  	
  Rendered	
  posts/index.html.erb	
  within	
  layouts/application	
  (25.3ms)	
  
Completed	
  200	
  OK	
  in	
  679ms	
  (Views:	
  666.4ms	
  |	
  ActiveRecord:	
  1.0ms)
Started	
  GET	
  "/posts/"	
  for	
  192.168.47.2	
  at	
  2015-­‐03-­‐23	
  18:37:31	
  +0000	
  
Cannot	
  render	
  console	
  from	
  192.168.47.2!	
  Allowed	
  networks:	
  127.0.0.1,	
  ::1,	
  127.0.0.0/127.255.255.255	
  
	
  	
  ActiveRecord::SchemaMigration	
  Load	
  (0.1ms)	
  	
  SELECT	
  "schema_migrations".*	
  FROM	
  "schema_migrations"	
  
Processing	
  by	
  PostsController#index	
  as	
  HTML	
  
	
  	
  Post	
  Load	
  (0.2ms)	
  	
  SELECT	
  "posts".*	
  FROM	
  "posts"	
  
	
  	
  Comment	
  Load	
  (0.2ms)	
  	
  SELECT	
  "comments".*	
  FROM	
  "comments"	
  WHERE	
  "comments"."post_id"	
  IN	
  (1,	
  2)	
  
	
  	
  Rendered	
  posts/index.html.erb	
  within	
  layouts/application	
  (25.8ms)	
  
Completed	
  200	
  OK	
  in	
  109ms	
  (Views:	
  96.4ms	
  |	
  ActiveRecord:	
  0.9ms)
#	
  app/controllers/posts_controller.rb	
  
def	
  index	
  
	
  	
  @posts	
  =	
  Post.includes(:comments).all	
  
	
  	
  respond_to	
  do	
  |format|	
  
	
  	
  	
  	
  format.html	
  #	
  index.html.erb	
  
	
  	
  	
  	
  format.xml	
  	
  {	
  render	
  :xml	
  =>	
  @posts	
  }	
  
	
  	
  end	
  
end
<%	
  @posts.each	
  do	
  |post|	
  %>	
  
	
  	
  <tr>	
  
	
  	
  	
  	
  <td><%=	
  post.name	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  post.comments.size	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Show',	
  post	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Edit',	
  edit_post_path(post)	
  %></td>	
  
	
  	
  	
  	
  <td><%=	
  link_to	
  'Destroy',	
  post,	
  confirm:	
  'Are	
  you	
  sure?',	
  method:	
  :delete	
  %></td>	
  
	
  	
  </tr>	
  
<%	
  end	
  %>
SEARCH
ASYNC JOBS
Browser
1s
BackendBrowser
1s
Tarefa DemoradaBackendBrowser
1s
Tarefa DemoradaBackendBrowser
1s 4 segundos
Browser
1s
BackendBrowser
1s
BackendBrowser
1s
Redis
BackendBrowser
1s 200 ms
Redis
BackendBrowser
1s 200 ms
Redis
Tarefa Demorada
(Worker)
3.8 segundos
class	
  VerySlowJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(*args)	
  
	
  	
  	
  	
  #	
  Tarefa	
  Demorada	
  
	
  	
  end	
  
end
class	
  VerySlowJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(*args)	
  
	
  	
  	
  	
  #	
  Tarefa	
  Demorada	
  
	
  	
  end	
  
end
VerySlowJob.perform_later	
  record
class	
  VerySlowJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(*args)	
  
	
  	
  	
  	
  #	
  Tarefa	
  Demorada	
  
	
  	
  end	
  
end
VerySlowJob.perform_later	
  record
VerySlowJob.set(wait_until:	
  
Date.tomorrow.noon).perform_later(record)
class	
  VerySlowJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(*args)	
  
	
  	
  	
  	
  #	
  Tarefa	
  Demorada	
  
	
  	
  end	
  
end
VerySlowJob.set(wait:	
  1.week).perform_later(record)
VerySlowJob.perform_later	
  record
VerySlowJob.set(wait_until:	
  
Date.tomorrow.noon).perform_later(record)
#	
  app/workers/report_job.rb	
  
class	
  ReportJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(email,	
  from_date	
  =	
  nil,	
  to_date	
  =	
  nil)	
  
	
  	
  	
  	
  now	
  =	
  Time.zone.now	
  
	
  	
  	
  	
  from_date	
  ||=	
  now	
  -­‐	
  1.year	
  
	
  	
  	
  	
  to_date	
  	
  	
  ||=	
  now	
  
	
  	
  	
  	
  file_path	
  =	
  ModelGigante.generate_excel(from_date,	
  to_date)	
  
	
  	
  	
  	
  NotifyReportMailer.send(email,	
  file_path)	
  
	
  	
  end	
  
end
#	
  app/controllers/reports_controller.rb	
  
#	
  ...	
  
def	
  create	
  
	
  	
  ReportJob.perform_later(params[:email],	
  
	
  	
  	
  	
  params[:from_date],	
  params[:to_date])	
  
	
  	
  flash[:notice]	
  =	
  "Report	
  is	
  being	
  generated.	
  
	
  	
  	
  	
  It	
  will	
  be	
  sent	
  to	
  your	
  email."	
  
	
  	
  redirect_to	
  reports_path	
  
end	
  
#	
  ...
#	
  app/workers/report_job.rb	
  
class	
  ReportJob	
  <	
  ActiveJob::Base	
  
	
  	
  queue_as	
  :default	
  
	
  	
  
	
  	
  def	
  perform(email,	
  from_date	
  =	
  nil,	
  to_date	
  =	
  nil)	
  
	
  	
  	
  	
  now	
  =	
  Time.zone.now	
  
	
  	
  	
  	
  from_date	
  ||=	
  now	
  -­‐	
  1.year	
  
	
  	
  	
  	
  to_date	
  	
  	
  ||=	
  now	
  
	
  	
  	
  	
  file_path	
  =	
  ModelGigante.generate_excel(from_date,	
  to_date)	
  
	
  	
  	
  	
  NotifyReportMailer.send(email,	
  file_path)	
  
	
  	
  end	
  
end
ASYNC MESSAGES
http://blog.tryneighborly.com/amazon-sns-for-apns-on-rails/
<!-­‐-­‐	
  chat.html	
  -­‐-­‐>	
  
<script	
  src="//js.pusher.com/2.2/pusher.min.js"></script>	
  
<script>	
  
$(function()	
  {	
  
	
  	
  	
  	
  var	
  pusher	
  =	
  new	
  Pusher('YOUR_APP_KEY');	
  
	
  	
  	
  	
  var	
  channel	
  =	
  pusher.subscribe('my-­‐app-­‐chat');	
  
	
  	
  	
  	
  channel.bind('new-­‐message',	
  function(data)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $('#chat-­‐box').append(data.message);	
  
	
  	
  	
  	
  });	
  
})	
  
</script>
<!-­‐-­‐	
  chat.html	
  -­‐-­‐>	
  
<script	
  src="//js.pusher.com/2.2/pusher.min.js"></script>	
  
<script>	
  
$(function()	
  {	
  
	
  	
  	
  	
  var	
  pusher	
  =	
  new	
  Pusher('YOUR_APP_KEY');	
  
	
  	
  	
  	
  var	
  channel	
  =	
  pusher.subscribe('my-­‐app-­‐chat');	
  
	
  	
  	
  	
  channel.bind('new-­‐message',	
  function(data)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $('#chat-­‐box').append(data.message);	
  
	
  	
  	
  	
  });	
  
})	
  
</script>
#	
  config/initializer/pusher.rb	
  
Pusher.app_id	
  =	
  ENV['PUSHER_APP_ID']	
  
Pusher.key	
  	
  	
  	
  =	
  ENV['PUSHER_APP_KEY']	
  
Pusher.secret	
  =	
  ENV['PUSHER_APP_SECRET']	
  
<!-­‐-­‐	
  chat.html	
  -­‐-­‐>	
  
<script	
  src="//js.pusher.com/2.2/pusher.min.js"></script>	
  
<script>	
  
$(function()	
  {	
  
	
  	
  	
  	
  var	
  pusher	
  =	
  new	
  Pusher('YOUR_APP_KEY');	
  
	
  	
  	
  	
  var	
  channel	
  =	
  pusher.subscribe('my-­‐app-­‐chat');	
  
	
  	
  	
  	
  channel.bind('new-­‐message',	
  function(data)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $('#chat-­‐box').append(data.message);	
  
	
  	
  	
  	
  });	
  
})	
  
</script>
#	
  config/initializer/pusher.rb	
  
Pusher.app_id	
  =	
  ENV['PUSHER_APP_ID']	
  
Pusher.key	
  	
  	
  	
  =	
  ENV['PUSHER_APP_KEY']	
  
Pusher.secret	
  =	
  ENV['PUSHER_APP_SECRET']	
  
#	
  app/workers/new_message_worker.rb	
  
require	
  'pusher'	
  
class	
  NewMessageWorker	
  
	
  	
  include	
  Sidekiq::Worker	
  
	
  	
  	
  
	
  	
  def	
  perform(message_id)	
  
	
  	
  	
  	
  @message	
  =	
  Message.find(message_id)	
  
	
  	
  	
  	
  Pusher.trigger('my-­‐app-­‐chat',	
  'new-­‐message',	
  {:message	
  =>	
  @message})	
  
	
  	
  end	
  
end
<!-­‐-­‐	
  chat.html	
  -­‐-­‐>	
  
<script	
  src="//js.pusher.com/2.2/pusher.min.js"></script>	
  
<script>	
  
$(function()	
  {	
  
	
  	
  	
  	
  var	
  pusher	
  =	
  new	
  Pusher('YOUR_APP_KEY');	
  
	
  	
  	
  	
  var	
  channel	
  =	
  pusher.subscribe('my-­‐app-­‐chat');	
  
	
  	
  	
  	
  channel.bind('new-­‐message',	
  function(data)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $('#chat-­‐box').append(data.message);	
  
	
  	
  	
  	
  });	
  
})	
  
</script>
#	
  app/controllers/messages_controller.rb	
  
#	
  ...	
  
def	
  create	
  
	
  	
  @message	
  =	
  Message.new(params[:message])	
  
	
  	
  if	
  @messsage.save	
  
	
  	
  	
  	
  NewMessageWorker.perform_async(@message.id)	
  
	
  	
  	
  	
  #	
  ...	
  
end	
  
#	
  ...
#	
  config/initializer/pusher.rb	
  
Pusher.app_id	
  =	
  ENV['PUSHER_APP_ID']	
  
Pusher.key	
  	
  	
  	
  =	
  ENV['PUSHER_APP_KEY']	
  
Pusher.secret	
  =	
  ENV['PUSHER_APP_SECRET']	
  
#	
  app/workers/new_message_worker.rb	
  
require	
  'pusher'	
  
class	
  NewMessageWorker	
  
	
  	
  include	
  Sidekiq::Worker	
  
	
  	
  	
  
	
  	
  def	
  perform(message_id)	
  
	
  	
  	
  	
  @message	
  =	
  Message.find(message_id)	
  
	
  	
  	
  	
  Pusher.trigger('my-­‐app-­‐chat',	
  'new-­‐message',	
  {:message	
  =>	
  @message})	
  
	
  	
  end	
  
end
TUNING RUBY
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
• Mark and Sweep GC (1.8+)
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
• Mark and Sweep GC (1.8+)
• Bitmap Marking GC (1.9.3+)
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
• Mark and Sweep GC (1.8+)
• Bitmap Marking GC (1.9.3+)
• Lazy Sweep GC (2.0.0+)
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
• Mark and Sweep GC (1.8+)
• Bitmap Marking GC (1.9.3+)
• Lazy Sweep GC (2.0.0+)
• Restricted Generational GC (RGenGC 2.1.0+)
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
• Mark and Sweep GC (1.8+)
• Bitmap Marking GC (1.9.3+)
• Lazy Sweep GC (2.0.0+)
• Restricted Generational GC (RGenGC 2.1.0+)
• Restricted Incremental GC (RIncGC 2.2.0+)
http://www.infoq.com/news/2014/12/ruby-2.2.0-released
TRADE-OFF:
+ GC OU + RAM
#	
  Gemfile	
  
source	
  'https://rubygems.org'	
  
ruby	
  "2.2.1"	
  
#	
  performance	
  tuning	
  
gem	
  'escape_utils'	
  
gem	
  'fast_blank'	
  
gem	
  'oj'	
  
gem	
  'oj_mimic_json'
http://marianposaceanu.com/articles/improve-rails-performance-by-adding-a-few-gems
#	
  Gemfile	
  
source	
  'https://rubygems.org'	
  
if	
  ENV['RAILS_ENV']	
  ==	
  'production'	
  
	
  	
  ruby	
  '1.9.3',	
  :engine	
  =>	
  'jruby',	
  :engine_version	
  =>	
  '1.7.18'	
  
else	
  
	
  	
  ruby	
  '2.2.1'	
  
end	
  
gem	
  'rails',	
  '4.2.0'	
  
gem	
  'rails-­‐api'	
  
gem	
  'srt'	
  
gem	
  'pg',	
  platforms:	
  'mri'	
  
gem	
  'activerecord-­‐jdbcpostgresql-­‐adapter',	
  platforms:	
  'jruby'	
  
gem	
  'sidekiq'	
  
gem	
  'dalli'	
  
gem	
  'puma'	
  
gem	
  'rack-­‐cache'	
  
gem	
  'rack-­‐cors',	
  require:	
  'rack/cors'	
  
gem	
  'rails_12factor'	
  
gem	
  'newrelic_rpm'
#	
  Gemfile	
  
source	
  'https://rubygems.org'	
  
if	
  ENV['RAILS_ENV']	
  ==	
  'production'	
  
	
  	
  ruby	
  '1.9.3',	
  :engine	
  =>	
  'jruby',	
  :engine_version	
  =>	
  '1.7.18'	
  
else	
  
	
  	
  ruby	
  '2.2.1'	
  
end	
  
gem	
  'rails',	
  '4.2.0'	
  
gem	
  'rails-­‐api'	
  
gem	
  'srt'	
  
gem	
  'pg',	
  platforms:	
  'mri'	
  
gem	
  'activerecord-­‐jdbcpostgresql-­‐adapter',	
  platforms:	
  'jruby'	
  
gem	
  'sidekiq'	
  
gem	
  'dalli'	
  
gem	
  'puma'	
  
gem	
  'rack-­‐cache'	
  
gem	
  'rack-­‐cors',	
  require:	
  'rack/cors'	
  
gem	
  'rails_12factor'	
  
gem	
  'newrelic_rpm'
#	
  Gemfile	
  
source	
  'https://rubygems.org'	
  
if	
  ENV['RAILS_ENV']	
  ==	
  'production'	
  
	
  	
  ruby	
  '1.9.3',	
  :engine	
  =>	
  'jruby',	
  :engine_version	
  =>	
  '1.7.18'	
  
else	
  
	
  	
  ruby	
  '2.2.1'	
  
end	
  
gem	
  'rails',	
  '4.2.0'	
  
gem	
  'rails-­‐api'	
  
gem	
  'srt'	
  
gem	
  'pg',	
  platforms:	
  'mri'	
  
gem	
  'activerecord-­‐jdbcpostgresql-­‐adapter',	
  platforms:	
  'jruby'	
  
gem	
  'sidekiq'	
  
gem	
  'dalli'	
  
gem	
  'puma'	
  
gem	
  'rack-­‐cache'	
  
gem	
  'rack-­‐cors',	
  require:	
  'rack/cors'	
  
gem	
  'rails_12factor'	
  
gem	
  'newrelic_rpm'
#	
  Gemfile	
  
source	
  'https://rubygems.org'	
  
if	
  ENV['RAILS_ENV']	
  ==	
  'production'	
  
	
  	
  ruby	
  '1.9.3',	
  :engine	
  =>	
  'jruby',	
  :engine_version	
  =>	
  '1.7.18'	
  
else	
  
	
  	
  ruby	
  '2.2.1'	
  
end	
  
gem	
  'rails',	
  '4.2.0'	
  
gem	
  'rails-­‐api'	
  
gem	
  'srt'	
  
gem	
  'pg',	
  platforms:	
  'mri'	
  
gem	
  'activerecord-­‐jdbcpostgresql-­‐adapter',	
  platforms:	
  'jruby'	
  
gem	
  'sidekiq'	
  
gem	
  'dalli'	
  
gem	
  'puma'	
  
gem	
  'rack-­‐cache'	
  
gem	
  'rack-­‐cors',	
  require:	
  'rack/cors'	
  
gem	
  'rails_12factor'	
  
gem	
  'newrelic_rpm'
9 DICAS
Monitoring New Relic
Monitoring New Relic
Caching Memcache, ETAG
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Async Jobs Sidekiq, Redis
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Async Jobs Sidekiq, Redis
Async Messages pusher.com
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Async Jobs Sidekiq, Redis
Async Messages pusher.com
Auto scale HireFire,AdeptScale
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Async Jobs Sidekiq, Redis
Async Messages pusher.com
Auto scale HireFire,AdeptScale
Rubies Ruby 2.2.1, JRuby
Monitoring New Relic
Caching Memcache, ETAG
Assets CloudFlare, Cloudinary
Database PostgreSQL
Search Elastic
Async Jobs Sidekiq, Redis
Async Messages pusher.com
Auto scale HireFire,AdeptScale
Rubies Ruby 2.2.1, JRuby
Deployment Heroku
OBRIGADO
slideshare.net/akitaonrails

QConSP 2015 - Dicas de Performance para Aplicações Web

  • 1.
    DICAS DE PERFORMANCE WEB FabioAkita @AkitaOnRails
  • 11.
  • 15.
  • 16.
  • 17.
  • 26.
  • 30.
  • 31.
  • 35.
  • 40.
    def  sitemap      sleep  3      @posts  =  Post.select([  :id,  :slug,  :updated_at,  :published_at  ]).published      respond_to  do  |format|          format.xml  {  render  layout:  false  }      end   end Processing  by  ArchivesController#sitemap  as  XML      Rendered  archives/sitemap.xml.builder  (785.5ms)   Completed  200  OK  in  3879.4ms  (Views:  770.4ms  |  ActiveRecord:  18.7ms  |  Solr:  0.0ms)
  • 41.
    def  sitemap      @posts  =  cache('sitemap',  :expires_in  =>  12.hours)  {          sleep  3          Post.select([  :id,  :slug,  :updated_at,  :published_at  ]).published      }      respond_to  do  |format|          format.xml  {  render  layout:  false  }      end   end Processing  by  ArchivesController#sitemap  as  XML      Rendered  archives/sitemap.xml.builder  (763.3ms)   Completed  200  OK  in  842.4ms  (Views:  754.2ms  |  ActiveRecord:  13.3ms  |  Solr:  0.0ms)
  • 42.
    def  sitemap      @posts  =  cache('sitemap',  :expires_in  =>  12.hours)  {          Post.select([  :id,  :slug,  :updated_at,  :published_at  ]).published      }      if  stale?(last_modified:  @posts.first.updated_at.utc,          etag:  "posts/#{@posts.count}-­‐#{@posts.first.updated_at.utc}")          respond_to  do  |format|              format.xml  {  render  layout:  false  }          end      end   end Processing  by  ArchivesController#sitemap  as  XML   Completed  304  Not  Modified  in  6.2ms  (ActiveRecord:  1.7ms)
  • 44.
  • 46.
  • 50.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 69.
    #  Gemfile   gem  'cloudinary'   gem  'attachinary'
  • 70.
    #  Gemfile   gem  'cloudinary'   gem  'attachinary' #  config/cloudinary.yml   production:      cloud_name:  "sample"      api_key:  "874837483274837"      api_secret:  "a676b67565c6767a6767d6767f676fe1"
  • 71.
    #  Gemfile   gem  'cloudinary'   gem  'attachinary' #  config/cloudinary.yml   production:      cloud_name:  "sample"      api_key:  "874837483274837"      api_secret:  "a676b67565c6767a6767d6767f676fe1"#  app/models/user.rb   class  User  <  ActiveRecord::Base      attr_accessible  :name            has_attachment    :avatar      has_attachments  :photos,  maximum:  3   end
  • 72.
    #  Gemfile   gem  'cloudinary'   gem  'attachinary' #  config/cloudinary.yml   production:      cloud_name:  "sample"      api_key:  "874837483274837"      api_secret:  "a676b67565c6767a6767d6767f676fe1"#  app/models/user.rb   class  User  <  ActiveRecord::Base      attr_accessible  :name            has_attachment    :avatar      has_attachments  :photos,  maximum:  3   end #  app/views/users/_form.html.slim   =  form_for(@user)  do  |user_form|          =  user_form.text_field(:name)          =  attachinary_file_field_tag  ‘user[avatar]',              @user,  :avatar          =  attachinary_file_field_tag  ‘user[photos]’,              @user,  :photos          =  user_form.submit("Save")
  • 73.
    #  Gemfile   gem  'cloudinary'   gem  'attachinary' #  config/cloudinary.yml   production:      cloud_name:  "sample"      api_key:  "874837483274837"      api_secret:  "a676b67565c6767a6767d6767f676fe1"#  app/models/user.rb   class  User  <  ActiveRecord::Base      attr_accessible  :name            has_attachment    :avatar      has_attachments  :photos,  maximum:  3   end #  app/views/users/_form.html.slim   =  form_for(@user)  do  |user_form|          =  user_form.text_field(:name)          =  attachinary_file_field_tag  ‘user[avatar]',              @user,  :avatar          =  attachinary_file_field_tag  ‘user[photos]’,              @user,  :photos          =  user_form.submit("Save") #  app/controllers/users_controller.rb   ...      def  create          @user  =  User.new(params[:user])          @user.save!      end
  • 74.
    #  app/views/users/show.html.slim   -­‐  if  user.avatar.present?      =  cl_image_tag(user.avatar.path,  width:  80,  height:  100,  crop:  :thumb,  gravity:  :face)      -­‐  user.photos.each  do  |photo|          =  cl_image_tag(photo.path,  size:  '70x50',  crop:  :fill,  radius:  20) #  Gemfile   gem  'cloudinary'   gem  'attachinary' #  config/cloudinary.yml   production:      cloud_name:  "sample"      api_key:  "874837483274837"      api_secret:  "a676b67565c6767a6767d6767f676fe1"#  app/models/user.rb   class  User  <  ActiveRecord::Base      attr_accessible  :name            has_attachment    :avatar      has_attachments  :photos,  maximum:  3   end #  app/views/users/_form.html.slim   =  form_for(@user)  do  |user_form|          =  user_form.text_field(:name)          =  attachinary_file_field_tag  ‘user[avatar]',              @user,  :avatar          =  attachinary_file_field_tag  ‘user[photos]’,              @user,  :photos          =  user_form.submit("Save") #  app/controllers/users_controller.rb   ...      def  create          @user  =  User.new(params[:user])          @user.save!      end
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
    #  config/environments/production.rb   config.action_controller.asset_host  =  "d3vam04na8c92l.cloudfront.net" <%=  stylesheet_link_tag        "application",  :media  =>  "all"  %>   <%=  javascript_include_tag  "application"  %>   <%=  image_tag("rails.png")  %> <link  href="http://d3vam04na8c92l.cloudfront.net/stylesheets/application-­‐6502e5a88f02b90aeb32c2dd21ea37ab.css"  rel="stylesheet"  />
  • 87.
  • 91.
    #  Gemfile   gem  "lol_dba",  group:  :development   #  no  Terminal   bundle  install   bundle  exec  rake  db:find_indexes
  • 93.
    #  app/controllers/posts_controller.rb   def  index      @posts  =  Post.all      respond_to  do  |format|          format.html  #  index.html.erb          format.xml    {  render  :xml  =>  @posts  }      end   end
  • 94.
    #  app/controllers/posts_controller.rb   def  index      @posts  =  Post.all      respond_to  do  |format|          format.html  #  index.html.erb          format.xml    {  render  :xml  =>  @posts  }      end   end <%  @posts.each  do  |post|  %>      <tr>          <td><%=  post.name  %></td>          <td><%=  post.comments.size  %></td>          <td><%=  link_to  'Show',  post  %></td>          <td><%=  link_to  'Edit',  edit_post_path(post)  %></td>          <td><%=  link_to  'Destroy',  post,  confirm:  'Are  you  sure?',  method:  :delete  %></td>      </tr>   <%  end  %>
  • 95.
    #  app/controllers/posts_controller.rb   def  index      @posts  =  Post.all      respond_to  do  |format|          format.html  #  index.html.erb          format.xml    {  render  :xml  =>  @posts  }      end   end <%  @posts.each  do  |post|  %>      <tr>          <td><%=  post.name  %></td>          <td><%=  post.comments.size  %></td>          <td><%=  link_to  'Show',  post  %></td>          <td><%=  link_to  'Edit',  edit_post_path(post)  %></td>          <td><%=  link_to  'Destroy',  post,  confirm:  'Are  you  sure?',  method:  :delete  %></td>      </tr>   <%  end  %> Started  GET  "/posts/"  for  192.168.47.2  at  2015-­‐03-­‐23  18:31:11  +0000   Cannot  render  console  from  192.168.47.2!  Allowed  networks:  127.0.0.1,  ::1,  127.0.0.0/127.255.255.255      ActiveRecord::SchemaMigration  Load  (0.1ms)    SELECT  "schema_migrations".*  FROM  "schema_migrations"   Processing  by  PostsController#index  as  HTML      Post  Load  (0.2ms)    SELECT  "posts".*  FROM  "posts"      Comment  Load  (0.2ms)    SELECT  "comments".*  FROM  "comments"  WHERE  "comments"."post_id"  =  ?    [["post_id",  1]]      Comment  Load  (0.1ms)    SELECT  "comments".*  FROM  "comments"  WHERE  "comments"."post_id"  =  ?    [["post_id",  2]]      Rendered  posts/index.html.erb  within  layouts/application  (25.3ms)   Completed  200  OK  in  679ms  (Views:  666.4ms  |  ActiveRecord:  1.0ms)
  • 97.
    #  app/controllers/posts_controller.rb   def  index      @posts  =  Post.all      respond_to  do  |format|          format.html  #  index.html.erb          format.xml    {  render  :xml  =>  @posts  }      end   end <%  @posts.each  do  |post|  %>      <tr>          <td><%=  post.name  %></td>          <td><%=  post.comments.size  %></td>          <td><%=  link_to  'Show',  post  %></td>          <td><%=  link_to  'Edit',  edit_post_path(post)  %></td>          <td><%=  link_to  'Destroy',  post,  confirm:  'Are  you  sure?',  method:  :delete  %></td>      </tr>   <%  end  %> Started  GET  "/posts/"  for  192.168.47.2  at  2015-­‐03-­‐23  18:31:11  +0000   Cannot  render  console  from  192.168.47.2!  Allowed  networks:  127.0.0.1,  ::1,  127.0.0.0/127.255.255.255      ActiveRecord::SchemaMigration  Load  (0.1ms)    SELECT  "schema_migrations".*  FROM  "schema_migrations"   Processing  by  PostsController#index  as  HTML      Post  Load  (0.2ms)    SELECT  "posts".*  FROM  "posts"      Comment  Load  (0.2ms)    SELECT  "comments".*  FROM  "comments"  WHERE  "comments"."post_id"  =  ?    [["post_id",  1]]      Comment  Load  (0.1ms)    SELECT  "comments".*  FROM  "comments"  WHERE  "comments"."post_id"  =  ?    [["post_id",  2]]      Rendered  posts/index.html.erb  within  layouts/application  (25.3ms)   Completed  200  OK  in  679ms  (Views:  666.4ms  |  ActiveRecord:  1.0ms)
  • 98.
    Started  GET  "/posts/"  for  192.168.47.2  at  2015-­‐03-­‐23  18:37:31  +0000   Cannot  render  console  from  192.168.47.2!  Allowed  networks:  127.0.0.1,  ::1,  127.0.0.0/127.255.255.255      ActiveRecord::SchemaMigration  Load  (0.1ms)    SELECT  "schema_migrations".*  FROM  "schema_migrations"   Processing  by  PostsController#index  as  HTML      Post  Load  (0.2ms)    SELECT  "posts".*  FROM  "posts"      Comment  Load  (0.2ms)    SELECT  "comments".*  FROM  "comments"  WHERE  "comments"."post_id"  IN  (1,  2)      Rendered  posts/index.html.erb  within  layouts/application  (25.8ms)   Completed  200  OK  in  109ms  (Views:  96.4ms  |  ActiveRecord:  0.9ms) #  app/controllers/posts_controller.rb   def  index      @posts  =  Post.includes(:comments).all      respond_to  do  |format|          format.html  #  index.html.erb          format.xml    {  render  :xml  =>  @posts  }      end   end <%  @posts.each  do  |post|  %>      <tr>          <td><%=  post.name  %></td>          <td><%=  post.comments.size  %></td>          <td><%=  link_to  'Show',  post  %></td>          <td><%=  link_to  'Edit',  edit_post_path(post)  %></td>          <td><%=  link_to  'Destroy',  post,  confirm:  'Are  you  sure?',  method:  :delete  %></td>      </tr>   <%  end  %>
  • 99.
  • 104.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
    BackendBrowser 1s 200 ms Redis TarefaDemorada (Worker) 3.8 segundos
  • 116.
    class  VerySlowJob  <  ActiveJob::Base      queue_as  :default          def  perform(*args)          #  Tarefa  Demorada      end   end
  • 117.
    class  VerySlowJob  <  ActiveJob::Base      queue_as  :default          def  perform(*args)          #  Tarefa  Demorada      end   end VerySlowJob.perform_later  record
  • 118.
    class  VerySlowJob  <  ActiveJob::Base      queue_as  :default          def  perform(*args)          #  Tarefa  Demorada      end   end VerySlowJob.perform_later  record VerySlowJob.set(wait_until:   Date.tomorrow.noon).perform_later(record)
  • 119.
    class  VerySlowJob  <  ActiveJob::Base      queue_as  :default          def  perform(*args)          #  Tarefa  Demorada      end   end VerySlowJob.set(wait:  1.week).perform_later(record) VerySlowJob.perform_later  record VerySlowJob.set(wait_until:   Date.tomorrow.noon).perform_later(record)
  • 120.
    #  app/workers/report_job.rb   class  ReportJob  <  ActiveJob::Base      queue_as  :default          def  perform(email,  from_date  =  nil,  to_date  =  nil)          now  =  Time.zone.now          from_date  ||=  now  -­‐  1.year          to_date      ||=  now          file_path  =  ModelGigante.generate_excel(from_date,  to_date)          NotifyReportMailer.send(email,  file_path)      end   end
  • 121.
    #  app/controllers/reports_controller.rb   #  ...   def  create      ReportJob.perform_later(params[:email],          params[:from_date],  params[:to_date])      flash[:notice]  =  "Report  is  being  generated.          It  will  be  sent  to  your  email."      redirect_to  reports_path   end   #  ... #  app/workers/report_job.rb   class  ReportJob  <  ActiveJob::Base      queue_as  :default          def  perform(email,  from_date  =  nil,  to_date  =  nil)          now  =  Time.zone.now          from_date  ||=  now  -­‐  1.year          to_date      ||=  now          file_path  =  ModelGigante.generate_excel(from_date,  to_date)          NotifyReportMailer.send(email,  file_path)      end   end
  • 123.
  • 127.
  • 128.
    <!-­‐-­‐  chat.html  -­‐-­‐>   <script  src="//js.pusher.com/2.2/pusher.min.js"></script>   <script>   $(function()  {          var  pusher  =  new  Pusher('YOUR_APP_KEY');          var  channel  =  pusher.subscribe('my-­‐app-­‐chat');          channel.bind('new-­‐message',  function(data)  {                  $('#chat-­‐box').append(data.message);          });   })   </script>
  • 129.
    <!-­‐-­‐  chat.html  -­‐-­‐>   <script  src="//js.pusher.com/2.2/pusher.min.js"></script>   <script>   $(function()  {          var  pusher  =  new  Pusher('YOUR_APP_KEY');          var  channel  =  pusher.subscribe('my-­‐app-­‐chat');          channel.bind('new-­‐message',  function(data)  {                  $('#chat-­‐box').append(data.message);          });   })   </script> #  config/initializer/pusher.rb   Pusher.app_id  =  ENV['PUSHER_APP_ID']   Pusher.key        =  ENV['PUSHER_APP_KEY']   Pusher.secret  =  ENV['PUSHER_APP_SECRET']  
  • 130.
    <!-­‐-­‐  chat.html  -­‐-­‐>   <script  src="//js.pusher.com/2.2/pusher.min.js"></script>   <script>   $(function()  {          var  pusher  =  new  Pusher('YOUR_APP_KEY');          var  channel  =  pusher.subscribe('my-­‐app-­‐chat');          channel.bind('new-­‐message',  function(data)  {                  $('#chat-­‐box').append(data.message);          });   })   </script> #  config/initializer/pusher.rb   Pusher.app_id  =  ENV['PUSHER_APP_ID']   Pusher.key        =  ENV['PUSHER_APP_KEY']   Pusher.secret  =  ENV['PUSHER_APP_SECRET']   #  app/workers/new_message_worker.rb   require  'pusher'   class  NewMessageWorker      include  Sidekiq::Worker            def  perform(message_id)          @message  =  Message.find(message_id)          Pusher.trigger('my-­‐app-­‐chat',  'new-­‐message',  {:message  =>  @message})      end   end
  • 131.
    <!-­‐-­‐  chat.html  -­‐-­‐>   <script  src="//js.pusher.com/2.2/pusher.min.js"></script>   <script>   $(function()  {          var  pusher  =  new  Pusher('YOUR_APP_KEY');          var  channel  =  pusher.subscribe('my-­‐app-­‐chat');          channel.bind('new-­‐message',  function(data)  {                  $('#chat-­‐box').append(data.message);          });   })   </script> #  app/controllers/messages_controller.rb   #  ...   def  create      @message  =  Message.new(params[:message])      if  @messsage.save          NewMessageWorker.perform_async(@message.id)          #  ...   end   #  ... #  config/initializer/pusher.rb   Pusher.app_id  =  ENV['PUSHER_APP_ID']   Pusher.key        =  ENV['PUSHER_APP_KEY']   Pusher.secret  =  ENV['PUSHER_APP_SECRET']   #  app/workers/new_message_worker.rb   require  'pusher'   class  NewMessageWorker      include  Sidekiq::Worker            def  perform(message_id)          @message  =  Message.find(message_id)          Pusher.trigger('my-­‐app-­‐chat',  'new-­‐message',  {:message  =>  @message})      end   end
  • 132.
  • 134.
  • 135.
  • 136.
    • Mark andSweep GC (1.8+) http://www.infoq.com/news/2014/12/ruby-2.2.0-released
  • 137.
    • Mark andSweep GC (1.8+) • Bitmap Marking GC (1.9.3+) http://www.infoq.com/news/2014/12/ruby-2.2.0-released
  • 138.
    • Mark andSweep GC (1.8+) • Bitmap Marking GC (1.9.3+) • Lazy Sweep GC (2.0.0+) http://www.infoq.com/news/2014/12/ruby-2.2.0-released
  • 139.
    • Mark andSweep GC (1.8+) • Bitmap Marking GC (1.9.3+) • Lazy Sweep GC (2.0.0+) • Restricted Generational GC (RGenGC 2.1.0+) http://www.infoq.com/news/2014/12/ruby-2.2.0-released
  • 140.
    • Mark andSweep GC (1.8+) • Bitmap Marking GC (1.9.3+) • Lazy Sweep GC (2.0.0+) • Restricted Generational GC (RGenGC 2.1.0+) • Restricted Incremental GC (RIncGC 2.2.0+) http://www.infoq.com/news/2014/12/ruby-2.2.0-released
  • 143.
  • 145.
    #  Gemfile   source  'https://rubygems.org'   ruby  "2.2.1"   #  performance  tuning   gem  'escape_utils'   gem  'fast_blank'   gem  'oj'   gem  'oj_mimic_json' http://marianposaceanu.com/articles/improve-rails-performance-by-adding-a-few-gems
  • 147.
    #  Gemfile   source  'https://rubygems.org'   if  ENV['RAILS_ENV']  ==  'production'      ruby  '1.9.3',  :engine  =>  'jruby',  :engine_version  =>  '1.7.18'   else      ruby  '2.2.1'   end   gem  'rails',  '4.2.0'   gem  'rails-­‐api'   gem  'srt'   gem  'pg',  platforms:  'mri'   gem  'activerecord-­‐jdbcpostgresql-­‐adapter',  platforms:  'jruby'   gem  'sidekiq'   gem  'dalli'   gem  'puma'   gem  'rack-­‐cache'   gem  'rack-­‐cors',  require:  'rack/cors'   gem  'rails_12factor'   gem  'newrelic_rpm'
  • 148.
    #  Gemfile   source  'https://rubygems.org'   if  ENV['RAILS_ENV']  ==  'production'      ruby  '1.9.3',  :engine  =>  'jruby',  :engine_version  =>  '1.7.18'   else      ruby  '2.2.1'   end   gem  'rails',  '4.2.0'   gem  'rails-­‐api'   gem  'srt'   gem  'pg',  platforms:  'mri'   gem  'activerecord-­‐jdbcpostgresql-­‐adapter',  platforms:  'jruby'   gem  'sidekiq'   gem  'dalli'   gem  'puma'   gem  'rack-­‐cache'   gem  'rack-­‐cors',  require:  'rack/cors'   gem  'rails_12factor'   gem  'newrelic_rpm'
  • 149.
    #  Gemfile   source  'https://rubygems.org'   if  ENV['RAILS_ENV']  ==  'production'      ruby  '1.9.3',  :engine  =>  'jruby',  :engine_version  =>  '1.7.18'   else      ruby  '2.2.1'   end   gem  'rails',  '4.2.0'   gem  'rails-­‐api'   gem  'srt'   gem  'pg',  platforms:  'mri'   gem  'activerecord-­‐jdbcpostgresql-­‐adapter',  platforms:  'jruby'   gem  'sidekiq'   gem  'dalli'   gem  'puma'   gem  'rack-­‐cache'   gem  'rack-­‐cors',  require:  'rack/cors'   gem  'rails_12factor'   gem  'newrelic_rpm'
  • 150.
    #  Gemfile   source  'https://rubygems.org'   if  ENV['RAILS_ENV']  ==  'production'      ruby  '1.9.3',  :engine  =>  'jruby',  :engine_version  =>  '1.7.18'   else      ruby  '2.2.1'   end   gem  'rails',  '4.2.0'   gem  'rails-­‐api'   gem  'srt'   gem  'pg',  platforms:  'mri'   gem  'activerecord-­‐jdbcpostgresql-­‐adapter',  platforms:  'jruby'   gem  'sidekiq'   gem  'dalli'   gem  'puma'   gem  'rack-­‐cache'   gem  'rack-­‐cors',  require:  'rack/cors'   gem  'rails_12factor'   gem  'newrelic_rpm'
  • 151.
  • 153.
  • 154.
  • 155.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary
  • 156.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL
  • 157.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic
  • 158.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic Async Jobs Sidekiq, Redis
  • 159.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic Async Jobs Sidekiq, Redis Async Messages pusher.com
  • 160.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic Async Jobs Sidekiq, Redis Async Messages pusher.com Auto scale HireFire,AdeptScale
  • 161.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic Async Jobs Sidekiq, Redis Async Messages pusher.com Auto scale HireFire,AdeptScale Rubies Ruby 2.2.1, JRuby
  • 162.
    Monitoring New Relic CachingMemcache, ETAG Assets CloudFlare, Cloudinary Database PostgreSQL Search Elastic Async Jobs Sidekiq, Redis Async Messages pusher.com Auto scale HireFire,AdeptScale Rubies Ruby 2.2.1, JRuby Deployment Heroku
  • 164.