Tag Archives: sinatra

How to Implement Caching in Sinatra/Ruby Apps

Sinatra is a lightweight web framework for Ruby. It’s a DSL that you can easily learn to build web applications faster or create apis that can easily serve millions of requests per day, while using least amount of RAM and CPU cycles (as compare to Ruby on Rails or other ruby frameworks). In this article, you will learn how to further optimise a Sinatra app by adding a simple in-memory caching system.

Ruby is my favourite language and I prefer Sinatra for most of my web projects, except when I’m creating something complex and going with the sensible default of Rails seems like a better choice. Recently, one of my website (sinatra based) was loading slow due to lots of database calls and implementing a caching system was the easiest thing to do to speed up the website. I used memcached because of its simplicity.

What is Memcached ?

Memcached is a high performance in-memory key value caching system. All the data is stored in memory (RAM) and it provides a very fast lookup via their keys. I just wanted to save expensive database calls, memcached is very efficient at this. Persistence was not required otherwise I would have selected redis.

Setting up Memcached on Ubuntu

Install it via your package manager or terminal.

sudo apt-get install memcached

Then you will also need a memcached client for ruby. I used the Dalli Gem. Add this to Gemfile and install it via bundler.

Here is a simple caching strategy I used for a blog. You could use this as starting point and customise it for your need.

Although default memcached config would serve you well but If you want to tweak few things for memcached, look at the configuration file at /etc/memcached.conf. To check the live status (total cached objects, ram consumed, hits, misses etc) use this command :

watch "echo stats | nc 127.0.0.1 11211"

When you’re working with caching, testing whether it works or not, flushing the cached objects is required sometimes. Here is that handy command to flush all the key-values from the memory :

echo 'flush_all' | nc localhost 11211

You could also pass options block to Dalli instance for setting an expiry timeline for stored objects.

>> options = { :namespace => blog, :expires_in => 10.day, :compress => true }
>> Dalli::Client.new('localhost:11211', options)

By implementing a simple caching using memcached saved me lots of expensive database calls, cpu power, and resulted in faster experience for the users as well. That’s win-win for everyone! (users, servers, developers ). Cache it if you can!

Relevant Links and References :

Deploying Sinatra Applications to VPS with Passenger/Nginx [Ubuntu 14.04]

Sinatra is a lightweight web development framework (a.k.a micro framework) written in Ruby. It’s a MVC framework (just like Rails) but more suitable for simple and small web projects or APIs (otherwise, you’d probably end up writing too much code, which could be done more easily in Rails, in case of complex web applications).

If you’re new to web development and you’ve just learned ruby then then you should start with sinatra. It’s much easier to learn and you can start creating something (ideally something useful) within hours. Create a simple web app and deploy to Heroku or your VPS using passenger. This article is all about deploying your Sinatra app to VPS.

Why passenger ?

For simple lightweight web applications (sinatra apps), I prefer passenger. You can easily run multiple applications on a single server without any extra configuration (good for small side projects). Later, you can move to unicorn (probably faster response cycle) or puma (lightweight, optimized for concurrency) or something else, if you really need to.

Getting a VPS

Before going through the steps, I assume you’ve already selected a VPS company, if not I would recommend Digital Ocean. (That’s a referral link and you’ll receive $10 credit – worth two months of free hosting) They’ve great plans starting at just $5 per month, well suitable for fun/side projects. For server operating system, select Ubuntu 14.04 LTS, otherwise, you may need to adjust few commands a little, depending on the Linux distribution. For memory and other requirements, 512 MB plan may be enough for a starting out, as you can always upgrade later if required.

Step 1. Server setup

Login into the server (with default root user) :

ssh root@SERVER_IP_ADDRESS

Update existing packages and install nano (a lightweight text editor)

apt-get update && apt-get upgrade
apt-get -y install python-software-properties nano

Creating a user

adduser username --ingroup sudo

Now, upload your ssh key (public key) from development machine. (local)

ssh-copy-id username@IP_ADDRESS

Now ssh into the server, and make sure ssh keys are setup as as expected. Additionally, you can turn off password authentication for additional security, type sudo nano /etc/ssh/sshd_config and disable password authentication by changing the value from yes to no. (PasswordAuthentication no)

Note : If you haven’t created any ssh key before (on your local computer), then run ssh-keygen to generate one.

Step 2. Install Ruby

install curl, git and other dependencies

sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev

installing rbenv

git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

Install ruby 2 and set it as the default

git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL
rbenv install 2.1.5
rbenv global 2.1.5

Now check if it’s installed correctly

ruby -v

Update gemrc and Install Bundler

echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
rbenv rehash

Step 3. Install nginx and passenger

Adding the passenger repository (official)

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 561F9B9CAC40B2F7
sudo apt-get install apt-transport-https ca-certificates
sudo sh -c "echo 'deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main' >> /etc/apt/sources.list.d/passenger.list"
sudo chown root: /etc/apt/sources.list.d/passenger.list
sudo chmod 600 /etc/apt/sources.list.d/passenger.list
sudo apt-get update

Install nginx (with extras) and passenger

sudo apt-get install nginx-extras passenger

Step 4. Deployment setup

Setting up nginx and passenger is fairly easy. Just make sure the ruby path is setup correctly in your nginx config. Type  which ruby to get the ruby version and make sure that’s correctly specified at /etc/nginx/nginx.conf. (just lookout for the lines that says passenger_ruby)

/etc/nginx/nginx.conf file (make sure it looks like this)

passenger_ruby /home/rkjha/.rbenv/shims/ruby;

Server config for nginx/passenger

sudo nano /etc/nginx/sites-available/example.com

Here is a sample config you can use. Replace example.com and username accordingly. (it also creates a 301 redirect for www version of your domain, you can change that if you want).

Now create a symlink for that config and reload the server to apply the new config.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo service nginx reload

Setting up git/bitbucket Add your project to git (if you’ve not done that already), create an account at bitbucket.org And create a repository (private unless you want the source code of your app to be available open/freely) there. You may need to upload the public key (server), go to repo’s settings and Add Deployment key there.

git init
git add --all
git commit -am "first release"
git remote add origin git@bitbucket.org:USERNAME/YOUR-REPO-NAME.git  
git push origin master

If you’ve not used git before then check out some free tutorials listed here and comeback later. Deploy using custom rake task You can use some deployment tools like Capistrano (probably overkill for a Sinatra app) or mina (a lightweight deployment tool). But here, I’ll keep things simple and just use a simple rake task to deploy the code.

Step 5. Deploy

To deploy the app simply type : (you may need to add bundle exec before rake command)

rake deploy:setup
rake deploy

You may also need to run bundle install on server, as the rake task is only fetching the latest files from bitbucket repo. Or you can add few lines there (Rakefile) to do that for you. I’ve tried to keep things as simple as possible or I’ll add that later.

If the deployment is successful you can add a DNS entry for your domain. (Make sure you also add an entry for www, so, the www.example.com will be redirected to example.com, without creating any confusion or duplicate issue in Search Engines)

Note : If you’ve any problem or I missed something, let me know (via comments).

References