Wednesday, May 6, 2020

Errno::ENOSPC: No space left on device

Recently one of our EC2 instance was down due to insufficient disk space. To make it up immediately we had increased its disk space dynamically using EC2 console.

After debugging we found that there was an unexpected spike in rails logs ( some custom logs also ) and logrotate failed to rotate them due to insufficient memory.


After increasing memory, we still, getting ‘Errno::ENOSPC: No space left on device’ in some Sidekiq jobs. Every though more than 50%  disk space was available.





After googling, got that might be running out of inodes, but it seems that also not an issue.





But in the above output found a strength file system for /tmp called 'overflow'. So googled out on that term and got the exact reason for an issue.


Here is that reason,


When the system boots and the hard drive is full, nothing can write to /tmp. So during init, a tmpfs is created and mounted. This way your system can safely boot because it can write to /tmp.


So started looking on, how to reset back tmp, found many solutions but those seem to be risky, but out of that found nice solution provided by Jarrod’s blog.


He just asked to unmount an overflow partition so that /tmp will go back to the normal pointing.


sudo umount overflow

But after running this command was getting an error,

umount: /tmp: device is busy.

              (In some cases useful info about processes that use
               the device is found by lsof(8) or fuser(1))

Again his blog helped. Found comment in his blog for exactly the above issue.


sudo umount -l /tmp

And finally /tmp got mounted back to normal.

Friday, May 6, 2016

How to test model validations with validation type

While reading a code or while doing code review, I have see many times people write a test cases for model validation like this,
Before
# app/models/post.rb
class Post < ActiveRecord::Base
  validates :title, presence: true
end
# test/models/post_test.rb
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert post.errors.has_key(:title)
  # or some times
  assert_equal ["can't be blank"], post.errors.messages[:title]
end
And if same attribute has more than one validation then,
# app/models/post.rb
class Post < ActiveRecord::Base
  validates :title, presence: true, length: { in: 10..60 }
end
Most of the people do,
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert_equal ["can't be blank", "is too short (minimum is 10 characters)"], post.errors.messages[:title]
end
I think instead we can use symbol for it. Rails internally uses I18n locale support for error messages. For more details have a quick look on reference links provided below.

Means we can take a benifits from it. So improved test case code is,
After
test "should have the necessary required validators" do
  post = Post.new
  assert_not post.valid?
  
  assert post.errors.added? :title, :blank
  assert post.errors.added? :title, :too_short, { count: 20 }
end
While for custom validate method, you can use same approch by defining new key. Key can be any valid symbol.
Before
class Post < ActiveRecord::Base
  has_many :tags

  validates :title, presence: true
  validate :validate_tags_count
  
  private
    def validate_tags_count
      errors.add(:tags, "required alteast 2 tags") if tags.reject(&:marked_for_destruction?).count < 2
    end
end
After
class Post < ActiveRecord::Base
  has_many :tags

  validates :title, presence: true
  validate :validate_tags_count
  
  private
    def validate_tags_count
      errors.add(:tags, :required, count: 2) if tags.reject(&:marked_for_destruction?).count < 2
    end
end
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert post.errors.added? :tags, :required, { count: 2 }
end
#config/locales/en.yml
# Globally for all models
en:
  errors:
    messages: 
      required: "minimum %{count} tags required" 

or
 
#config/locales/en.yml
# only to post models
en:
  activerecord:
    errors:
      models:
        post:
          attributes:
            tags:
              required: "minimum %{count} tags required"
Same concept will work on rails 5 as well. Tested with rails 5.0.0.beta4.

References :
http://api.rubyonrails.org/classes/ActiveModel/Errors.html
activemodel/lib/active_model/validations/presence.rb
activemodel/lib/active_model/locale/en.yml

Monday, January 5, 2015

Ruby 2.2.0 installation using rbenv failing

On my ubuntu 14.04 development machine, ruby 2.2.0 installation using rbenv was failing with following error
➜  ~  rbenv install 2.2.0
Downloading ruby-2.2.0.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/7671e394abfb5d262fbcd3b27a71bf78737c7e9347fa21c39e58b0bb9c4840fc
Installing ruby-2.2.0...

BUILD FAILED (Ubuntu 14.04 using ruby-build 20141225-7-g4aeeac2)

Inspect or clean up the working tree at /tmp/ruby-build.20150105132503.8460
Results logged to /tmp/ruby-build.20150105132503.8460.log

