<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>chris.heald.me</title>
    <description>My personal blog.</description>
    <link>https://chris.heald.me//</link>
    <atom:link href="https://chris.heald.me//feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Thu, 24 Mar 2022 01:33:34 -0500</pubDate>
    <lastBuildDate>Thu, 24 Mar 2022 01:33:34 -0500</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>Domains, DNS, and SSL, oh my!</title>
        <description>&lt;p&gt;I recently helped a friend navigate the process of untangling himself from his registrar’s expensive SSL offerings, and wrote up the following to help illustrate what the pieces are and how they fit together. He asked that I publish this somewhere that others could benefit from it, so here it is!&lt;/p&gt;

&lt;h2 id=&quot;registrars-dns-servers-and-ssl&quot;&gt;Registrars, DNS, Servers, and SSL.&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Registrars are gatekeepers for registering “names”, which are subdomains on top-level domains (TLDs). There’s something called the “internet root nameservers” which are the bottom of the turtle stack. They say “this so and so can answer queries for this particular TLD” like .com and .net and .mobile and whatever. There are 13 root servers on the internet (actually served by hundreds of machines, but logically, there are 13), and they currently serve about 730 TLDs. All the software in the world that uses DNS knows about these 13 root servers (or delegates that knowledge). This is known as the &lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_root_zone&quot;&gt;DNS Root Zone&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;The root servers are administrated by ICANN, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Domain_name_registrar&quot;&gt;Domain Name Registrars&lt;/a&gt; (like GoDaddy and Namecheap) contract with ICANN to provide domain registration services, so Namecheap says “I want to be able to sell domains on the .com TLD”, they pay ICANN, ICANN lets the root servers know about Namecheap as a valid resolver of .com domains.&lt;/p&gt;

    &lt;p&gt;Registrars’ primary function is to sell registration of domains on the TLDs they contract on. They may provide other services, but fundamentally you pay them money, they agree to resolve DNS lookups for NS (nameserver) queries for yourname.com to a nameserver of your choosing to figure out where it should actually go.&lt;/p&gt;

    &lt;p&gt;This is the point at which your NECESSARY engagement with them is at an end. Most registrars sell a bunch of other services, like hosting, SSL, and mail. None of this is required (but may be convenient, if often overpriced).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once I, as a requesting client, have discovered the nameserver for a domain, I send a query to that nameserver for the record I want. If you want to discover the webserver IP address for a name, you send a query for an A record. If you want the mailserver, you send a query for the MX record. So on and so forth.&lt;/p&gt;

    &lt;p&gt;The nameserver responds with an IP address to the querying client.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The client can make a request of the kind and shape of its choosing to the IP address. If you know the IP address, you don’t have to go through the whole rigamarole, but nobody wants to hand out “please visit 152.43.20.221/~mysite/public/ for more information” on their business cards.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;So, SSL. SSL is used during step 3, usually by clients making HTTP requests (which are used for serving web pages). It’s a way to validate that the IP you’re talking to actually is authorized to respond to requests for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt;, and it allows for the encryption of traffic in a way that both ends can understand. Clients send a request to an IP, but they also include a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host: yourname.com&lt;/code&gt; header which indicates “this request was made because a client wanted &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt;”, which lets the server do some routing (in case multiple domains are hosted on one IP) and lets it decide what cert to respond with (the one that matches the request for that domain)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To validate an SSL certificate, your client has to know about a &lt;a href=&quot;https://en.wikipedia.org/wiki/Certificate_authority&quot;&gt;certificate authority&lt;/a&gt; that has signed the certificate you receive from the server. CAs can be thought of as notaries public. Browsers come with a list of trusted CAs (LetsEncrypt is one). If a random website hands you a cert, you have no idea if they made it up or not. But if the cert you receive for a given domain is cryptographically signed as valid by a CA (notary) you trust, then you can trust that the certificate was issued by someone who owns that domain, because the CA trusts that they own the domain. Many domain name registrars also provide CA services (they get themselves registered as trusted notaries for signing certs).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To get an SSL cert that’s signed as valid by a CA, I therefore have to prove to the CA operator that I do, in fact, have permission to serve traffic on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt;. How this proof is established is up to the CA. Some registars do that by requiring that you host your domain registration with them, so they know by lazy virtue of the fact that you’re logged in and that you registered &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt; that you do in fact have permissions on that name, but there are other ways, too.&lt;/p&gt;

    &lt;p&gt;By contrast, LetsEncrypt lets you prove ownership by either&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;making your website answer to a random challenge they provide, or&lt;/li&gt;
      &lt;li&gt;making your DNS answer to a random challenge they provide&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;Both prove you have control over the domain. This is why it works anywhere with any host, though, because as long as you can provide those challenges you can prove ownership and get your certs signed by them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The SSL certificate is basically just a binary blob which can be read by computers as a cryptographically-signed claim of “this certificate is for this domain and is good through this date”. Once you HAVE the cert you can install and use it anywhere you want (as along as the name you’re serving under matches what’s on the cert). You don’t have to use a registrar’s hosting services, you don’t have to use their mail services, you can serve that binary blob from any server you want. As long as the client’s expectation (I requested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt; and got a certificate for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourname.com&lt;/code&gt;) is met, and the certificate is signed as valid by a CA the client trusts, you’re good to go.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;a-practical-example&quot;&gt;A Practical Example&lt;/h2&gt;

&lt;p&gt;I register my domains through Namecheap, and I tell Namecheap “use these DigitalOcean nameservers for my names”.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bas&quot;&gt;$ whois heald.me | grep Name
Domain Name: HEALD.ME
Registrar: NameCheap, Inc.
Name Server: NS1.DIGITALOCEAN.COM
Name Server: NS2.DIGITALOCEAN.COM
Name Server: NS3.DIGITALOCEAN.COM
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I configure my DNS at DigitalOcean, which publishes the records I specify on their nameservers. These records include things like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;heald.me.        300  IN      TXT     &quot;v=spf1 include:_spf.protonmail.ch mx ~all&quot;
chris.heald.me.  300  IN      A       178.128.3.212
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My DNS is configured to point my names to my DigitalOcean servers (178.128.3.212).&lt;/p&gt;

&lt;p&gt;When my client goes to look up my DNS, it goes to the root servers (or to a server that has information from the root servers cached) and figures out where we’re going. Here’s a full verbose trace of a query for “the A record for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chris.heald.me&lt;/code&gt;”.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dig IN A heald.me +trace

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.16.15-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; IN A heald.me +trace
;; global options: +cmd
.                       12681   IN      NS      f.root-servers.net.
.                       12681   IN      NS      g.root-servers.net.
.                       12681   IN      NS      h.root-servers.net.
.                       12681   IN      NS      i.root-servers.net.
.                       12681   IN      NS      j.root-servers.net.
.                       12681   IN      NS      k.root-servers.net.
.                       12681   IN      NS      l.root-servers.net.
.                       12681   IN      NS      m.root-servers.net.
.                       12681   IN      NS      a.root-servers.net.
.                       12681   IN      NS      b.root-servers.net.
.                       12681   IN      NS      c.root-servers.net.
.                       12681   IN      NS      d.root-servers.net.
.                       12681   IN      NS      e.root-servers.net.
;; Received 525 bytes from 192.168.4.248#53(192.168.4.248) in 63 ms

