Paluy on Miscellaneous

Of course I'm an optimist - I don't see much point in being anything else. - Winston Churchill

Process Information

I found it very useful to see my application process info on an admin page, as following:

You can do it inside your Rails app.

Inside your controller/admin_controller.rb:

1
2
3
4
5
6
def index
  @processes = `ps -g #{Process.getsid($$)} -o pid,wchan,%cpu,%mem,rss,vsz,command`.strip.split("\n")
  @proc_headers = @processes.shift.split(/\s+/)
  @processes.map! { |line| line.strip.split(/\s+/, @proc_headers.size) }.
      reject! { |line| line.last.start_with?('ps -g') }
end

and add the following to your view/admin/index.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<table>
  <thead>
    <tr>
    <% @proc_headers.each do |th| %>
      <th><%= th %></th>
    <% end %>
    </tr>
  </thead>
  <tbody>
    <% @processes.each do |line| %>
      <tr>
      <% line.each do |td| %>
        <td><%= td %></td>
      <% end %>
      </tr>
    <% end %>
  </tbody>
</table>

I would like to thank @romanbsd for this source.

Protect Email From Spam Bots

TL;DR

Spam bots are getting a lot smarter at harvesting email addresses from web pages. Use encode option while using mail_to

Ways to protect your email: me@domain.com

  • Weak solution - substitute characters: me_at_domain_dot_com
  • Weak solution - HEX encoding: %6d%65@%64%6f%6d%61%69%6e.%63%6f%6d
  • Better solution - Javascript encoding:
1
2
3
4
5
mail_to "me@domain.com", "Contact us", :encode => "javascript"
# => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
#
# The JavaScript evaluates the following:
# document.write('<a href=\"mailto:me@domain.com\">Contact us<\/a>');

Check more details here.

Another way to encode your mail_to with JavaScript

Email obfuscator script 2.1 by Tim Williams

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script type="text/javascript" language="javascript">
<!--
// Email obfuscator script 2.1 by Tim Williams, University of Arizona
// Random encryption key feature by Andrew Moulden, Site Engineering Ltd
// This code is freeware provided these four comment lines remain intact
// A wizard to generate this code is at http://www.jottings.com/obfuscator/
{
  coded = "B6@LSBeJv.ASB"
  key = "78ncPmS9z0hseXrvAKBlgi1OC6tjYU2qdIJw5MVfQyb3ZLH4ExRWNTpoFkuGDa"
  shift=coded.length
  link=""
  for (i=0; i<coded.length; i++) {
    if (key.indexOf(coded.charAt(i))==-1) {
      ltr = coded.charAt(i)
      link += (ltr)
    }
    else {
      ltr = (key.indexOf(coded.charAt(i))-shift+key.length) % key.length
      link += (key.charAt(ltr))
    }
  }
  document.write("<a href='mailto:"+link+"'>Contact us</a>")
}
//-->
</script><noscript>Sorry, you need Javascript on to email me.</noscript>

Important! You can’t protect your email 100%

If the bot evaluates JavaScript before harvesting email addresses, you can’t resist it.

For example, it can be done using PhantomJS

Caution - To_time in Rails

TL;DR

Attention on time zone while using today.to_time in your Rails application!

Ruby to_time in your Rails application

Suppose, you need to convert some date to datetime. You can use the Ruby to_time method:

1
2
3
4
5
6
7
date = Date.new(2013, 04, 15)

date.to_time            # 2013-04-15 00:00:00 +0300
date.to_time(:local)    # 2013-04-15 00:00:00 +0300
# The Time Zone depends on your local machine

date.to_time(:utc)      # 2013-04-15 00:00:00 +0000

If your server time zone is UTC, but Rails application has different time zone, for example:

1
config.time_zone = 'Central Time (US & Canada)'

converting from date to time will ignore Rails settings!

It happens, when you need to output your date in epoch format (for example: in json).

I suggest to convert your date to utc and than to epoch.

1
epoch = date.to_time(:utc).to_i

Then, apply local time zone using JavaScript::

1
var date = new Date(epoch);

Profiling Rails Applications

Your finaly got clients and everything works fine.

But users expect several performance issues.

How can I profile my Ruby on Rails application?

Available Tools

GC::Profiler

The GC profiler provides access to information on GC runs including time, length and object space size.

1
2
3
4
5
6
7
8
GC.start # clear GC before profiling
GC::Profiler.enable

# your code

GC.start
puts GC::Profiler.report
GC::Profiler.disable

Ruby Benchmark

Ruby has core module to Benchmark your code. It helps you to mesure running time of your code.

1
2
3
4
5
6
7
8
9
10
11
12
13
require 'benchmark'

Benchmark.bm do|b|
  b.report("+= ") do
    a = ""
    500_000.times { a += "." }
  end

  b.report("<< ") do
    a = ""
    500_000.times { a << "." }
  end
end

I suggest you to add benchmark_suite gem. There is a set of enhancements to the standard library benchmark.rb

ObjectSpace