Last 10 log lines:
make[1]: *** [ext/fiddle/all] Error 2
make[1]: *** Waiting for unfinished jobs....
installing default nkf libraries
installing default date_core libraries
linking shared-object date_core.so
make[2]: Leaving directory `/tmp/ruby-build.20150105132503.8460/ruby-2.2.0/ext/date'
linking shared-object nkf.so
make[2]: Leaving directory `/tmp/ruby-build.20150105132503.8460/ruby-2.2.0/ext/nkf'
make[1]: Leaving directory `/tmp/ruby-build.20150105132503.8460/ruby-2.2.0'
make: *** [build-ext] Error 2
I have resolved this issue by installing following dependencies
sudo apt-get install autoconf bison libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3 libgdbm-dev
References:
https://github.com/sstephenson/ruby-build/wiki

Sunday, May 25, 2014

How to: Override devise default routes for sign in and sign out

In one of my application, I need to override default routes provided by Devise for better SEO.

SEO expert asked me to change default url in such way,
"/users/sign_in" => "/sign-in"
"/users/sign_up" => "/sign-up"

And I know, it is possible in devise. Devise is awesome authentication gem which provides lots of customization as per our app needs. In fact, every gem publisher should study the devise code for better understanding on how to provide api's for customization.

So here is the full customization of default routes,

redirect section : 
        On line 5 and 6, I wrote a redirection rule for SEO purpose and for demonstration over here. But best place is to place redirection rules in your web server, that is in nginx or apache.
        This only applies to you, if your app is already live and search engine has already indexed your sign in and sign out routes.

skip section :
        On line 10, I have skipped generation of default routes for session(sign_in) and registration(sign_up).

devise_scope block :
        In devise_scope block, we need to provide our own urls for sign_in and sign_out. So we need to provide url format with controller and action.
        Also, for each urls, we need to provide path helper using ":as", so that we can still use old path name, without code change.

Tuesday, May 6, 2014

Upgrading from Rails 4.0 to rails 4.1 - My experience

TL;DR: I have recently upgraded one of my rails app from rails version 4.0 to 4.1 and collected steps over here.

Before starting upgrade I went through following link and it is definitely worth to read those links,
1. Rails 4.1 release notes
2. Rails 4.1 upgrade steps

These link almost covered every thing which we need to for rails 4.1 upgrade. Thanks to rails team.

While upgrading you have to note that, your app might be using some gems which is not yet rails-4.1 compatible or might be it's dependent gem.
In my case it was, "sidekiq-failures" which was not compatible with sidekiq-3.0.0 and sidekiq-3.0.0 was rails-4.1 compatible. So I have disabled "sidekiq-failures" for now. ( Author of gem is working on it and might be he has fixed it be the now ).

Here are my steps for rails upgrade.
1. bundle update :
        Bundle update is our routine process, but still I have started with it. So that my stack is ready with all latest gems. You can use "bundle outdated" to check which of your gems are outdated and how much. If major update is happening with any gem then you must have to look into its release notes.

2. Rails 4.1 update and bundle update again :
        Then I have updated rails version first and then bundle update again. Along with this update I have referred rails upgrade steps guide and followed steps which was applicable to my apps.

Out of that important once are,
1. Changes to config/secrets.yml
2. Changes to test helper
3. Addition of spring gem

These are important changes and you have to do it before proceeding.

3. Spec run and code fixes : 
        Running test cases over here is very much important to quickly identify issue with code.
Here are some of failing cases,
a. has_many with through relation :
one of my relation was failing with wrong query
relation was post has_many badges
has_many :badges, -> { order "badgings.id DESC" }, through: :badgings
and failing query is,
"SELECT "badges".* FROM "badges" ORDER BY badgings.id DESC"
So here I was ordering badges using through relation, which is indeed wrong, but I was surprised why it was working previously and not after upgrade. I tried to find cause but not able to find it.
Please comment here, if you know the cause.

For fixing I have changed above line to,
has_many :badges, -> { order "badges.id DESC" }, through: :badgings
b. Complex count quires :
The Major changes, I did with count queries in all pagination. I have changed
@tags.all.count
to
@tags.to_a.count
Please see https://github.com/rails/rails/pull/10710

4. Removed mail_view gem ( If you are using ): 
        Now mail_view gem is part of rails 4.1. As, author of mail_view gem has integrated his gem into rails so there is very less change need to do.
- remove mail_view routes mounting from routes 
- and search and replace MailView to ActionMailer::Preview

Apart from these steps, I have followed following steps which are only applicable if your are using sidekiq-3 or acts-as-taggable-on-3.1
1. For sidekiq :
If you are using Sidekiq for back-grounding and Capistrano for deployment, then your Capistrano script will fail if you have upgraded to Sidekiq-3. Since in Sidekiq-3, Capistrano integrated support has been removed.
To make Capistrano script to work, we need to use "capistrano-sidekiq" gem.
- Just add "capistrano-sidekiq" gem to Gemfile
- And replace "sidekiq/capistrano" to "capistrano/sidekiq" in Capfile.

2. For acts-as-taggable-on :
If you have upgraded acts-as-taggable-on-3.1.1 then you need to run the following generator,
rake acts_as_taggable_on_engine:install:migrations
Since acts-as-taggable-on has added some new columns in tagging system.

And done. Now my app is working on rails 4.1 very smoothly. 


Monday, October 14, 2013

Testing Responsive web design with Rspec

Recently I am working on an application where we have used zurb foundation for responsive web design. We have separated all devices screen broadly into three categories as follows,

1. small : Screen width upto 590.
2. medium : Screen width upto 1025.
3. large : Screen width uptp 1280.

Very soon we will add new category,
4. xlarge : above 1280+ ;)

Now all things are fine, but until you didn't write acceptance test, you can't make sure that your design is working fine for all screen.

Before taking responsive web design into consideration, we have already written feature specs using rspec + capybara (selenium).

So here we want handy configuration which will not affect existing feature spec and should treat existing specs meant to be written for large screen.

So here is, how I have added configuration for my rspec suite to target testing responsive design.


With above configuration, if you write any feature spec without :device_size then it will run that spec against 'large' screen. And if you want to write a spec for 'small' and 'medium' devices you can write by using metadata 'device_size => :small' or 'device_size => :medium'
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      # spec with default screen size i.e large
    end 

    scenario "abc", :js => true, :device_size => :small do
      # spec with small screen size
    end 
  end
With the help of 'config.include ScreenSize, :type => :feature' you can directly change screen size into example.
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      set_screen_size(:medium)
      # spec with default screen size i.e large
    end 
  end
If you want to some special configuration based on device size you can do that using,
  config.before(:each, :device_size => :small) do
    # special configuration .........
  end
TODO :
I want to split a test suite into feature/small/*.rb for small devices, feature/large/*.rb for large devices and so on.. and apply specific metadata configuration based on type of suite.

References :
http://www.blaulabs.de/2011/11/22/acceptance-testing-with-responsive-layouts

Sunday, September 22, 2013

Capybara wait for ajax call to finish

TL;DR: Before capybara 2.0.0, wait_until method was available which can be used for wait for ajax call to finish. In capybara 2.0.0, wait_until method is removed as it is not needed and was creating confusion. Capybara automatically waits for elements to appear or disappear on the page. If you do something like find("#foo"), this will block until an element with id “foo” appears on the page, as will has_content?("bar"), click_link("baz") and most other things you can do with Capybara.

Since today morning I was facing very strange issue with capybara feature spec. One of my spec was failing with following error.
spec:
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_text("This is my comment")
      page.should have_link('Reply')
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end

       expected to find text "This is my comment" in "BROWSE CATEGORIES FORMATS MY PREGNANCY VOICES SERVICES POST TAGS MyText About Author1 — A brief write up about the author will come here. This will not exceed more than 250 characters. Give us your opinion. Discuss it with people like you. Javier Huel - This is my comment 0 likes | 0 replies LIKE | Reply | Edit | Destroy SIMILAR POSTS about us | our team | our vision | give feedback | privacy policy | terms of use | best viewed in | careers | site map © 1998–2013 ABC, Inc. All rights reserved."
     # ./spec/features/commets_spec.rb:42:in `block (3 levels) in ' 