me.                     172800  IN      NS      a0.nic.me.
me.                     172800  IN      NS      a2.nic.me.
me.                     172800  IN      NS      b0.nic.me.
me.                     172800  IN      NS      b2.nic.me.
me.                     172800  IN      NS      c0.nic.me.
me.                     86400   IN      DS      45352 8 2
;; Received 681 bytes from 192.203.230.10#53(e.root-servers.net) in 9 ms

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the root server query. My computer talks to my upstream DNS servers, which are preconfigured to know about the root servers. It went to the root servers and said “I need to know where to find information about .me domains”. It learns that this a set of servers at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nic.me&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These records exist because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nic.me&lt;/code&gt; is contracted with ICANN to provide .me registrations.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;heald.me.               86400   IN      NS      ns2.digitalocean.com.
heald.me.               86400   IN      NS      ns3.digitalocean.com.
heald.me.               86400   IN      NS      ns1.digitalocean.com.
;; Received 606 bytes from 199.253.60.1#53(b0.nic.me) in 206 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It went to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b0.nic.me&lt;/code&gt; and asked for where to find nameservers for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heald.me&lt;/code&gt;. b0.nic.me said “Oh, that’s these DigitalOcean servers”.&lt;/p&gt;

&lt;p&gt;These records exist because Namecheap is contracted with nic.me and was permitted to write the records for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heald.me&lt;/code&gt; after I bought the domain through them.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chris.heald.me.         300     IN      A       178.128.3.212
;; Received 53 bytes from 173.245.59.41#53(ns2.digitalocean.com) in 31 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, it went to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ns.digitalocean.com&lt;/code&gt; and said “I need the A record for chris.heald.me” and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ns2.digitalocean.com&lt;/code&gt; responded “That’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;178.128.3.212&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These records exist because I instructed DigitalOcean to serve them for the domain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heald.me&lt;/code&gt; via their nameservers.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;My DigitalOcean servers are running nginx, which I’ve configured to automatically provision SSL certificates for my names from LetsEncrypt, and which respond to challenges by LetsEncrypt by creating an arbitrary challenge file on demand to be fetched via an HTTP request.&lt;/p&gt;

&lt;p&gt;Requests for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chris.heald.me&lt;/code&gt; go to the root name servers to find out who can serve &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.me&lt;/code&gt;, then delegates down the chain to find who registered &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heald.me&lt;/code&gt;. Namecheap says “I’ve got that one! Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ns1.digitalocean.com&lt;/code&gt; to find out more”.&lt;/p&gt;

&lt;p&gt;Your client goes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ns1.digitalocean.com&lt;/code&gt; and says “I want the A record for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chris.heald.me&lt;/code&gt;”, and DO says “that’s 178.128.3.212”.&lt;/p&gt;

&lt;p&gt;Your client connects to 178.128.3.212 on port 443 (which is the port for HTTPS by convention) and says “This is an HTTPS request for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host: chris.heald.me&lt;/code&gt;”.&lt;/p&gt;

&lt;p&gt;My server responds with “Here’s my certificate for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.heald.me&lt;/code&gt;”. Your client says “Yup, it’s signed by LetsEncrypt, and I trust LetsEncrypt, so I trust you. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chris.heald.me&lt;/code&gt; matches what I asked for, so we can go ahead. Give me what you have for Path: /”&lt;/p&gt;

&lt;p&gt;My server responds with HTML content and your browser displays it. Et voila!&lt;/p&gt;
</description>
        <pubDate>Thu, 20 Jan 2022 00:00:00 -0600</pubDate>
        <link>https://chris.heald.me//2022/domains-dns-ssl/</link>
        <guid isPermaLink="true">https://chris.heald.me//2022/domains-dns-ssl/</guid>
        
        
      </item>
    
      <item>
        <title>Secure DNS-based ad filtering with Pihole and Stubby via Docker</title>
        <description>&lt;p&gt;I’ve been running a &lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pihole DNS server&lt;/a&gt; internally for a while. It’s great! It’s like adblock, except for your whole network - all your devices, TVs, phones, tablets, and computers - get ad filtering.&lt;/p&gt;

&lt;p&gt;Recently I wanted to see if I could get my Pihole to play nice with &lt;a href=&quot;https://dnsprivacy.org/wiki/display/DP/About+Stubby&quot;&gt;Stubby&lt;/a&gt;. With the imminent advent of &lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_over_HTTPS&quot;&gt;DNS-over-HTTPS&lt;/a&gt; and the subsequent ISP freakout over the loss of their sweet, sweet data mining, I figured now was as good a time as any to add an extra layer to my network.&lt;/p&gt;