ObjectSpace class already described in previous post. You should also check Visualizing memory leaks in Ruby using ObjectSpace methods.

RUBY-PROF

How to use in Rails:

  • Add the ruby-prof to your gemfile
1
2
3
group :profile do
  gem 'ruby-prof'
end

Note: verify your /config/environments/profile.rb has the following settings:

1
2
config.cache_classes = true
config.cache_template_loading = true
  • Add rack adapter to config.ru:
1
2
3
if Rails.env.profile?
  use Rack::RubyProf, :path => '/temp/profile'
end

Note: By default the rack adapter will generate a html call graph report and flat text report. You can add printer option as following:

1
use Rack::RubyProf, :path => '/temp/profile', :printers => {RubyProf::GraphHtmlPrinter => 'graph.html'} # or any other printer as described

More printers options can be found here

  • Add Profile to your code:
1
2
3
4
5
6
7
8
9
# define mesurment type
# as described: https://github.com/ruby-prof/ruby-prof#measurements
RubyProf.measure_mode = RubyProf::PROCESS_TIME

RubyProf.start

# code to profile

result = RubyProf.stop

Perftools.rb

While ruby-prof is a good tool, I, personally, prefere to use perftools.rb. It also has a rack gem.

The main advantage of perftools: it samples your process using setitimer() so it can be used in production with minimal overhead.

How to use in Rails:

  • Add the rack-perftools_profiler to your gemfile
1
gem 'rack-perftools_profiler', :require => 'rack/perftools_profiler'
  • Add middleware to config/environment.rb:
1
config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'pdf', :bundler => true

See more options here.

  • Profile your application
1
2
3
curl http://localhost:3000/__start__
curl http://localhost:3000/__stop__
curl http://localhost:3000/__data__

Visualization of your report

  • ruby-prof and perftools.rb has support for different output formats. One of them is KCacheGrind
  • In Ruby 2 you can gather your stack traces. Read more here

Summary

  • Remember: More Objects => Longer GC => Slow application
  • Always examine your HEAP
  • Use Tools!

Debugging Rails Application - Part 2

In previous post, I described which tools I use for debugging Rails (Ruby) applications.

In this post I will show you how to debug your Garbage Collector.

Why should I debug my GC?

First, you should understand how ruby allocates memory for new objects. What happened inside your OS, each time you write book = Book.new?

How Ruby Memory Works

RVM defines Process Heap for each Ruby process. Than, it creates first Ruby Heap. Each Ruby Heap contains allocated objects. There is a “Free List” array inside the heap.

Ruby Heap - Free List

While new object allocated, the RVM looks for an empty place in “Free List” and allocates it for a newly creted one. If the “Free List” is empty (all slots are allocated), RVM calls to Garbage Collector (GC).

The Garbage Collector finds non-reachable objects and remove them. If “Free List” is still empty, another Heap allocated.

Conclusion: The more objects we have, the longer GC process. And it’s a cause for slow application.

How should I debug my GC?

I suggest to use pry-debugger.

There are several tools we can use while debugging:

  • ObjectSpace class
  • Heap dump (via GC patch)

But to get it all functionality you need to use patched version of Ruby.

To enable heap dump support, pass the –enable-gcdebug option to the rvm install command:

1
rvm install 1.9.3 -C --enable-gcdebug -n gcdebug

ObjectSpace class

An example of method I used to get a better output:

1
2
3
4
5
6
7
8
9
10
11
12
13
def get_object_stats
  return_value = Hash.new
  ObjectSpace::each_object(Object) {|my_object|
    unless return_value[my_object.class.to_s.downcase].nil?
      return_value[my_object.class.to_s.downcase][:count] += 1
    else
      return_value[my_object.class.to_s.downcase] = Hash.new
      return_value[my_object.class.to_s.downcase][:name] = my_object.class
      return_value[my_object.class.to_s.downcase][:count] = 1
    end
  }
  return_value.sort_by {|k,v| -v[:count]}
end

Source

Heap dump

1
2
3
4
5
6
7
8
GC.enable_stats if defined?(GC) && GC.respond_to?(:enable_stats)
GC.start # clean up heap
# ...
# YOUR CODE GOES HERE
# ...
# dump it, including class names of heap objects
GC.dump_file_and_line_info(filename: "heap.dump", include_class_names: true)
stat_string = "allocated: #{GC.allocated_size/1024}K total in #{GC.num_allocations} allocations, GC calls: #{GC.collections}, GC time: #{GC.time / 1000} msec"

Other interesting solutions to check:

Note: the memprof works only with Ruby 1.8.x

My slides at RubyUnderground

Debugging Rails Application - Part 1

How to debug Rails application?

There are several gems to debug your Ruby code:

debugger - A fork of ruby-debug that works on Ruby 1.9.3

Install (rvm):

1
gem install debugger -- --with-ruby-include=$rvm_path/src/ruby-1.9.3-p374/
Gemfile
1
2
3
  group :development, :test do
    gem 'debugger'
  end