Now in above error, I am clearly able to see that the text is present in page, but still it is not able to match it.

To quickly understand what is happening, I have used pry. On pry prompt, just verified expectation again and this time it is evaluated true.
[1] pry(#)> page.should have_text("This is my comment")
=> true 

Means, here capybara was not waiting for ajax call to finish.

So I have googled for "capybara + wait for ajax call to finish" and found solution to use "wait_until" method.

When I have tried this method, I got into another error,
Failure/Error: wait_until do
     NoMethodError:
       undefined method `wait_until' for #
     # ./spec/features/commets_spec.rb:41:in `block (3 levels) in '
While googled more, I found strong discussion of Jonas Nicklas and found that wait_until is removed from capybara 2.0.0 ;(.

Alternatively, I found how to implement "wait_until" back into capybara.

But in same discussion Jonas Nicklas has posted a link of his blog post where he has explained about why this decision was reached. Also he has mentioned how we can wait for ajax call to modify DOM.

So, my spec changed to,
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_selector(".comment", :text => "This is my comment")
      page.should have_link('Reply')                         # Subsequent call also passed due to have_selector
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end
 
and which is passing. ;)

References :
https://groups.google.com/forum/#!topic/ruby-capybara/qQYWpQb9FzY
http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara
https://gist.github.com/KevinTriplett/5087744

Monday, May 6, 2013

Ubuntu 13.04 + XPS Dell + sound is not working

Recently I have upgraded my Ubuntu OS from 12.10 to 13.04 on my Dell XPS.

But suddenly I faced issue with my sound. I am not able to get sound form my machine.

After googling, I found solution for Dell Vostro, but after some time I found for dell xps too. I tried solution on my Dell box and my sound is back.

Here is the solution,
sudo gedit /etc/modprobe.d/alsa-base.conf
Add following line at the bottom of a file
options snd-hda-intel model=dell-bios
But don't forget to reboot your machine.
References :
http://askubuntu.com/questions/288503/vostro-3560-sound-is-not-working-after-upgrade-to-ubuntu-13-04
http://ubuntuforums.org/showthread.php?t=1457676

Saturday, September 22, 2012

Rails 4 - Transaction isolation level


After long time I am writing small post, as now a days not able manage a time or you can say I am just working ;)

TL;DR - In Rails 4, Jon Leighton has added a support for specifying transaction isolation level. This transaction level we can pass when starting transaction(or you can say while calling transaction method)

Note : This blog post is copied from Jon Leighton commits, which he did in a rails repository

Before continuing reading this post, it will be good if you read about Transaction Isolation over here.

Isolation is a property that defines how/when the changes made by one operation become visible to other concurrent operations. Isolation is one of the ACID (Atomicity, Consistency, Isolation, Durability) properties.

Highlevel of Isolation does affect on Concurrency property of ACID, as it require to use locks or multiversion concurrency control.

Anyways here is a documentation which Jon Leighton has added in rails trank.

If your database supports setting the isolation level for a transaction, you can set it like so:

Post.transaction(isolation: :serializable) do
  # ...
end

Valid isolation levels are:
* `:read_uncommitted`
* `:read_committed`
* `:repeatable_read`
* `:serializable`

You should consult the documentation for your database to understand the semantics of these different levels:

* http://www.postgresql.org/docs/9.1/static/transaction-iso.html
* https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html

An `ActiveRecord::TransactionIsolationError` will be raised if:
* The adapter does not support setting the isolation level
* You are joining an existing open transaction
* You are creating a nested (savepoint) transaction

The mysql, mysql2 and postgresql adapters support setting the transaction isolation level. However, support is disabled for mysql versions below 5, because they are affected by a bug (http://bugs.mysql.com/bug.php?id=39170) which means the isolation level gets persisted outside the transaction.
References :
http://en.wikipedia.org/wiki/Isolation_(database_systems)
http://www.postgresql.org/docs/9.1/static/transaction-iso.html
https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
https://github.com/rails/rails/commit/392eeecc11a291e406db927a18b75f41b2658253

Sunday, July 1, 2012

bundle install +
tar_input.rb:49:in `initialize': not in gzip format (Zlib::GzipFile::Error)

This is one of the pending post since 6 month, thought it might helpful to some people.

Today on production server our system team found very strange issue with REE and bundler which lead production server was down for 20 min. This issue did not occur on test (Since on test and production have different version of REE)

Issue is as follows,


Issue was new to me, so I started digging into it using the almighty google.

The solution which I found on net was not applicable since we couldn't afford a downtime.

Issue is actually with ruby-enterprise-1.8.7-2010.02. Which is fixed in ruby-enterprise-1.8.7-2011.01 as described in release note.

http://blog.phusion.nl/2011/02/12/ruby-enterprise-edition-1-8-7-2011-01-released/

But we don't have a time to upgrade REE at this point. So I digged into it and found temporary solution described here.

http://stackoverflow.com/questions/2494659/strange-bundler-error-tar-input-rb49in-initialize-not-in-gzip-format-zlib


Basically you have to clear you gem cache either you are use rvm or not. so I have simply deleted content from my gems folder
like,
sudo rm -rf /opt/ruby-enterprise-1.8.7-2010.02/lib/ruby/gems/1.8/cache/

And this worked !!!....

Sunday, February 12, 2012

Configure Repository In Redmine

As Rubyist, most of us we use Redmine for Project Management and git for source code management. Written using the Ruby on Rails framework, it is cross-platform and cross-database. Redmine is open source and released under the terms of the GNU General Public License v2 (GPL).

Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

Redmine provides many features out of that is SCM integration (SVN, CVS, Git, Mercurial, Bazaar and Darcs), which I going to explaing you in this blog post.

I am explaning this because, many time we do a code review for team members. Also some time we need to review a perticuler fix done for any bug. At that time it is very difficult to go through a git log.

So it will be great to use to redmine for this, were we can attach a code to feature or bug and even from git log to issue.

Redmine has this great feature, http://www.redmine.org/projects/redmine/wiki/RedmineRepositories#Repository-user-mapping

Now if we have already git server then, then their are two solution available,

  1. Make git server to support http protocol
  2. Replicate git server on Redmine server it self

both above option are very easy but issue in second option. Issue if you have git server which is runnning separately. Which happens in my case.

Our git server is only accessisble via git protocol like

git clone user_name@repository_server:path_to_project.git

Our git server is used by more than 30 developers were more than 13 repositories get accessed. So I don't want to distrub the development process. So I have decided to replicate git server on redmine.

A. Replicate git server to be used by Redmine

  1. Login to your Redmine server from console
  2. Clone the repository with following command
  # change to your respective user 
 $ su - name_of_user   
  
  # Create a folder to hold your repository
 $ mkdir /any/path/to/create/repo
 
  # change directory
 $ cd /any/path/to/create/repo
 
 # Make a bare clone of the repo
 $ git clone --bare ssh://git@reposerver/usr/local/git_root/foo-project.git
 
  # Switch to repository
 $ cd foo-project.git
 
  # add orignal repository in newly created repository for fetching changes done in orignal repository 
 $ git remote add origin ssh://git@reposerver/usr/local/git_root/foo-project.git
 
  # Above command will feches commit from origanl repo
 $ git fetch origin
 $ git reset --soft refs/remotes/origin/master

  #Make sure bypass the password prompt using ssh config for following command to work from crontab.
 
  # Add the following to your crontab
 */10 * * * * cd /var/local/git_copies/foo-project.git && git fetch origin && git reset --soft refs/remotes/origin/master > /dev/null

Using above command we have setuped a replica of git server on redmine server.

B. Add project repository in Redmine

Go to the redmine, select your project and add repository local path. Goto => setting => repository

C. Fetch commits into redmine

When you first browse the repository, Redmine retrieves the description of all of the existing commits and stores them in the database.
This is done only once per repository but can take a very long time (or even time out) if your repository has hundreds of commits.

To avoid this, you can do it offline.
Run the following rake command on redmine server:

$ ruby script/runner "Repository.fetch_changesets" -e production

All commits will be retrieved in to the Redmine database.

Since Redmdine 0.9.x, you can use following link of your redmine to execute fetch_changesets for a specific project, or all.
http://redmine.example.com/sys/fetch_changesets (=> fetches changesets for all active projects)
http://redmine.example.com/sys/fetch_changesets?id=foo (=> fetches changesets for project foo only)

Now we have sucessfully configured git reposiroty in Redmine.

Now you can use following link for linking revision and commits to issue and vice-varsa.
http://www.redmine.org/projects/redmine/wiki/RedmineTextFormatting

Thursday, November 3, 2011

Passenger tuning for rails application

Guys, it has been a long over due post. I have finished tuning passenger long back but caught up with Diwali celebration.

Anyways generally I prefer simple and easily understandable configuration. So following configuration is as per my best knowledge and google findings. ;)

I have verified my configuration using ab - Apache HTTP server benchmarking tool. Also tried other tools such as Jmeter, httperf. I have also used passenger-memory-stats for finding rails instance size and passenger-status for finding number of request which are pending in global queue.

I have started passenger tuning using passenger nginx user guide. You will also find passenger-apache guide.

As I am using nginx, following configuration is with respect to nginx but same will applies for apache except some extra directives and syntax.

After digging into passenger tuning, I realized that passenger well configured for production. But still Based on nginx-passenger user guide I have collected following list of directive, which we can configure according to our needs.

Directive Default Value Nginx Block Use
passenger_max_pool_size <integer> 6 http Maximum instances on server
passenger_pool_idle_time <integer> 300 sec http instance idle time
passenger_max_instances_per_app <integer> 0 http Maximum instances allowed to single app
passenger_min_instances <integer> 1 http, server, location, if minimum number of application instances which are always active
passenger_pre_start <url> - http Pre start application
passenger_use_global_queue <on|off> on http, server, location, if Turns the use of global queuing on or off
passenger_ignore_client_abort <on|off> off http, server, location, if Ignore client aborts
rails_framework_spawner_idle_time <integer> 1800 sec http, server, location, if FrameworkSpawner server idle time
rails_app_spawner_idle_time <integer> 600 sec http, server, location, if ApplicationSpawner server idle time
passenger_log_level <integer> 0 (Can be 0, 1, 2, 3) http for how much information Passenger should write into error.log
passenger_debug_log_file <filename> error.log http allow to specify the file that debugging and error messages should be written
passenger_pass_header <header name>
http, server, location, if Used to pass headers

Almost all of these directive has default value which is good enough for an applications. But there are some directive which very important to configure depending on our application environment.

1. passenger_max_pool_size :
            The maximum number of Ruby on Rails or Rack application instances that may be simultaneously active. A larger number results in higher memory usage, but improved ability to handle concurrent HTTP clients.
            The value should be at least equal to the number of CPUs (or CPU cores) that you have. If your system has 2 GB of RAM, then we recommend a value of 30. If your system is a Virtual Private Server (VPS) and has about 256 MB RAM, and is also running other services such as MySQL, then we recommend a value of 2.
            I recomond you to find your application instance size using passenger-memory-stats and configure value of passenger_max_pool_size.
            e.g.  If your application instance size is 300 MB and RAM size 2 GB then value of passenger_max_pool_size should be 4 because 1200 MB(4*300) will be used by application instance and remaning for other process.

2. passenger_max_instances_per_app :
            The maximum number of application instances that may be simultaneously active for a single application. This helps to make sure that a single application will not occupy all available slots in the application pool.
            This value must be less than passenger_max_pool_size. A value of 0 means that there is no limit placed on the number of instances a single application may use, i.e. only the global limit of passenger_max_pool_size will be enforced.

3. passenger_pre_start :
            By default, Phusion Passenger does not start any application instances until said web application is first accessed. The result is that the first visitor of said web application might experience a small delay as Phusion Passenger is starting the web application on demand. If that is undesirable, then this directive can be used to pre-started application instances during Nginx startup.
            This directive accepts the URL of the web application you want to pre-start. It may be specified any number of times.
passenger_pre_start http://foo.com/; 
passenger_pre_start http://bar.com:3500/; 
passenger_pre_start http://myblog.com/store;

Let's get into live scenarios


1. A server with multiple rails applications (Development and test server)
            I have a server with 16 GB of RAM and 8 core cpu, on which redmine(bug tracking system), test rails app and other developers app instances are running. Means you can say mutiple apps are running.
            According to passenger-memory-stats redmine is taking 300 MB and other rails app 500 MB. So I have kept following configuration
passenger_max_pool_size 16;
# arround 8 GB for rails application and remaning for other process
passenger_pool_idle_time 150;
# reduced idle time as multiple app are running
passenger_max_instances_per_app 8; 
# since single app should not use whole pool size, but simuteniouly should handle mutiple requests
2. A server with single rails application (Production server)
            On my production enviroment we have 8 GB of RAM and 4 core cpu, on which only single rails application is running.
            As my rails application instance size is 500 MB, I have kept following configuration on production
passenger_max_pool_size 10;
# arround 5 GB for rails application and remaning for other process
passenger_pool_idle_time 600;
# Increased as it is production
passenger_min_instances 2;
# atlease two instances should in memory at any time
passenger_pre_start http://myprodapp.com/;
# pre start my instance at the time of nginx start instade on first request
For testing whether required instances are forked or not use ab (Apache benchmark) as described above.

Reference : http://www.alfajango.com/blog/performance-tuning-for-phusion-passenger-an-introduction/

Friday, October 14, 2011

Simple nginx tuning for rails application on production server

Recently I was working on tuning my production environment for better performance. In production server mainly three things makes a difference to the web application,
  1. REE-GC Tuning
  2. Nginx Tuning
  3. Passenger Tuning

As I have already done REE-GC tuning in previous articles, so lets discuss on Nginx tuning. Passenger tuning will be get covered in subsequent articles.

In Nginx there are many ways to boost your application performance, but I would like to share few important tweaks.

To validate performance improvement, you can use page-speed or Yslow

Before doing these tweaks on my rails application, yslow and page-speed score was around 25-35/100. After doing these tweaks it increased to 90-96/100

To get 100/100 we have to write a good code-design not best code-design ;). In our application the performance dip is due to not using CSS sprite.


A. Enable Gzip :
Gzip helps to compress and decompress the data on-the-fly between browser and server. Nginx has HttpGzipModule for same.

Add following lines into to http block nginx.conf
gzip  on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
gzip_vary on;
gzip_disable     "MSIE [1-6]\.";
#gzip_proxied any;
#gzip_buffers 128 128k;
#gzip_min_length  1100;
Depending on your need you can set the above commented directives.


B. Enable caching :
Setting an expiry date or a maximum age in the HTTP headers for static resources instructs the browser to load previously downloaded resources from local disk rather than over the network.

Add following lines into to server block nginx.conf
   location ~* \.(ico|css|js|gif|jpe?g|png|swf)(\?[0-9]+)?$ {
         expires max;
         break;
   }

C. Minify JavaScript :
Compacting JavaScript code can save many bytes of data and speed up downloading, parsing, and execution time.
For this you can use any third party tools or gems like Jammit,asset_packager to minify your JavaScript files. Rails 3 has built in support.

Sample nginx.conf :
Apart from this you can use multiple assets server for static resources, which will be covered in subsequent articles.

Tuesday, August 23, 2011

Install Redis server on Ubuntu maunally as service

This post is all about installing redis server manually on Ubuntu system and then setup it as a service.

For this post I have originally referred a blog post of denofubiquity.

By default Ubuntu provides redis server installation by

sudo apt-get install redis-server

And you done with the installation of redis server.

But if you want to install latest version of redis server, then you can go through the rest of post.

wget http://redis.googlecode.com/files/redis-2.2.12.tar.gz
# You can specify a link of redis-server which you want to install
tar -zxf redis-2.2.12.tar.gz
cd redis-2.2.12
make
sudo make install

Now setup a redis.conf. I have copied a redis.conf from default installation of redis-server which I have used. If you want to install redis-server for your production machine you may refer a redis.conf from denofubiquity's post

wget https://raw.github.com/gist/1164482/77e4ecf14ffac42b0e987e7ffe16cb757d734ff9/redis.conf
sudo mkdir /etc/redis
sudo mv redis.conf /etc/redis/redis.conf
Copy redis-server startup scripts.
wget https://raw.github.com/gist/1164503/d1bc2cc6782b943d9b28aa93fc7038f4ae5a905f/redis-server
sudo mv redis-server /etc/init.d/redis-server
sudo chmod +x /etc/init.d/redis-server
sudo useradd redis
sudo mkdir -p /var/lib/redis
sudo mkdir -p /var/log/redis
sudo chown redis.redis /var/lib/redis
sudo chown redis.redis /var/log/redis
Mark redis server as startup services.
sudo update-rc.d redis-server defaults
Start your redis server.
sudo /etc/init.d/redis-server start

Note :

You can customise your redis configuration as per your requirements.
Like from Redis 2.2, it supports unix socket, so you can use it if required.
- Specify socket in redis.conf using this line
unixsocket /tmp/redis.sock
- Tell Redis.new() to read it as required. like
Redis.new(:path => "/tmp/redis.sock")

Also you can customise memory configuration in your redis.conf.

Reference :

http://www.denofubiquity.com/nosql/412

Wednesday, August 17, 2011

Nginx rewrite non-www url to www

One of my project facing issue with non-www url. At Browser side we are calling multiple services from different domain and same time we are sharing same cookies for calling services from our website. But it was giving authentication failure for our services.

Issue:

We have embedded zimbra collaboration suite into our rails application. Once user logged into our application, he will automatically gets Single signed into zimbra collaboration suite. We ware showing zimbra UI in separate iframe on web page.

Zimbra has nice feature to extend their own functionality using zimlet.

Zimlet is simply JavaScript code which runs on client side(On Browser).

Using the power of zimlet we want to access the services of our own application, where we ware sharing cookies which already got created for portal on browser side when user signed in.

Every thing was working fine on production. But some time some users were facing issue of authentication failure for zimlet functionality, even though they have signed in into rails application.

After digging into issue, found that some users accessing the application using without www. i.e "https://abcrails.com", and zimlet was trying to access a rails application using "https://www.abcrails.com" where authentication is failing. As cookies ware created for "https://abcrails.com" and not for "https://www.abcrails.com"

For this I have only one solution depending on my requirements, is force non-www url to www.

So following nginx configuration is for:

1. Force http reuest to https

2. Force non-www url to www

Monday, August 15, 2011

Replace DelayedJob with Resque

Background task in Ruby on Rails

One of our task of migrating background task from Delayed_job server to the Resque server. But I have kept this task with low priority in my task list queue.

But since last week, our IT team is facing an issue with Delayed_job server. Our delayed_job server is getting crashed 20 times in 24 hours.

I havn't done the root cause analysis. but I have decided to take this an opportunity to replace delayed_job to resque server on high priority.

Is is not mean that Resque is better than Delayed_job. They have their own features. The working functionality of both server is same.

As Resque is best fitting in our requirements I have choose Resque in place of Delayed_job.

Also our IT team don't have any way or clue get the list of background tasks which are completed, which are pending and which are currently in process.

Why Resque server:

Well following info is sufficient from page https://github.com/defunkt/resque

Resque vs DelayedJob

How does Resque compare to DelayedJob, and why would you choose one over the other?

  • Resque supports multiple queues
  • DelayedJob supports finer grained priorities
  • Resque workers are resilient to memory leaks / bloat
  • DelayedJob workers are extremely simple and easy to modify
  • Resque requires Redis
  • DelayedJob requires ActiveRecord
  • Resque can only place JSONable Ruby objects on a queue as arguments
  • DelayedJob can place any Ruby object on its queue as arguments
  • Resque includes a Sinatra app for monitoring what's going on
  • DelayedJob can be queried from within your Rails app if you want to add an interface

If you're doing Rails development, you already have a database and ActiveRecord. DelayedJob is super easy to setup and works great. GitHub used it for many months to process almost 200 million jobs.

Choose Resque if:

  • You need multiple queues
  • You don't care / dislike numeric priorities
  • You don't need to persist every Ruby object ever
  • You have potentially huge queues
  • You want to see what's going on
  • You expect a lot of failure / chaos
  • You can setup Redis
  • You're not running short on RAM

Choose DelayedJob if:

  • You like numeric priorities
  • You're not doing a gigantic amount of jobs each day
  • Your queue stays small and nimble
  • There is not a lot failure / chaos
  • You want to easily throw anything on the queue
  • You don't want to setup Redis

In no way is Resque a "better" DelayedJob, so make sure you pick the tool that's best for your app.

Action:

This task is done for, quick solution to the production issue. So you might also use it, if you have same issue.

This task I have done on one of our project which is running on rails 2.3.5 with REE 1.8.7. But it will work same for rails 3 also.

Before diving into migration, I highly encourage you to checkout railscasts 171(delayed_job) and RAILSCASTS-271(Resque).

1. Install Redis server

It is dependency for resque server. Like Delayed_jobs uses database(delayed_jobs table), Resque uses Redis server.

I am using ubuntu so,

> sudo apt-get install redis-server

If you want to install latest version of the Redis you can install manually.

2. Install resque gem

If you are using bundler mention it in Gemfile or in your config/environment.rb depending on your application.

3. Load Resque server tasks

Add a file called lib/tasks/resque.rake

# loads all rake task of resque
require 'resque/tasks'

# following statement is required only if your background task needs rails enviroment else skip it
task "resque:setup" => :environment

4. Replace your Delayed_job code with Resque

A.

Find a places in code where you or your developers has been used delayed_jobs.

For this you can simply find a line "send_later" or "Delayed::Job.enqueue" in your code.

Use your development IDE or use grep tool from command line

> cd your_project

> grep -rn "send_later" .

and

> grep -rn "Delayed::Job.enqueue" .

B.

From the above result you will able to find location where delayed_job is used.

So if your are using SomeModel.send_later(function_name, parameter1, parameter2, ..., parameter n)

Replace a line like this

#SomeModel.send_later(function_name, parameter1, parameter2, ..., parameter n)
Resque.enqueue(NameOfModelOrClass, parameter1, parameter2, ..., parameter n)

Name of NameOfModelOrClass can be define from the following step

Either use one of the following

1. Modify respective model

Add following line into the model,

@queue = :name_of_queue

Name of the queue can be any thing whatever you want. You can also logically separate background task by using queue name.

Mofify function_name in such way,

def self.function_name(parameter1, parameter2, ..., parameter n)
{
	# Defination of a function
}

to,

#def self.function_name(parameter1, parameter2, ..., parameter n)
def self.perform(parameter1, parameter2, ..., parameter n)
{
	# Defination of a function
}

2. Separate code from model which I have preferred.

Define class in lib folder(any location you want)

Class FileImportWorker
{
	@queue = :name_of_the_queue

	def self.perform()
	{
		# cut the defination of the function_name and paste here
	}

}

Pass this class name to Resque.enqueue method.

C.

if you are using Delayed::Job.enqueue(ClassOfJob.new(parameter1, parameter2, ..., parameter n))

#Delayed::Job.enqueue(ClassOfJob.new(parameter1, parameter2, ..., parameter n))
Resque.enqueue(ClassOfJob, parameter1, parameter2, ..., parameter n)

e.g

class NewsletterJob < Struct.new(:text, :emails)
    def perform
      emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
    end    
  end 
Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))

to,

class NewsletterJob
    @queue = :name_of_the_queue

    def self.perform(text, emails)
      emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
    end    
  end 
Resque.enqueue(NewsletterJob, 'lorem ipsum...', Customers.find(:all).collect(&:email))

5. Now start your Resque server,

You can start server using a following command.

rake resque:work QUEUE=* COUNT=1

QUEUE=* : specify on which queue to work. * for all Queue

COUNT=1 : Number of worker process

6. Access to queue

Now you can run your code and can have look of pending and in-progress tasks. For this run the following command,

resque-web

It will start resque web interface on port 5678

http://localhost:5678

Bonus Point:

1. Mount Reqsue web interface server into your application

Instead of accessing resque web interface using localhost:5478, you can mount web interface into your application.

Add the following line into your config/routes.rb

mount Resque::Server, :at => "/resque"

Now, access via http://your_server/resque

Also, you can provide basic authentication for it,

add a file called config/initializers/resque_auth.rb with following code,

Resque::Server.use(Rack::Auth::Basic) do |user, password|
  password == "secret"
end

2. You can club background tasks (which we have defined in lib folder) into single folder

As done in rails cast you can add a folder app/workers, and keep your all tasks file in it.

or

Which I done by adding folder lib/workers

Only you need to load this folder into your application,

In rails 2,

Add file config/initializers/load_workers.rb with following code.

Dir["#{RAILS_ROOT}/lib/workers/*.rb"].each { |f| require(f) }

In rails 3,

add following line in application.rb,

config.autoload_paths += %W(#{config.root}/lib/workers)

Troubleshooting:

1. Resque database connection issue

We are using postgresql in our portal but found that when I pushed my code on testing server where we are using same configuration which we have for our production ( We have configured Nginx + Passenger + REE for same), resque was not working.

After looking into resque-web interface found following error,

PGError: server closed the connection unexpectedly

Which I solved by adding following code into /lib/tasks/resque.rake

task "resque:setup" => :environment do
  ENV['QUEUE'] = '*'

  Resque.after_fork do |job|
    ActiveRecord::Base.establish_connection
  end

end

desc "Alias for resque:work (To run workers on Heroku)"
task "jobs:work" => "resque:work"

You can find this solution in following URL.

RAILSCASTS-271(Resque)

http://stackoverflow.com/questions/2611747/rails-resque-workers-fail-with-pgerror-server-closed-the-connection-unexpectedly

I didn't find time to do root cause analysis for this issue, but I thing this is due to passenger as it was working well on my development machine in production mode.

2. Sequence issue

Exact error I forgot but I have faced a sequence related error. Solution was to not to pass a complex object in call Resque.enqueue, like ActiveRecord, File data etc.

3. undefined method `******' for #Hash:000

This is due to, if your are storing complex object(ActiveRecord) in resque. Resque uses Redis server which is key-value store, which returns object in hash.

So tackle this simple pass object-id as parameter and in background processing code extract the original object again using ID.

Saturday, May 28, 2011

Convert string to a class/model

This post is related to how to convert string to class ( model ). I came across this situation while migrating one of my organization's project on rails 3.

Many times we came across a situation where we need to convert a string to respective class or model. So that we can call methods on it. Generally to handle this situation new rails developers use case statement like this,

case class_name_string
when 'Account'
object = Account.find(id)

when 'Contact'
object = Contact.find(id)

when 'Opportunity'
object = Opportunity.find(id)

when 'Campaign'
object = Campaign.find(id)

when 'Model1'
object = Model1.find(id)

when 'Model2'
object = Model2.find(id)
:
:
:
:
when 'Modeln'
object = Modeln.find(id]) 
end