&lt;p&gt;I use and love Docker. I’ve worked up this simple Bash script which will wire up Stubby (pointed to Cloudflare’s DNS-over-TLS servers), and then point the Pihole to Stubby. This means that the Pihole does all its name resolution away from the prying eyes of my ISP (yes, it gives it to Cloudflare, though Cloudflare has a &lt;a href=&quot;https://developers.cloudflare.com/1.1.1.1/commitment-to-privacy/&quot;&gt;better stance on privacy&lt;/a&gt; than most ISPs), and I get DNS filtered. Best of both worlds!&lt;/p&gt;

&lt;p&gt;Here’s the script. I run it on an Ubuntu 16.04 server, but you should be able to run it on anything that can run Docker.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;$#&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-ne&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; 123.123.123.123 password&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Specify your host's LAN IP and the desired Pihole password&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;

docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt; always &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; stubby &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8053:8053/udp mvance/stubby:latest
&lt;span class=&quot;c&quot;&gt;# We need a network for the pihole to be able to talk to stubby&lt;/span&gt;
docker network create &lt;span class=&quot;nt&quot;&gt;--subnet&lt;/span&gt; 172.18.0.0/16 dns
&lt;span class=&quot;c&quot;&gt;# Attach stubby to the network, specifying the IP it should use&lt;/span&gt;
docker network connect dns stubby &lt;span class=&quot;nt&quot;&gt;--ip&lt;/span&gt; 172.18.0.3

docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; pihole &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$IP&lt;/span&gt;:53:53/tcp &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$IP&lt;/span&gt;:53:53/udp &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 67:67/udp &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 80:80 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 443:443 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; pihole:/etc/pihole/ &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; dnsmasq.d:/etc/dnsmasq.d/ &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ServerIP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DNSMASQ_LISTENING&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;WEBPASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;unless-stopped &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--cap-add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;NET_ADMIN &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--dns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;172.18.0.3 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    pihole/pihole:latest

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your password for https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/admin/ is &quot;&lt;/span&gt;
docker logs pihole 2&amp;gt; /dev/null | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'password:'&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Attach pihole to the network, specifying the IP it should use&lt;/span&gt;
docker network connect dns pihole &lt;span class=&quot;nt&quot;&gt;--ip&lt;/span&gt; 172.18.0.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can go to http://your-lan-ip/admin to log into the Pihole dashboard (and you should, it’s neat), or just resolve a request against it. Try this:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LAN_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;123.123.123.123 &lt;span class=&quot;c&quot;&gt;# replace 123.123.123.123 with your server's LAN IP&lt;/span&gt;

dig @&lt;span class=&quot;nv&quot;&gt;$LAN_IP&lt;/span&gt; google.com                   &lt;span class=&quot;c&quot;&gt;# normal request - should succeed&lt;/span&gt;
dig @&lt;span class=&quot;nv&quot;&gt;$LAN_IP&lt;/span&gt; analytics.google.com         &lt;span class=&quot;c&quot;&gt;# pihole-filtered request - should fail&lt;/span&gt;
dig @&lt;span class=&quot;nv&quot;&gt;$LAN_IP&lt;/span&gt; sigok.verteiltesysteme.net   &lt;span class=&quot;c&quot;&gt;# dnssec test - should succeed&lt;/span&gt;
dig @&lt;span class=&quot;nv&quot;&gt;$LAN_IP&lt;/span&gt; sigfail.verteiltesysteme.net &lt;span class=&quot;c&quot;&gt;# dnssec test - should fail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could do this with a docker-compose file pretty easily, too, but I appreciate the straightforwardness of Bash scripts. :)&lt;/p&gt;

&lt;p&gt;And that’s it! Just update your DHCP server (probably your router) to hand out your LAN IP as the DNS server for devices on your network, and DNS on your network is secured and you’ll notice a lot of ads, trackers, and other annoyances no longer exist.&lt;/p&gt;
</description>
        <pubDate>Thu, 24 Oct 2019 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2019/docker-pihole-stubby-dns-tls/</link>
        <guid isPermaLink="true">https://chris.heald.me//2019/docker-pihole-stubby-dns-tls/</guid>
        
        
      </item>
    
      <item>
        <title>Docker &amp; Default Routes</title>
        <description>&lt;p&gt;Docker has some surprising behavior when you have a container attached to multiple networks. If you’re not aware, it can cause some really bizarre network issues.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;If you have multiple networks attached to your container, make sure your first network lexically sorts before the others you attach, or your default gateway will change, which breaks all kinds of things.&lt;/p&gt;

&lt;h2 id=&quot;the-long-version&quot;&gt;The Long Version&lt;/h2&gt;

&lt;p&gt;Lately I’ve been working on blue-green deployment processes with Docker. It’s a simple enough idea: bring up a container, make sure it’s healthy, and then turn on network routing for it. This is especially important when “container up” doesn’t mean “application in the container is ready” (basically any Rails or Java app, for example).&lt;/p&gt;

&lt;p&gt;The basic structure of my setup is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jwilder/nginx-proxy&quot;&gt;nginx-proxy&lt;/a&gt; on each Docker host runs on a known port, and the LB proxies to it&lt;/li&gt;
  &lt;li&gt;Containers are brought up with a VIRTUAL_HOST environment variable, which instructs nginx-proxy to rewrite its nginx config and reload, so it can now route to the new container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a problem when the container isn’t quite ready, because nginx can be reloaded before the application is answering. This results in at least 1 request stalling failing, and depending on upstream nginx configs, can end up blacklisting the entire backend (or docker host, in the LB’s case) for a timeout period.&lt;/p&gt;

&lt;p&gt;So, to solve this, I’m using the typical back/front network setup:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;nginx-proxy is attached to the docker bridge network.&lt;/li&gt;
  &lt;li&gt;nginx-proxy is also attached to each application’s front network, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app_stage&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Each container is brought up in its back network, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app_stage_internal&lt;/code&gt; and health checked.&lt;/li&gt;
  &lt;li&gt;Once the application is healthy, the container is attached to the front network, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app_stage&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;We kick off the nginx config scan/rewrite/reload, and nginx should start routing traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not that hard, right? Except…well, it didn’t work. The first few requests after attaching the front network to the container would stall. If I had open long-running connections (Redis and Websockets were the biggest offenders), then they just remained open but never received any data. They were connected, but effectively dead. What on earth was going on? If I used just one network, it wasn’t a problem. I’d begun to wonder if Docker’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network connect&lt;/code&gt; mechanism was broken in some way that either everyone knew about and didn’t talk about, or if I just had a broken setup. I couldn’t find any reference to anyone else having any problem like this.&lt;/p&gt;

&lt;p&gt;I finally stumbled into the solution using &lt;a href=&quot;https://github.com/nicolaka/netshoot&quot;&gt;netshoot&lt;/a&gt; to inspect every metric I could think of. I noticed that when I’d bring up my containers with this mechanism, I’d get something like:&lt;/p&gt;

&lt;div class=&quot;language-s highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;🐳&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destination&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gateway&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genmask&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Metric&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iface&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;172.24.34.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;172.24.34.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255.255.255.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then when the second network was attached:&lt;/p&gt;

&lt;div class=&quot;language-s highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;🐳&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;routing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destination&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gateway&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genmask&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flags&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Metric&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iface&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;172.24.35.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eth1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;172.24.34.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255.255.255.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;172.24.35.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255.255.255.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eth1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wait, &lt;em&gt;what&lt;/em&gt;? Why was the default gateway changing after connecting a second NIC?&lt;/p&gt;

&lt;p&gt;It turns out the answer is &lt;a href=&quot;https://docs.docker.com/v17.09/engine/userguide/networking/&quot;&gt;right there in the documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can connect and disconnect running containers from networks without restarting the container. When a container is connected to multiple networks, its &lt;strong&gt;external connectivity is provided via the first non-internal network, in lexical order&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wow, okay. Your default gateway becomes whichever network &lt;em&gt;appears first in a list sorted by name&lt;/em&gt;. Yikes.&lt;/p&gt;

&lt;p&gt;The fix was easy enough. Rather than using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app_stage&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app_stage_internal&lt;/code&gt;, I just switched my network naming convention to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BACK_app_stage&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FRONT_app_stage&lt;/code&gt;. That way, the front networks always sort after the back networks and don’t take over as the default gateway when a new network is attached. It’d also work to make the back network internal, but since my containers do occassionally depend on external network connectivity to come up (I’m &lt;a href=&quot;https://www.phusionpassenger.com/library/indepth/security_update_check.html&quot;&gt;looking at you, Passenger&lt;/a&gt;), my team needs back networks to be able to get to internet.&lt;/p&gt;

&lt;p&gt;I did find &lt;a href=&quot;https://github.com/moby/moby/issues/20179&quot;&gt;this issue&lt;/a&gt; which describes the problem (and asks for a fix), but it’s been open for about 2.5 years now. A better fix would be some way to specify the metric of the new interface during &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network connect&lt;/code&gt;, but I can’t find any way to do that, either. A third option would be to just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip&lt;/code&gt; in the container to rejigger the default route, but that’s also a no-go since the ip tool may not be available in every container.&lt;/p&gt;
</description>
        <pubDate>Sat, 29 Sep 2018 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2018/docker-default-routes/</link>
        <guid isPermaLink="true">https://chris.heald.me//2018/docker-default-routes/</guid>
        
        
        <category>Docker</category>
        
        <category>Devops</category>
        
      </item>
    
      <item>
        <title>Upgrading legacy Ruby &amp; Rails installs to support TLS 1.2</title>
        <description>&lt;p&gt;I recently had a legacy machine running an ancient Ruby 1.8.7 app which I needed to upgrade to support TLS 1.2. A few bespoke requirements made it infeasible to upgrade the core OS to a more modern version, but fortunately, it was relatively straightforward to get it back to a servicable state. While it would be &lt;em&gt;vastly&lt;/em&gt; preferable to rebuild the application for a more modern stack, it wasn’t feasible in this case, so this is the next-best option.&lt;/p&gt;

&lt;p&gt;The biggest issue with upgrading these kinds of machines is the ancient OpenSSL versions linked against tools like curl and wget, which can make it difficult to retrieve files from remote locations for upgrading! Our path forward is SCP.&lt;/p&gt;

&lt;p&gt;The tl;dr here is that we’re going to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;scp tarballs of the openssl and curl sources to the target machine&lt;/li&gt;
  &lt;li&gt;Build and install them, with Curl pointed to our newly-installed OpenSSL&lt;/li&gt;
  &lt;li&gt;Use the upgraded Curl to bootstrap (or ugprade RVM)&lt;/li&gt;
  &lt;li&gt;Use RVM to install an RVM-specific OpenSSL&lt;/li&gt;
  &lt;li&gt;Use RVM to install Ruby with our new RVM OpenSSL install&lt;/li&gt;
  &lt;li&gt;Custom-compile the Passenger agent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2 id=&quot;replacing-curl&quot;&gt;Replacing Curl&lt;/h2&gt;

&lt;p&gt;Curl’s a fundamental tool, one that we take for granted far too often. Old versions which only support TLS 1.0 can’t communicate with large parts of the web now, so our first order of business is to get a working communications line out to the rest of the web.&lt;/p&gt;

&lt;p&gt;I grabbed the &lt;a href=&quot;https://www.openssl.org/source/&quot;&gt;OpenSSL 1.0.2o tarball&lt;/a&gt; and the &lt;a href=&quot;https://curl.haxx.se/download.html&quot;&gt;curl 7.6.0 tarball&lt;/a&gt; locally, then scp’d them up to the target machine:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;scp curl-7.60.0.tar.gz user@target:~
scp openssl-1.0.2o.tar.gz user@target:~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then on my target machine, as root:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mv /home/user/curl-7.60.0.tar.gz /usr/local/src
mv /home/user/openssl-1.0.2o.tar.gz /usr/local/src
cd /usr/local/src
tar -xzf curl-7.60.0.tar.gz
tar -xzf openssl-1.0.2o.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Time to compile OpenSSL:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd openssl-1.0.2o
./configure --prefix=/opt/openssl-1.0.2o
make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And Curl:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd ../curl-7.60.0
PKG_CONFIG_PATH=/opt/openssl-1.0.2o/lib/pkgconfig ./configure
make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify that we have the right version:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl -V
curl 7.60.0 (i686-pc-linux-gnu) libcurl/7.60.0 OpenSSL/1.0.2o zlib/1.2.3
Release-Date: 2018-05-16
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP UnixSockets HTTPS-proxy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Great! We have a working lifeline to the outside world. Let’s make sure we can speak TLS 1.2:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl --tlsv1.2 -I https://google.com/
HTTP/1.1 301 Moved Permanently
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success!&lt;/p&gt;

&lt;h2 id=&quot;getting-and-installing-rvm&quot;&gt;Getting and installing RVM&lt;/h2&gt;

&lt;p&gt;Now that we have Curl, we can install RVM via the usual mechanism:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\curl -sSL https://get.rvm.io | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Relog your session, and then you have RVM ready:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rvm pkg install openssl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will custom-compile OpenSSL for RVM to use with the Ruby versions it installs. Then we just install Ruby:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rvm install ree --with-openssl-dir=/usr/local/rvm/usr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And test it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rvm use ree
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'
OpenSSL 1.0.1i 6 Aug 2014
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;passenger&quot;&gt;Passenger&lt;/h2&gt;

&lt;p&gt;Finally, we needed a updated passenger install. This is slightly fiddly because:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Newer gem versions won’t work on Ruby 1.8.7&lt;/li&gt;
  &lt;li&gt;Ruby 1.8.7 ships with a version of Rubygems that doesn’t realize that&lt;/li&gt;
  &lt;li&gt;Passenger on Ruby 1.8.7 won’t boot up apps with newer versions of Rubygems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ll work around this by doing the install with a newer Rubygems, then downgrading it for compatibility.&lt;/p&gt;

&lt;p&gt;First, upgrade Rubygems:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem install rubygems-update
update_rubygems
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then install Passenger:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem install passenger -v 5.3.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ll need to custom-compile Passenger’s agent against our custom OpenSSL version:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;EXTRA_PRE_LDFLAGS=&quot;-L/usr/local/rvm/lib&quot; EXTRA_CXXFLAGS=&quot;-I/usr/local/rvm/include&quot; EXTRA_CFLAGS=&quot;-I/usr/local/rvm/include&quot; passenger-install-nginx-module
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, downgrade Rubygems:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rvm rubygems latest-1.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Restart nginx and you should be up and running.&lt;/p&gt;
</description>
        <pubDate>Mon, 25 Jun 2018 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2018/upgrading-legacy-ruby/</link>
        <guid isPermaLink="true">https://chris.heald.me//2018/upgrading-legacy-ruby/</guid>
        
        <category>ruby</category>
        
        
      </item>
    
      <item>
        <title>faster_pathname: Making Sprockets faster</title>
        <description>&lt;p&gt;Late last year, I was working on replacing the Sprockets pipeline for our internal developers with a Guard-based solution, as a means of improving the speed
of change/reload/test cycles we’re all so familiar with. Using Guard to do our asset rebuilds was substantially faster, but I found that when I used Sprockets
to do asset lookups, things got a lot slower. This led to me digging around a bit and finding that &lt;a href=&quot;https://github.com/sstephenson/sprockets/issues/506&quot;&gt;Pathname is excrutiatingly slow&lt;/a&gt;, and Sprockets leans on it
really heavily.&lt;/p&gt;

&lt;p&gt;The Sprockets team decided to not fix the issue, but to instead just rely on Pathname less in the future. That’s good, but it doesn’t solve the problem that Sprockets is costing us many thousands of developer-hours &lt;em&gt;right now&lt;/em&gt;. So I threw together a gem to fix it, instead.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://rubygems.org/gems/faster_pathname&quot;&gt;faster_pathname&lt;/a&gt; is a gem that just monkeypatches Pathname with faster-than-default behavior. It’s primarily targeted at alleviating the issues that Sprockets tends to have, but it should result in a general performance improvement for anything that uses Pathname. I would have preferred that Sockets use a solution that subclassed Pathname with customized behavior, but since Sprockets doesn’t seem to be changing any time soon, this is a quick path to a solution.&lt;/p&gt;

&lt;p&gt;You can get the source at &lt;a href=&quot;https://github.com/cheald/faster_pathname&quot;&gt;GitHub&lt;/a&gt;. It’s very simple - just a couple of monkeypatches, and an isolated copy of the Ruby 1.9 and 2.0 pathname test suites. In my tests, it sped up Sprockets asset lookups by around 25%, and has a very noticable impact on page load times in development environments.&lt;/p&gt;

&lt;p&gt;Please note that it does not currently pass the 1.8 pathname tests, but I’ve not yet investigated that as 1.8 is officially deprecated, and I’m not a big fan of writing new software for deprecated platforms. The build currently fails on JRuby, but its test failures are consistent with stock JRuby’s test failures, so I’m confident in saying that it’s equivalently functional there, too. We’ve been running these patches (in non-gem form) across our dev team for a few months now, and have been very happy with the results.&lt;/p&gt;

&lt;p&gt;Try it out and let me know how it works for you.&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Feb 2014 00:00:00 -0600</pubDate>
        <link>https://chris.heald.me//2014/faster-pathname/</link>
        <guid isPermaLink="true">https://chris.heald.me//2014/faster-pathname/</guid>
        
        
      </item>
    
      <item>
        <title>No, Rails' CookieStore isn't broken</title>
        <description>&lt;p&gt;A post recently hit the Full Disclosure seclist titled “&lt;a href=&quot;http://seclists.org/fulldisclosure/2013/Sep/145&quot;&gt;Move away from CookieStore if you care about your users and their security&lt;/a&gt;”. The post discusses a property of session cookies - notably, that hitting “logout” doesn’t prevent a cookie from being reused to regain that session later, since if someone manages to jack one of your users’ cookies, they can just replay that cookie again at any time and gain access to the users’ account.&lt;/p&gt;

&lt;p&gt;It’s worth first noting that &lt;em&gt;this vulnerability requires your user’s session cookies to be compromised&lt;/em&gt; in the first place, so the whole vulnerability hinges on with “if your user is already owned, then…”&lt;/p&gt;

&lt;p&gt;That said, yes, if you use sessions naively, then a compromised cookie may be used to gain access to a user’s account so long as the application’s session secret hasn’t changed (thereby invalidating the cookie signature). Note that this is true for &lt;em&gt;all&lt;/em&gt; session stores, though in the case of serverside sessions, this only holds until the session gets swept (which may happen on explicit logout, but does not necessarily happen at a defined time otherwise); presuming you have some kind of session TTL in play, an attacker could keep their jacked session ID active indefinitely there, as well. A hijacked session cookie is Bad News (which is why you should be using HTTPS and HTTPS-only cookies!) no matter how you slice it.&lt;/p&gt;

&lt;p&gt;Fortunately, if you’re worried about this class of attack, mitigating it is Pretty Darn Simple.&lt;/p&gt;

&lt;p&gt;If you just want parity with serverside session stores that just perform expired session sweeps, then you can enforce a TTL on a session by just providing a TTL value in the session, and validating that when the session is read, then updating it when the session is written. You could do this trivially with a Rack middleware, or if you just want it in your app:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before_filter&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:validate_session_timestamp&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after_filter&lt;/span&gt;  &lt;span class=&quot;ss&quot;&gt;:persist_session_timestamp&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;SESSION_TTL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hours&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate_session_timestamp&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SESSION_TTL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ago&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;reset_session&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;login_path&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist_session_timestamp&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Any session that hasn’t been touched in 48 hours won’t validate and will get tossed out, same as serverside sessions (and as a bonus, you don’t have to do any session sweeping yourself! Hooray!) This does leave the cookie vulerable to TTL refreshes, so perhaps you want something more robust.&lt;/p&gt;

&lt;p&gt;Something that neither CookieStore or server-side stores can do by default is maintain a list of sessions associated with a given user, and provide the user a means to revoke access granted to previously-granted sessions. Consider the case where you walk away from a public computer having forgotten to hit “log out” - you have no means of invalidating that session from another computer. This is a problem!&lt;/p&gt;

&lt;p&gt;Fortunately, it’s trivial enough to just save a list of active sessions if desired:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Presume an active_sessions field on the model that is large enough to hold some list of sessions:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;serialize&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:active_sessions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;activate_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;active_sessions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;active_sessions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deactivate_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;active_sessions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SessionsController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;login&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activate_session&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:session_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logout&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deactivate_session&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:session_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;reset_session&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before_filter&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:validate_active_session&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate_active_session&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active_sessions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:session_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;reset_session&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;login_path&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could get more complex with this and save things like the last IP and geolocation that each session was active from, and present that to the user, GMail-style. You could enforce a maximum number of sessions that can be active at any given time. This makes it easy to let users log out other sessions:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;expire_sessions!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active_sessions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logout_other_sessions&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expire_sessions!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:session_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Redirect or whatever&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This technique is applicable to &lt;em&gt;all&lt;/em&gt; session stores, not just CookieStore (you won’t be able to get a list of sessions for a given user ID by default in ActiveRecordStore or whatever other serverside store you might want to use). You can give your users proactive control over their account security, and keep using CookieStore with all its benefits (like being invulnerable to session fixation!)&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Sep 2013 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2013/rails-session-cookies/</link>
        <guid isPermaLink="true">https://chris.heald.me//2013/rails-session-cookies/</guid>
        
        
      </item>
    
      <item>
        <title>jquery-migrate and XSS</title>
        <description>&lt;p&gt;We were recently flagged by a security researcher for an active XSS hole in our site. After bisecting the origin of the hole to the introduction of &lt;a href=&quot;https://github.com/jquery/jquery-migrate/&quot;&gt;jquery-migrate&lt;/a&gt;, I put together a minimal proof-of-concept for it and spoke to Dave Methvin on the jQuery team about it. He told me that this was not, in fact, a bug, but was working as intended. To that end, I’m publishing this to warn people about the danger of jquery-migrate’s divergent approach to this issue, so that you can be extra sure to sanitize your jQuery selectors.&lt;/p&gt;

&lt;p&gt;The proof of concept is as follows:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://code.jquery.com/jquery-migrate-1.2.1.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;a[href='&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;']&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(This particular example is a simplification of how we were persisting in-page tab selection state in a few places; I suspect it’s a semi-common use case)&lt;/p&gt;

&lt;p&gt;You can invoke it via something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://example.com/xss-poc.html#&amp;lt;img src=x onerror=prompt(1)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or you can just invoke, from a console:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;node = $(&quot;a[href='#&amp;lt;img src=x onerror=prompt(1) /&amp;gt;']&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With just bare jquery, this returns an empty set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt; - no nodes matched the selector. When jquery-migrate is present, it constructs nodes in a documentFragment (thus letting the img’s onerror fire, even if you never attach the node to the DOM), then returns the nodes, resulting in:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&amp;lt;img src=​&quot;x&quot; onerror=​&quot;prompt(1)​&quot;&amp;gt;​]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-issue&quot;&gt;The Issue&lt;/h2&gt;

&lt;p&gt;The basic problem here is that jQuery has this ambiguous syntax wherein you can actually select nodes in a document:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(&quot;foo&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or you can create nodes:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(&quot;&amp;lt;div id='foo'&amp;gt;&amp;lt;/div&amp;gt;&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In both cases, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt; function is used, and the operation is inferred by the data passed to it. jquery-migrate is doing something that breaks this inference and causes jQuery to attempt to construct a node rather than applying it as a selector in the example given:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(&quot;a[href='&amp;lt;img src=x onerror=prompt(1) /&amp;gt;']&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was unable to reproduce this exploit using only jQuery 2.0.3, 1.10.2, 1.9.1, 1.8.3, 1.7.2, or 1.6.4. However, adding jquery-migrate to any of these immediately resulted in the application becoming vulnerable to XSS, except under 1.6.4 and 1.7.2. As far as I can tell, this is because the jQuery team wants something like this to work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var node = $(&quot;bare text and node &amp;lt;img src=x /&amp;gt;&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and jquery-migrate somehow breaks something that causes it to misinterpret the a selector as a node construction. Given that this is divergent from the current jQuery core behavior, I’m not quite sure how it’s working as intended, but I’ve been assured that it is, so I feel that the best course of action is to educate people on the divergent behavior.&lt;/p&gt;

&lt;p&gt;Anyhow, all this is to say “jquery-migrate might punch XSS holes that you thought were closed”. Use with caution. And, as always, sanitize your inputs; assuming that a library that is not specifically responsible for sanitization is doing the right thing may end up leaving you in the lurch when they change something and stop sanitizing things that were previously sanitized.&lt;/p&gt;
</description>
        <pubDate>Mon, 26 Aug 2013 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2013/jquery-migrate-xss/</link>
        <guid isPermaLink="true">https://chris.heald.me//2013/jquery-migrate-xss/</guid>
        
        
      </item>
    
      <item>
        <title>Instrumenting Rails applications with ruby-prof</title>
        <description>&lt;p&gt;I recently read a blog post talking about a performance tuning tool called &lt;a href=&quot;http://ninjasandrobots.com/rails-performance-help-tracer-bullets/&quot;&gt;tracer_bullet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s already a great tool for doing this work, though, called &lt;a href=&quot;https://github.com/ruby-prof/ruby-prof&quot;&gt;ruby-prof&lt;/a&gt;. It’s a C extension, so it isn’t going to work in JRuby (but JRuby already has &lt;a href=&quot;https://github.com/jruby/jruby/wiki/Profiling-jruby&quot;&gt;great profiling tools available&lt;/a&gt;), but for people on MRI, it’ll work flawlessly.&lt;/p&gt;

&lt;h2 id=&quot;arbitrary-profiling&quot;&gt;Arbitrary Profiling&lt;/h2&gt;

&lt;p&gt;Profiling arbitrary Ruby code is as easy as something like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RubyProf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Some slow code here&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;callgrind.profile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;RubyProf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CallTreePrinter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:min_percent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means that it’s pretty easy to abstract into a helper:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;profile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RubyProf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;performance&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parameterize&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir_p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;callgrind.%s.%s.%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parameterize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parameterize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parameterize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RubyProf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CallTreePrinter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:min_percent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;helper_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:profile&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then call it from your code (anywhere, but let’s demo a view)&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% profile &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;myview&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%&amp;gt;
  # Some code here
&amp;lt;% end %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will generate the appropriate callgrind file in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp/performance&lt;/code&gt; directory.&lt;/p&gt;

&lt;h2 id=&quot;request-profiling&quot;&gt;Request Profiling&lt;/h2&gt;

&lt;p&gt;So, extending this to full requests should be pretty straightforward. You could just do it in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;around_filter&lt;/code&gt;, but there’s already a solution for you. This pattern is already wrapped up in &lt;a href=&quot;https://github.com/justinweiss/request_profiler&quot;&gt;request_profiler&lt;/a&gt; as a rack middleware. All you have to do is include it in your Gemfile:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'request_profiler'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;git://github.com/justinweiss/request_profiler.git&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you can profile a request just by adding a query parameter:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://foo.com/bar/baz?profile_request=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will generate a callgrind dump in tmp/performance, which you can then pull up with kcachegrind/qcachegrind.&lt;/p&gt;

&lt;h2 id=&quot;reading-a-dump&quot;&gt;Reading a dump&lt;/h2&gt;

&lt;p&gt;So now that you have your traces, what do you do with them? You probably want &lt;a href=&quot;http://kcachegrind.sourceforge.net/html/Home.html&quot;&gt;kcachegrind&lt;/a&gt; (for Windows/Linux) or qcachegrind (for OS X, install via homebrew).&lt;/p&gt;

&lt;p&gt;To demo, I’m using &lt;a href=&quot;https://github.com/fdv/publify&quot;&gt;publify&lt;/a&gt;, because it’s the first open source Rails app I could find. I know nothing about it. I just cloned and installed it:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/fdv/publify
cd publify
mv config/database.yml.sqlite config/database.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Added request_profile and installed:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;gem 'request_profiler', :git =&amp;gt; 'git://github.com/cheald/request_profiler.git'&quot; &amp;gt;&amp;gt; Gemfile
bundle install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add the middleware to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/environments/development.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config.middleware.insert 0, &quot;Rack::RequestProfiler&quot;, :printer =&amp;gt; ::RubyProf::CallTreePrinter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And start the server:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Logged in, and it’s time to profile!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://192.168.4.139:3000/admin?profile_request=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This generates a callgrind file for us. Opening it in KCachegrind gives us something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2013/08/perf1.png&quot;&gt;&lt;img src=&quot;/uploads/2013/08/perf1.png&quot; alt=&quot;Initial profile&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First things first, in the bottom, you’ll notice &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Total process_time cost: 420 198&lt;/code&gt;. This is in microseconds, which means that overall, this page took 420 milliseconds to run.&lt;/p&gt;

&lt;p&gt;Second, I’ve sorted by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Self&lt;/code&gt; column. This shows me which functions the action spent the most time in. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Incl.&lt;/code&gt; means the function plus its children. You’ll use both to track down slow bits.&lt;/p&gt;

&lt;p&gt;So, at the top here, you see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REXML::Attributes#get_attribute&lt;/code&gt;. It took almost 26ms, and its total runtime was 134ms, or about 32% of the total page runtime. I happen to know that REXML is slow and that you should always use Nokogiri instead because it is fast and awesome rather than slow and terrible (REXML was responsible for me almost completely writing off Ruby as a language, but that’s a story for another day).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2013/08/perf2.png&quot;&gt;&lt;img src=&quot;/uploads/2013/08/perf2.png&quot; alt=&quot;Initial profile&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the offending method gives me two intersting things here. I’m looking at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;All Callees&lt;/code&gt; view, sorted by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Distance&lt;/code&gt;. This is kind of like a stack trace, except that it includes all possible paths to this function. This is useful because it lets us figure out where in the application code this library code was invoked from. As you can see, I’ve highlighted the nearest application code to the method we’re looking at, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Admin::DashboardController#parse_rss&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I can find that in the source code easily. It’s pretty standard REXML:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_rss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;//channel/link/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;//channel/title/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;//item/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RssItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;title/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;link&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;link/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;description/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;author&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dc:publisher/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mktime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ParseDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parsedate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dc:date/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pubDate/text()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&amp;lt;\/?a\b.*?&amp;gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# remove all &amp;lt;a&amp;gt; tags&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m just going to replace that with a Nokogiri equivalent. After adding nokogiri to the Gemfile, I just added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse_rss_nokogiri&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_rss_nokogiri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Nokogiri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;XML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;//channel/link&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;//channel/title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;//item&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RssItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;link&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;author&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dc:publisher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mktime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ParseDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parsedate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dc:date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;pubDate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description_link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&amp;lt;\/?a\b.*?&amp;gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# remove all &amp;lt;a&amp;gt; tags&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then updated the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse&lt;/code&gt; method to call it:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_rss_nokogiri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s re-run the profiler and then check out the results:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2013/08/perf3.png&quot;&gt;&lt;img src=&quot;/uploads/2013/08/perf3.png&quot; alt=&quot;Improved profile&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;REXML is no longer in the callgrind trace, and because Nokogiri is so fast, it isn’t either. In fact, our overall page runtime has dropped from 430ms to 164ms - an improvement of 262%.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Using ruby-prof (and request_profiler), I was able to take an app I know literally nothing about, profile it, identify a slow spot in its default page, and improve its runtime by 262%. ruby-prof and kcachegrind rock. Use them.&lt;/p&gt;
</description>
        <pubDate>Fri, 02 Aug 2013 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2013/ruby-prof-for-rails/</link>
        <guid isPermaLink="true">https://chris.heald.me//2013/ruby-prof-for-rails/</guid>
        
        
        <category>Rails</category>
        
        <category>Performance</category>
        
      </item>
    
      <item>
        <title>Seppuqu: Self-terminating Sidekiqs</title>
        <description>&lt;p&gt;We’ve been having an issue with Sidekiq occassionally not shutting down properly during a deployment. This ends up causing issues, since it can mean that we get Sidekiq workers that end up running different code than what we expect that they’re running.&lt;/p&gt;

&lt;p&gt;I whipped up a quick little middleware that causes Sidekiq to self-terminate if it determines that it’s running a version of code that is older than the latest release. We use Capistrano, so my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sidekiq::current_release_version&lt;/code&gt; code just parses out the current version from the path, but you could use whatever versioning system you want as long as it compares &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; cleanly.&lt;/p&gt;

&lt;p&gt;At some point I’ll probably wrap this up into a little gem.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In an initializer&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Sidekiq&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current_release_version&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@current_release_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\d{10,}/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;latest_release_version&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;release_version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Middleware&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VersionEnforcerMiddleware&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lrv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;latest_release_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current_release_version&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lrv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crv&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;My version (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crv&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) mismatches latest version (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lrv&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;). Shutting down...&quot;&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Interrupt&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure_server&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;server_middleware&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Middleware&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VersionEnforcerMiddleware&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;release_version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current_release_version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 01 Aug 2013 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2013/seppuqu-self-terminating-sidekiqs/</link>
        <guid isPermaLink="true">https://chris.heald.me//2013/seppuqu-self-terminating-sidekiqs/</guid>
        
        <category>sidekiq</category>
        
        
        <category>Sidekiq</category>
        
      </item>
    
      <item>
        <title>Making MongoMapper 9x faster.</title>
        <description>&lt;p&gt;I’ve been doing some heavy work on &lt;a href=&quot;https://github.com/jnunemaker/mongomapper&quot;&gt;MongoMapper&lt;/a&gt; lately. It all started with a StackOverflow question, which led to a Rails developer user asking me what I thought of Mongoid vs MongoMapper. I’ve been using MM for ages, and was happy enough to offer a favorable opinion of it. But, I wanted to back up my assertions. I wrote a benchmark. It…was &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/pull/521&quot;&gt;disappointing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This touched off a huge amount of work which resulted in some extremely large bugfixes, massive improvements to MM’s document read speeds. Here’s how it happened.&lt;/p&gt;

&lt;h2 id=&quot;know-where-you-come-from&quot;&gt;Know Where You Come From&lt;/h2&gt;

&lt;p&gt;Initially, my goal was to prove the MongoMapper was approximately as fast at Mongoid at common use cases. I wrote a quick little benchmark, along the lines of this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MMUser&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MongoMapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;plugin&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MongoMapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;IdentityMap&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MongoidUser&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mongoid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:birthday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;LOOPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;150&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Mongomapper&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mm&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;LOOPS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MMUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Mongoid&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mongoid&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;LOOPS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MongoidUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nothing too fancy. But, imagine my dismay when I got the results:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MongoMapper Read 150000     batches of 1000       64.250000   5.950000  70.200000 ( 70.786035)
    Mongoid Read 150000     batches of 1000        5.810000   0.580000   6.390000 (  6.499160)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ouch. &lt;em&gt;Ouch&lt;/em&gt;. MongoMapper isn’t just slower, it’s an order of magnitude slower. Time to dig in. What’s going on?&lt;/p&gt;

&lt;h2 id=&quot;how-i-learned-to-stop-worrying-and-love-the-benchmark&quot;&gt;How I Learned To Stop Worrying And Love The Benchmark&lt;/h2&gt;

&lt;p&gt;One of my favorite tools for performance analysis is &lt;a href=&quot;http://kcachegrind.sourceforge.net/html/Home.html&quot;&gt;kcachegrind&lt;/a&gt;. I use Windows as my desktop, and do my development on a headless Linux server. Fortunately, kcachegrind is cross-compiled for Windows. There’s also a OS X equivalent called qcachegrind for the Mac users out there.&lt;/p&gt;

&lt;p&gt;I cut the benchmark down a bit for the purposes of iteration, and added ruby-prof profiling. This gives us something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper 30.030000  21.560000  51.590000 ( 51.948948)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So now we know where we start - about 1730 microseconds per document load.&lt;/p&gt;

&lt;p&gt;ruby-prof comes with support for cachegrind dumps baked in. I’ve written about it before, so I won’t cover it here. Instead, let’s just look at what kcachegrind says.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2013/07/mm_before.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m performing 150 loops and reading 200 documents apiece. Each document contains 5 keys (the 4 named plus _id), so I’d expect 5 * 150 * 200 = 150,000 calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt;. But instead, the first things I noticed were:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_key&lt;/code&gt; is called 600,000 times (4x per document load)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; is called 300,000 times (2x per document load)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Surely we can improve that. That sounds like a good place to start.&lt;/p&gt;

&lt;p&gt;MongoMapper uses a plugin architecture that starts with a basic implementation of a set of methods on each document, then allows plugins to override them and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt; to add additional functionality. This is nice because it helps encourage good separation of concerns, and it makes it easy to treat functionality as separate modules for the purposes of debugging. Unfortunately, this also means that a given module might not only be running the code that it thinks it is.&lt;/p&gt;

&lt;p&gt;MM does its document loads in &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/blob/v0.12.0/lib/mongo_mapper/plugins/keys.rb#L271-280&quot;&gt;keys.rb&lt;/a&gt;. This is pretty straightforward - it takes a hash of attributes, and for each attribute, calls a setter that sets the value and allows MM to do its typecasting magic. Easy, right?&lt;/p&gt;

&lt;p&gt;Well, almost. Because of the plugin architecture, our “private” method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; turns out to do an awful lot of work!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2013/07/mm_write_tree.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]=&lt;/code&gt; which calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dirty#write_key&lt;/code&gt;, which then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt; back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys::write_key&lt;/code&gt;. Let’s go look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dirty#write_key&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2013/07/mm_write_tree2.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Uh-oh. I think we found the source of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_key&lt;/code&gt; calls. What’s happening here is that MongoMapper’s dirty attributes functionality overwrites &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; so that whenever a key is written, its old value is read (that’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_key&lt;/code&gt; call), and is then compared to the value being written. If the values mismatch, then the field is marked dirty. But this is a database load! We don’t need plugins to be able to modify and act on the data - we just want our data to be set on the document.&lt;/p&gt;

&lt;p&gt;The solution I went with was to split this into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internal_write_key&lt;/code&gt; method and an overridable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; method. When loading from the database, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys&lt;/code&gt; is just concerned about getting the object populated. So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load_from_database&lt;/code&gt; now calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internal_write_key&lt;/code&gt;, and we changed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; to do the same, so we have a consistent plugin interface, but bypass letting plugins get their hooks into the data as it’s being read out of the DB.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--- a/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -284,7 +284,7 @@&lt;/span&gt; module MongoMapper
             if respond_to?(:&quot;#{key}=&quot;) &amp;amp;&amp;amp; !self.class.key?(key)
               self.send(:&quot;#{key}=&quot;, value)
             else
&lt;span class=&quot;gd&quot;&gt;-              self[key] = value
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+              internal_write_key key, value
&lt;/span&gt;             end
           end
         end
&lt;span class=&quot;p&quot;&gt;@@ -300,6 +300,10 @@&lt;/span&gt; module MongoMapper
         end

         def write_key(name, value)
&lt;span class=&quot;gi&quot;&gt;+          internal_write_key(name, value)
+        end
+
+        def internal_write_key(name, value)
&lt;/span&gt;           key         = keys[name.to_s]
           as_mongo    = key.set(value)
           as_typecast = key.get(as_mongo)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not too much, right? Let’s see what it does to performance:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper 15.420000  10.530000  25.950000 ( 26.153595)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not bad! Just cutting out the excessive reads and extra overhead due to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dirty#write_key&lt;/code&gt; improved our load time from 1730usec to 872usec - cutting our runtime in half. But we can do better!&lt;/p&gt;

&lt;p&gt;Plucky 0.5.2 &lt;a href=&quot;https://github.com/jnunemaker/plucky/pull/25&quot;&gt;had a bug in it&lt;/a&gt; that would cause cursors to be iterated twice when reading. This basically meant that MM had to parse twice the data per document load. Let’s relax the Plucky reference:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--- a/mongo_mapper.gemspec
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/mongo_mapper.gemspec
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -15,5 +15,5 @@&lt;/span&gt; Gem::Specification.new do |s|

   s.add_dependency 'activemodel',   '~&amp;gt; 3.0'
   s.add_dependency 'activesupport', '~&amp;gt; 3.0'
&lt;span class=&quot;gd&quot;&gt;-  s.add_dependency 'plucky',        '~&amp;gt; 0.5.2'
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  s.add_dependency 'plucky',        '~&amp;gt; 0.5'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That brings us to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper  8.080000   5.230000  13.310000 ( 13.426457)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Down to 443 usec per document load. Getting much better now!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2013/07/mm_step_2.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looking at a fresh callgrind trace, we see that we’re still spending a LOT of time in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; (which we expect; this is a read-from-the-database test after all), but perhaps that can be optimized?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_key&lt;/code&gt; is spending a lot of time in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys#set&lt;/code&gt;. However, the reason that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys#write_key&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Key#set&lt;/code&gt; is to cast the incoming value to a Mongo-friendly type. But, we know that we’re already coming into this with a Mongo-friendly type! We’ll add a parameter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internal_write_key&lt;/code&gt; that prevents it from trying to perform a cast in this case:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--- a/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -274,7 +274,7 @@&lt;/span&gt; module MongoMapper
             if respond_to?(:&quot;#{key}=&quot;) &amp;amp;&amp;amp; !self.class.key?(key)
               self.send(:&quot;#{key}=&quot;, value)
             else
&lt;span class=&quot;gd&quot;&gt;-              internal_write_key key, value
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+              internal_write_key key, value, false
&lt;/span&gt;             end
           end
         end
&lt;span class=&quot;p&quot;&gt;@@ -305,11 +305,11 @@&lt;/span&gt; module MongoMapper
           internal_write_key(name, value)
         end

-        def internal_write_key(name, value)
&lt;span class=&quot;gi&quot;&gt;+        def internal_write_key(name, value, cast = true)
&lt;/span&gt;           key = keys[name.to_s]
           set_parent_document(key, value)
           instance_variable_set :&quot;@#{name}_before_type_cast&quot;, value
&lt;span class=&quot;gd&quot;&gt;-          instance_variable_set :&quot;@#{name}&quot;, key.set(value)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+          instance_variable_set :&quot;@#{name}&quot;, cast ? key.set(value) : value
&lt;/span&gt;         end
     end
   end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our reward:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper  4.890000   3.560000   8.450000 (  8.531254)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Down to 284 usec/document!&lt;/p&gt;

&lt;p&gt;We’re out of obviously slow stuff, but let’s keep looking. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys::ClassMethods#keys&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys::#keys&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keys::ClassMethods#key?&lt;/code&gt; are cumulatively taking about 57 usec/document - about 20% of the total runtime!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2013/07/mm_step_3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looking in Keys::ClassMethods::key?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;key?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well, that won’t do; Ruby provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key?&lt;/code&gt; method on Hash already.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;---&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongo_mapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+++&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongo_mapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MongoMapper&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

         &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;key?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper  4.720000   3.190000   7.910000 (  7.987447)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another 22 usec/document gain.&lt;/p&gt;

&lt;p&gt;We’re making a lot of calls to the instance and class-level &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#keys&lt;/code&gt; methods. Perhaps those can be streamlined away.&lt;/p&gt;

&lt;p&gt;(This was actually a much more involved change, but it’s simplified for purposes here)&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--- a/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/lib/mongo_mapper/plugins/keys.rb
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -270,8 +270,9 @@&lt;/span&gt; module MongoMapper
       private
         def load_from_database(attrs)
           return if attrs.blank?
&lt;span class=&quot;gi&quot;&gt;+          @__keys = self.class.keys
&lt;/span&gt;           attrs.each do |key, value|
&lt;span class=&quot;gd&quot;&gt;-            if respond_to?(:&quot;#{key}=&quot;) &amp;amp;&amp;amp; !self.class.key?(key)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            if respond_to?(:&quot;#{key}=&quot;) &amp;amp;&amp;amp; !@__keys.key?(key)
&lt;/span&gt;               self.send(:&quot;#{key}=&quot;, value)
             else
               internal_write_key key, value, false
&lt;span class=&quot;p&quot;&gt;@@ -302,11 +303,12 @@&lt;/span&gt; module MongoMapper
         end

         def write_key(name, value)
&lt;span class=&quot;gi&quot;&gt;+          @__keys = self.class.keys
&lt;/span&gt;           internal_write_key(name, value)
         end

         def internal_write_key(name, value, cast = true)
&lt;span class=&quot;gd&quot;&gt;-          key = keys[name.to_s]
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+          key = @__keys[name.to_s]
&lt;/span&gt;           set_parent_document(key, value)
           instance_variable_set :&quot;@#{name}_before_type_cast&quot;, value
           instance_variable_set :&quot;@#{name}&quot;, cast ? key.set(value) : value
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And results:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper  4.200000   2.700000   6.900000 (  7.009927)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yet &lt;em&gt;another&lt;/em&gt; 29 usec/document gain.&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In Closing&lt;/h2&gt;

&lt;p&gt;There were a number of other changes, refactorings, and improvements made, and even more tested and discarded, but the fundamental procedure remains the same - measure, use those measurements to find a line of attack, and improve. The test suites will help you discover if you’ve messed something up, and your benchmarking tools can help you improve your runtimes by finding and eliminating unnecessary code paths.&lt;/p&gt;

&lt;p&gt;In the end, we ended up with something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Mongomapper  3.290000   2.410000   5.700000 (  5.761945)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Down from &lt;strong&gt;1730 usec/document&lt;/strong&gt; to &lt;strong&gt;192 usec/document&lt;/strong&gt;, a gain of ~9x, making it competitive with Mongoid. At some point it becomes difficult to do an apples-to-apples comparison, as Mongoid uses the Moped driver to talk to MongoDB, while MongoMapper still uses the 10gen Mongo driver, so there is a divergence in terms of what each ODM is doing under the covers.&lt;/p&gt;

&lt;p&gt;This is just the tip of the iceberg, but it’s the core of the optimization pass. In the process of making these changes, I &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/commit/46992503b502f432309652bb55788673087327b4&quot;&gt;converted the test suite to rspec&lt;/a&gt;, &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/commit/8e6cd50eac2e180978f556e269ded2255f23e31b&quot;&gt;made embedded associations lazy-loaded&lt;/a&gt;, &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/commit/520cc656faae99402a12a2d45cd38a454cb6dd03&quot;&gt;added finer-grained control over document marshalling&lt;/a&gt;, added &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/commit/98f264f47f01b7695cc580da90b0d4a3de7e55b6&quot;&gt;ActiveRecord-style block syntax to document creation&lt;/a&gt;, &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/commit/f001de06ca5487787862b8a94f898e5a88656a6a&quot;&gt;added key aliases&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;All of this work and more has gone into getting MongoMapper back into the race in terms of speed, and I’m proud to say that the &lt;a href=&quot;http://rubygems.org/gems/mongo_mapper/versions/0.13.0.beta1&quot;&gt;0.13.0.beta1 release&lt;/a&gt; has been published and is looking great so far. If all goes well, then we’ll be pushing 0.13.0 soon, which will be the last release in the 0.x series. Once that happens, we’ll be merging in &lt;a href=&quot;https://github.com/cheald/mongomapper/tree/rails4&quot;&gt;Rails 4 compatibility&lt;/a&gt;, making some &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/pull/529&quot;&gt;backwards-incompatible improvements to embedded associations&lt;/a&gt;, and more. Onward, to the future!&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Jul 2013 00:00:00 -0500</pubDate>
        <link>https://chris.heald.me//2013/mongomapper-performance-improvement/</link>
        <guid isPermaLink="true">https://chris.heald.me//2013/mongomapper-performance-improvement/</guid>
        
        <category>mongomapper</category>
        
        <category>plucky</category>
        
        
        <category>MongoDB</category>
        
        <category>Performance</category>
        
        <category>Ruby</category>
        
      </item>
    
  </channel>
</rss>
