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
Tyler McMullen
E
E
Put Down the Hammer
A B
E E
Tyler McMullen
R
Put Down the Hammer
B
A
R
T
E
A
R
E
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