This can be actually done pretty easily by using constantize in only one line

class_name_string.constantize.find(id)

Sunday, May 22, 2011

Touch pad is not working on Ubuntu 11.04

Following is blog post with respect to the problem which I faced on my dell xps laptop with Ubuntu 11.04. My mouse pad(Touch pad) was not working after login screen.

After some googling I found forum articles which describing same problem. Those peoples have different version of Ubuntu with different manufactures of laptop.

But I didn't found an exact cause of this issue. One of the person from the forum was saying, this is might be due to use of mobile broadband network.

Might be this is the cause because, I am also using mobile broadband on my laptop where issue is occurring. But at the same time in my company I am using Ubuntu 11.04 on my development machine with wired Internet but I never faced this issue.

Anyways following is the solution,
Execute following command on terminal

gconftool-2 --set --type boolean /desktop/gnome/peripherals/touchpad/touchpad_enabled true


Reference:

https://help.ubuntu.com/community/SynapticsTouchpad

http://ubuntuforums.org/showthread.php?t=1658103

Friday, April 15, 2011

Configure Rack-bug for rails 3

While migrating one of my rails application on rails 3, I thought of using rack-bug simultaneously for improving performance.

Following are the steps which I used for configuring rack-bug for rails 3 (3.0.6).


1. Install rack-bug (branch rails3) as plugin
   cd vendor/plugins
   git clone -b rails3 https://github.com/brynary/rack-bug.git