Usage:

1
2
  # Wherever you need a debugger, simply add:
  require 'debugger'; debugger

pry-debugger - Using debugger within pry

Gemfile
1
2
3
  group :development, :test do
    gem 'pry-debugger'
  end

Configuration (Optional):

Add aliases:

$HOME/.pryrc
1
2
3
4
  Pry.commands.alias_command 'con', 'continue'
  Pry.commands.alias_command 's', 'step'
  Pry.commands.alias_command 'n', 'next'
  Pry.commands.alias_command 'fin', 'finish'

NOTE: You won’t be able to use those words as params while debugging

Usage:

1
2
  # Wherever you need a debugger, simply add:
  binding.pry

Example:

home_controller.rb
1
2
3
4
5
6
7
8
9
  class HomeController < ApplicationController
    def index
      # Some code ...

      binding.pry
      # Code to debug
      # ...
    end
  end

Now start your rails server (rails s) and visit your controller url: http://localhost:3000/home. The pry debugger console will appear in your terminal. You can use all pry functionality inside your application in addition to predefined aliases(next, step, continue, etc.).

Other useful commands:

  • help - show a list of available commands
  • exit - exit a context with a user provided value
  • whereami - describe the current location (alternative: $)
  • break - set a breakpoint
  • wtf -v - see the entire backtrace

Improve Rails Bootup Time

Guide: How to improve your Rails Bootup Time

Benchmarks:

I used ruby-1.9.3-p327 with and without patch.

The test is:
1
2
echo puts Time.now > test_script
  time rails runner test_script
The results are:
  • ruby-1.9.3-p327: rails runner test_script 16.01s user 0.61s system 99% cpu 16.725 total
  • ruby-1.9.3-p327-fast_load: rails runner test_script 6.11s user 0.55s system 98% cpu 6.732 total

How to apply:

Download fast_load.patch

Than, install the patch:

1
rvm install 1.9.3 --patch fast_load.patch -n fast_load

Credentials

Thanks to @romanbsd for sending me this patch

Faster Hacking!!!

Update

I found the origin of this patch. It’s here and here It’s a part of falcon patch but only for fast require

Optimize Your RVM Ruby

TL;DR

If you are using rvm with ruby-1.9.3 patch < 327, reinstall it with cflags.patch.

I would like to share my experience and summarize this issue.

There are several posts, regarding Ruby compiling optimization while using RVM, like:

First you should understand what are Optimization Options.

O3 : turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone options.

I followed the RVM issue discussion at Github. It suggested to apply CFLAG.patch for all Ruby versions till ruby-1.9.3-p327.

NOTE: 1.9.3-p327 does not require it, rvm uses a new feature with allowing multiple paths in –with-opt-dir= - so no CFLAGS / LDFLAGS are used.

How to apply:

1
rvm reinstall 1.9.3 --patch cflags.patch

Is it really improves ruby benchmark? Yes.

See my results (benchmark.rb):

ruby-1.9.3-p286 without patch

1
2
3
4
./benchmark
       user     system      total        real
+=  46.020000   2.610000  48.630000 ( 48.827064)
<<   0.220000   0.000000   0.220000 (  0.224581)

ruby-1.9.3-p286 with patch

1
2
3
4
./benchmark
       user     system      total        real
+=  42.830000   2.480000  45.310000 ( 45.521011)
<<   0.100000   0.000000   0.100000 (  0.099425)

Is it good for production?

We use this patch in production with ruby-1.9.3-p286 for more than a month. Everything is stable, and we see significant improve in our application.

Summary

If you are using rvm with ruby-1.9.3 patch < 327, reinstall it with cflags.patch.

REST Debug Tips

If you need to debug Net protocol between your application and some other API, you can output all request, this way:

class Net::HTTP
  class << self
    def new_with_debug(*args)
      new_without_debug(*args).tap { |http| http.set_debug_output(STDERR) }
    end
    alias_method_chain :new, :debug
  end
end

Another “Monkey patch” for RestClient so it will always provide authentication:

require 'restclient'
module RestClient
  class Request
    def execute_with_auth(&block)
      @user ||= 'someuser'
      @pass ||= 'password'
      execute_without_auth(&block)
    end
    alias_method_chain :execute, :auth
  end
end

Thanks to @romanbsd for providing those tips.

Passwordless Login

I noticed that some developers didn’t know about Passwordless Login

On your local computer, you must have an RSA public/private keys. Otherwise generate it:

ssh-keygen -t rsa

One row solution is here:

cat .ssh/id_rsa.pub | ssh user@example.com 'cat >> .ssh/authorized_keys'

Otherwise step-by-step explanation:

Copy your public key to your example.com

scp ~/.ssh/id_rsa.pub user@example.com:~/

SSH to your server

ssh user@example.com

Now, append the public key to your authorized keys file and delete the file you uploaded:

mkdir .ssh
cat id_rsa.pub >> .ssh/authorized_keys
rm id_rsa.pub

Make sure your permissions set properly:

chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys