You are on page 1of 53

Put Down the Hammer

On using the right tool for the job.

Tyler McMullen
Put Down the Hammer

Case Study: Tag Autocompleter

Tyler McMullen
Put Down the Hammer

The Naïve Solution

Tyler McMullen
Put Down the Hammer

The Naïve Solution

•Tag.find
:conditions
=>
[‘text
LIKE
?’,
...]

Tyler McMullen
Put Down the Hammer

The Naïve Solution

•Tag.find
:conditions
=>
[‘text
LIKE
?’,
...]
•TagController#autocomplete

Tyler McMullen
Put Down the Hammer

That’s not so bad, is it?

Tyler McMullen
Put Down the Hammer

What is important for an autocompleter?

Tyler McMullen
Put Down the Hammer

SPEED

Tyler McMullen
Put Down the Hammer

Let’s enumerate the steps to get the tags.

Tyler McMullen
Put Down the Hammer

Ajax Call

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects
Render JSON/HTML

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects
Render JSON/HTML
Respond

Unnecessary steps that take up most of the time!

Tyler McMullen
Put Down the Hammer

How can we do this more efficiently?

Tyler McMullen
Put Down the Hammer

First, ditch the database.

Tyler McMullen
Put Down the Hammer

ActiveRecord and MySQL are overkill.

Tyler McMullen
Put Down the Hammer

Use a Trie.

Tyler McMullen
Put Down the Hammer

Use a Trie.

(Shameless Plug: http://github.com/tyler/trie)

Tyler McMullen
Put Down the Hammer

B E E

Tyler McMullen
Put Down the Hammer

A R

B E

Tyler McMullen
Put Down the Hammer

A R T

B E A R

Tyler McMullen
Put Down the Hammer

Use the right data structure for the job.

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Tag.find
MySQL full table scan
Map rows to ActiveRecord objects
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Trie#children(prefix)
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Trie#children(prefix)
Render JSON/HTML
Respond
Our next victims!

Tyler McMullen
Put Down the Hammer

Next step is to sidestep the Rails app altogether.

Tyler McMullen
Put Down the Hammer

If not Rails... then what?!

Tyler McMullen
Put Down the Hammer

Tyler McMullen
Put Down the Hammer

Rack is awesome for little web services.

Tyler McMullen
Put Down the Hammer

•Fast

Tyler McMullen
Put Down the Hammer

•Fast
•Low Memory Usage

Tyler McMullen
Put Down the Hammer

•Fast
•Low Memory Usage
•Easy

Tyler McMullen
Put Down the Hammer

class
Autocompleter


def
initialize




@trie
=
Trie.load_file(file,
/(\w+)\|(\d+)/)


end



def
call(env)




req
=
Rack::Request.new(env)




words
=
@trie.children(req.params['query'])[0,5]




[
200,






{
"Content‐Type"
=>
"text/html"
},






[words.to_json]
]


end
end

Tyler McMullen
Put Down the Hammer

What about routing?

Tyler McMullen
Put Down the Hammer

location
/autocomplete
{
proxy_pass
http://autocompleter;


}

Tyler McMullen
Put Down the Hammer

location
/autocomplete
{
proxy_pass
http://autocompleter;


}

(You are using Nginx, right?)

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Router
Action
Trie#children(prefix)
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Ajax Call
Nginx
Rack Service
Trie#children(prefix)
Render JSON/HTML
Respond

Tyler McMullen
Put Down the Hammer

Does it really matter?

Tyler McMullen
Put Down the Hammer

It’s handling 200 requests per second.

Tyler McMullen
Put Down the Hammer

It’s handling 200 requests per second.


On a single Thin instance.

Tyler McMullen
Put Down the Hammer

It’s handling 200 requests per second.


On a single Thin instance.
With a response time of 0.006s.

Tyler McMullen
Put Down the Hammer

It’s handling 200 requests per second.


On a single Thin instance.
With a response time of 0.006s.
Takes up 18mb of ram.

Tyler McMullen
Put Down the Hammer

It’s handling 200 requests per second.


On a single Thin instance.
With a response time of 0.006s.
Takes up 18mb of ram.
And <1% cpu usage.

Tyler McMullen
Put Down the Hammer

It’s good for your users and your budget.

Tyler McMullen
Put Down the Hammer

Questions?

Tyler McMullen

You might also like