If you want to you it as gem then add following line into Gemfile
gem 'rack-bug', :git => 'https://github.com/brynary/rack-bug.git', :branch => 'rails3'


2. Replace the code from file actionview_extension.rb
which is avilable in vendor/plugins/rack-bug/lib/rack/bug/panels/templates_panel/ as specified in bug of rack_bug repository


if defined?(ActionView) && defined?(ActionView::Template)
  ActionView::Template.class_eval do
    def render_with_rack_bug(*args, &block)
      Rack::Bug::TemplatesPanel.record(virtual_path) do
        render_without_rack_bug(*args, &block)
      end
    end
    alias_method_chain :render, :rack_bug
  end
end

If you are using gem override the specified file in some way


3. Add following lines into your config.ru
   require 'rack/bug'
   use Rack::Bug, :secret_key => "someverylongandveryhardtoguesspreferablyrandomstring"
   run myapp


4. Start your server and access the URL http://your_app/__rack_bug__/bookmarklet.html
   and enter the password.







Thursday, March 17, 2011

Ruby Version Manager with Gemset - ree installation


Recently I had given a session on Ruby version manager(RVM) with gemset, explained some of the important and useful bonus points which makes our life easier.


The reason behind this session being the need for our team to work on multiple projects and manage them efficiently, which run on different version of Rails. We are evaluating different version of ruby, like Ruby Enterprise edition, Ruby 1.9.2, Ruby 1.8.7.


I have posted my presentation on slideshare. You can download from there.



Now, I would like to discuss a problem which my colleagues has faced while installing Ruby enterprise edition under RVM.


A. While they using a command,
rvm install ree
They have got following error,


ERROR: Error running './installer -a /home/usera/.rvm/rubies/ree-1.8.7-2011.03 --dont-install-useful-gems ', please read /home/usera/.rvm/log/ree-1.8.7-2011.03/install.log
ERROR: There has been an error while trying to run the ree installer. Halting the installation.



So I have investigated about this error and I found that, some of the dependecy are met for ree installation.
This dependency I found using, installing ree manually under rvm,
Go to ree source folder, where ree source code is located
cd ~/.rvm/src/ree-1.8.7-2010.02


Then copy the command from above error, which is
./installer -a /home/usera/.rvm/rubies/ree-1.8.7-2011.03  --dont-install-useful-gems


After running above command, it will shows why ree installation failed.
On my colleague's machine, it is shows package libreadline5-dev is not installed

So,
sudo apt-get install libreadline5-dev
Then install the dependeny that ree asked, and then install ree again
rvm install ree
B. Also one of my colleague faced following issue, while starting server using ruby script/server


/home/usera/.rvm/gems/ree-1.8.7-2011.03@livia_portal/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:55: uninitialized constant ActiveSupport:ependencies::Mutex (NameError)
from /home/usera/.rvm/gems/ree-1.8.7-2011.03@livia_portal/gems/activesupport-2.3.5/lib/active_support.rb:56:in `require'
from /home/usera/.rvm/gems/ree-1.8.7-2011.03@livia_portal/gems/activesupport-2.3.5/lib/active_support.rb:56
from /home/usera/.rvm/gems/ree-1.8.7-2011.03@livia_portal/gems/rails-2.3.5/lib/commands/server.rb:1:in `require'
from /home/usera/.rvm/gems/ree-1.8.7-2011.03@livia_portal/gems/rails-2.3.5/lib/commands/server.rb:1
from script/server:3:in `require'
from script/server:3



After investing I found that, latest version of gem is conflicting with old versions of rails (Rails-2.3.5 and gem 1.6.2).


So we have added following line into config/boot.rb
require 'thread'
Which resolved the above issue.