Category: Technology

  • CORS woes on Heroku

    After spending the past 4 hours attempting to solve what boiled down to a rather simple problem, I figure I’d better blog about it to save someone else the time and effort.

    If you’ve been leveraging Passenger’s new –nginx-config-template command line option to add CORS headers to static assets served from a Rails app hosted on Heroku, and the CORS headers recently disappeared under mysterious circumstances… read on.

    I’ve been using the method described here to add CORS headers to custom fonts served from a Heroku-hosted Rails app that’s proxied by Nginx which handles serving static files. I recently updated to Rails 4.2.2 and suddenly, my custom fonts (.woff and .woff2 files) no longer had CORS headers on them.

    After the aforementioned hours spent scratching my head, I discovered that the latest version of the sprockets gem is generating asset digests that are 64 chars in length, where previously they had been 32. Nginx’s default regexp for identifying requests for static assets assumes the digest will be 32 chars long, like so:

    # Rails asset pipeline support.
    location ~ "^/assets/.+-[0-9a-f]{32}\..+" {
      error_page 490 = @static_asset;
      error_page 491 = @dynamic_request;
      recursive_error_pages on;</code>
    
      if (-f $request_filename) {
        return 490;
      }
      if (!-f $request_filename) {
        return 491;
      }
    }
    

    Changing the regexp to recognize digests that are 64 chars in length immediately solved the problem:

    location ~ "^/assets/.+-[0-9a-f]{64}\..+" {
       ...
    }
    

    I had to laugh after something so stupid and silly cost me a good chunk of my Saturday to debug. But at least it’s working now. My statically served custom fonts have the correct CORS headers and Chrome and Firefox are happy again.

  • Faster PDFs with wicked_pdf and delayed_job (part 3)

    In part 2 we coded our PDF generator as a background job. But the PDF is still being stored on the local file system. Let’s store it in S3 instead and give our users a URL so they can download it.

    First let’s add the AWS SDK gem to our Gemfile:

    gem "aws-sdk"
    

    Let’s define environment variables for our AWS credentials:

    AWS_ACCESS_KEY_ID=abc
    AWS_SECRET_ACCESS_KEY=secret
    

    Next we’ll modify our background job to connect to S3 and upload our PDF file instead of saving it to the local file system:

    class PdfJob < ActiveJob::Base
      def perform(html)
        pdf = WickedPdf.new.pdf_from_string(html)
        s3 = AWS::S3.new
        bucket = s3.buckets['my-bucket'] # replace with your bucket name
        bucket.objects['output.pdf'].write(pdf)
      end
    end
    

    Nice! But how do we enable our users to download the file? S3 has several options for this. One option would be to make the bucket publicly accessible. The downside to this approach is that it would allow anyone to download any PDFs stored in the bucket, regardless of who originally uploaded them. Depending on what kind of data is being included in the PDFs, this could be a bad idea.

    A better option is to generate a temporary URL. This URL can be given to a user so they can download the file, but the URL is only usable for the period of time we specify. This reduces the likelihood that the PDF will be exposed publicly. Here’s how it’s done:

    class PdfJob < ActiveJob::Base
      def perform(html)
        # ...
        obj = bucket.objects['output.pdf'].write(pdf)
        url = obj.url_for(:get, expires: 3.minutes.from_now).to_s
      end
    end
    

    Looks good. But how do we get this URL back to the user? The background job is asynchronous so it’s not like we can generate the PDF and return the string to the user all in the same HTTP request.

    A simple approach is to write the URL back into the database. Let’s introduce a new user param and update the user with the URL (this assumes the column exists on the users table):

    class PdfJob < ActiveJob::Base
      def perform(html, user)
        # ...
        url = obj.url_for(:get, s3_url_options).to_s
        user.update_attribute(:pdf_url, url)
      end
    end
    

    Now that the URL is available in the database, we can display it on the user’s profile page.

    If we want to get even fancier we can write some JavaScript that’s executed immediately after the user requests a PDF. This script would periodically poll an Ajax endpoint in our app to determine if the URL has been written to the users table yet. When it detects the URL, it would redirect the user to the URL. This would make the PDF generation process seamless from the user’s perspective.

    An example in jQuery might look something like this:

    function poll(btn) {
      $.get("http://www.our-app.com/users/123/pdf_url", function(data) {
        if (data.length > 0) {
          window.location = data;
        } else {
          setTimeout(function() { poll(btn) }, 2000);
        }
      });
    }
    

    Our controller action might look like this:

    class UsersController < ApplicationController
      def pdf_url
        user = User.find(params[:id])
        render text: user.pdf_url
      end
    end
    

    And there you have it. I hope this gave you a good idea of just how easy it can be to generate PDFs in a background job. If your site isn’t getting much traffic, it’s probably not worth going this route. But if it’s a popular site (or you expect it to be one day) it would be well worth investing the time to background this process. It’ll go a long way towards keeping your HTTP response times short, and your app will feel much snappier as a result.

  • Faster PDFs with wicked_pdf and delayed_job (part 2)

    In part 1 we learned why backgrounding is important. Now let’s dive into some code.

    First things first. Add wicked_pdf and delayed_job to your Gemfile:

    gem "wicked_pdf"
    gem "delayed_job"
    

    Now we can generate a PDF from inside our Rails app with this simple command:

    html = "<strong>Hello world!</strong>"
    pdf = WickedPdf.new.pdf_from_string(html)
    IO.write("output.pdf", pdf)</pre>
    

    You’ll notice that the more complex the HTML, the longer it takes wicked_pdf to run. That’s exactly why it’s important to run this process as a background job instead of in a web server process. A complex PDF with embedded images can take several seconds to render. That translates into several seconds of unavailability for the web process handling that particular request.

    Let’s move this code into a background job:

    class PdfJob < ActiveJob::Base
      def perform
        html = "<strong>Hello world!</strong>"
        pdf = WickedPdf.new.pdf_from_string(html)
        IO.write("output.pdf", pdf)
      end
    end
    

    Now we can queue the background job from a Rails controller like this:

    class PdfController < ApplicationController
      def generate_pdf
        PdfJob.perform_later
      end
    end
    

    The only problem is, our job isn’t doing anything particularly interesting yet. The HTML is statically defined and we’re writing out to the same file each time the job runs. Let’s make this more dynamic.

    First, let’s consider the HTML we want to generate. In a Rails app, the controller is generally responsible for rendering HTML from a given ERB template using a specific layout. There are ways to render ERB templates outside controllers, but they tend to be messy and unwieldy. In this situation, it’s perfectly reasonable to render the HTML in the controller and pass it along when we queue a job:

    class PdfController < ApplicationController
      def generate_pdf
        html = render_to_string template: "my_pdf"
        PdfJob.perform_later(html)
      end
    end
    

    This assumes an ERB template named “my_pdf.erb” exists and contains the HTML we want to convert into a PDF. Our method definition within our background job then becomes:

    class PdfJob < ActiveJob::Base
      def perform(html)
        pdf = WickedPdf.new.pdf_from_string(html)
        IO.write("output.pdf", pdf)
      end
    end
    

    delayed_job actually persists the HTML passed to the job in a database table so the job can retrieve the HTML when it gets executed. Since the job is executed asynchronously, the HTML has to be stored somewhere temporarily.

    So far, so good. The job will generate a PDF based on the HTML rendered in the controller. But how do we return this PDF back to the user when it’s ready? It turns out there are a variety of ways to do this. Saving the PDF to the file system in a publicly accessible folder is always an option. But why consume precious storage space on our own server when we can just upload to Amazon S3 instead for a few fractions of a cent?

    What’s nice about S3 is that it can be configured to automatically delete PDFs within a bucket after 24 hours. Furthermore, we can generate a temporary URL to allow a user to download a PDF directly from the S3 bucket. This temporary URL expires after a given period of time, greatly reducing the chance that a third party might access sensitive information.

    Next week I’ll demonstrate how to integrate S3 into our background job using the AWS SDK.

  • Faster PDFs with wicked_pdf and delayed_job (part 1)

    What do you get when you combine the slick PDF generation capabilities of wicked_pdf with the elegance and efficiency of delayed_job? A high performance way to convert HTML pages into beautiful PDF documents.

    I’ve been leveraging wicked_pdf to generate high school transcripts from my SaaS app, Teascript, since 2009. Prior to that I had been using Prawn which ultimately proved to lack the flexibility I needed to produce beautiful PDFs.

    wicked_pdf converts HTML pages into PDF documents using WebKit, the engine behind Apple’s Safari browser (among others). For the past few years, Teascript produced PDFs without any kind of backgrounding in place. This meant that if someone’s PDF took an unusually long time to generate, they were tying up a web server process for that entire duration.

    If multiple users generated PDFs simultaneously, it might prevent other visitors from accessing the site. Not good. Furthermore, if the PDF generation process exceeded the web server’s default timeout, the user might not ever get the PDF, just an error page.

    Any time your web app integrates with a third party API or a system process, it’s a viable candidate for backgrounding. delayed_job to the rescue. By offloading the long-running processes onto background workers, we free our web server to do what it’s best at: serving static HTML and images.

    Backgrounding isn’t a silver bullet, though. It introduces added complexity into the app, making it more vulnerable to failures. This requires writing additional code to handle these failure scenarios gracefully. But at the cost of this added complexity, we can ensure our web server stays fast and lean while our users still get the pretty PDF they want.

    Next week we’ll dive into some actual code. I’ll demonstrate how to integrate wicked_pdf with delayed_job and hook the entire thing up to your Rails app. Don’t touch that remote.

  • Unicorn vs. Passenger on Heroku

    I’ve been hosting my flagship SaaS app on Heroku since 2008. Overall it’s been a stable, if a bit overpriced, platform. Over the past year, however, I’ve been experiencing mysterious performance problems. The app runs fine for several weeks. Then suddenly I begin receiving exception reports about certain methods not being found on certain objects. Restarting my dynos would fix the problem for a few days or a few weeks, but eventually I would start getting errors again. It definitely felt like some sort of memory issue.

    After profiling the app and discovering nothing, I installed the Librato dashboard which offers a basic line graph of memory usage across dynos. I began noticing a correspondence between this line getting above 200 MB and my app throwing errors.

    Each dyno on Heroku theoretically has 512 MB of memory. I was running my app on Unicorn with 2 processes per dyno. I wouldn’t expect problems unless each process exceeded 256 MB. I was confused why I was seeing problems at just 200 MB of usage. True, the line would continue creeping up if I didn’t restart my dynos, and would eventually exceed 256 MB which would trigger an auto-restart of the dynos. But this took a long time to happen, and in the meantime my visitors were experiencing a slower app and/or outright errors.

    I spent several days attempting to identify where the app was leaking memory. Why did the memory usage line continue climbing? I tried various techniques to identify the problem but was unable to reproduce the leak on my local system. Eventually I decided a different tactic was necessary. Heroku has been recommending Puma as an alternative to Unicorn for a while now, so my first thought was to switch to Puma which uses threads for concurrency instead of processes. However, my app runs under MRI, not JRuby, so I wouldn’t necessarily be able to take advantage of those performance gains. Instead I opted for Passenger which now runs on Heroku.

    The results have been beyond what I expected. My memory usage line is now perfectly straight. No increase over time. No eventual errors and dyno restarts due to overconsumption. What Passenger is doing under the covers is spinning up new processes during high traffic periods and killing them during low traffic periods. My app has been running for 3 months now and I haven’t had to restart any of my dynos, nor have I encountered any performance issues with the app. Success!

    I can think of two explanations as to why Passenger fixed these problems: first, perhaps Unicorn itself was causing my app to leak memory in a strange way. Second, and more likely, Passenger’s built-in ability to spin up processes on demand is keeping memory leakage to a minimum due to processes regularly being refreshed. Regardless of which explanation is correct, I’m happy the app is no longer throwing errors at inconvenient times. Most importantly, my users are having a far more consistent experience. If they’re happy, I’m happy.

  • Fix Bluetooth in OS X Yosemite

    I love OS X. It’s an incredibly reliable operating system and it’s usually a joy to operate. Unfortunately, since upgrading from OS X Mavericks to Yosemite I had been plagued with Bluetooth connectivity problems:

    • My Apple keyboard would randomly disconnect from the computer. Once this happened, it became impossible to reconnect it again without restarting. Turning the keyboard off and on again wouldn’t fix it.
    • My Magic Mouse’s tracking motion would randomly become jerky and stuttering. This would last for 2 or 3 minutes and then return to normal. Turning the mouse off and on again wouldn’t fix it.
    • Devices that I hadn’t added would show up in Bluetooth Preferences as being permanently “remembered.” Whenever I would try to “forget” these devices and closed the Preferences window, they would immediately show up again after opening Bluetooth Preferences.
    • My mouse and keyboard also showed up in Preferences and could not be “forgotten.” Same as above, as soon as I removed them and closed Preferences, they would appear when I immediately opened Preferences again.

    These problems were incredibly frustrating. I did a lot of research trying to determine how best to resolve them. None of the solutions I found worked. These included:

    • Replacing the batteries in the Bluetooth device
    • Disabling and re-enabling Bluetooth
    • Clearing the PRAM
    • Resetting the SMC
    • Restarting the computer (this temporarily fixed the problems but they always came back)

    However, I believe I’ve finally fixed these strange connectivity problems for good. A couple of days ago I moved the following files to my Desktop and restarted:

    • /Library/Preferences/com.apple.Bluetooth.plist*
    • ~/Library/Preferences/com.apple.Bluetooth.plist*
    • ~/Library/Preferences/ByHost/com.apple.Bluetooth.*

    It’s important to move (not copy) the files. This forces Yosemite to re-create the files on reboot. (I could have just deleted the files but I wanted to keep them around as backups in case something went wrong.) Since doing this, my Bluetooth devices have been happily connecting and disconnecting appropriately and I have no more stuck devices in my Preferences.

  • Standing desks improve productivity

    I’ll admit it: I’ve been coveting a standing desk for years. The idea of escaping the uncomfortable prison of my 10-year-old desk chair is compelling. But standing desks are prohibitively expensive for many people, and up until recently that included me.

    My new standing deskIt finally got to be too much, though. A few weeks ago I bit the bullet and, after doing extensive research on the various options available, finally settled on the NextDesk Terra. There are many companies that manufacture standing desks, but NextDesk seems to offer the best quality-to-price ratio. I also appreciate the built-in Belkin power strip. And let’s face it, their bamboo desk surfaces are downright gorgeous.

    And the motorized raising and lowering is definitely the “wow” factor. Is it necessary? Not really. I guess the argument could be made that the motor is saving my back from having to manually crank the thing up and down, but this being my first standing desk it’s hard to say how much more difficult a manual lifting mechanism would make things. But I’m happy with the motor.

    How often do I find myself standing now that I have the correct equipment? Almost all the time. I easily spend 80% of my work day standing. What’s nice about leaving the desk in its raised position is that it’s so easy to just walk up to the workstation and get something done. My office is in my converted dining room right next to my kitchen, so I’ll often find myself wandering over to get something done while I’m waiting for some water to boil on the stove, or waiting for the microwave to finish warming a bowl of soup.

    Eliminating the need to sit before using the computer has lowered a psychological threshold that was preventing me from handling small tasks when a few minutes of free time suddenly appeared in my day. When I had to sit, I wouldn’t bother because my brain was telling me it would take longer to perform the action of sitting than it would to get something done once I did sit.

    If you’re considering upgrading to a standing desk, I recommend glancing over this article which was instrumental in my decision. Do you already use a standing desk? Comment below and let me know which model you chose and what you like best about it.

     

  • Muut for Octopress

    Muut (formerly Moot) is a lightweight, customizable commenting system. Octopress is a static blogging framework built on Jekyll. Mix the two and you have a delicious recipe enabling visitors to comment on static blog posts without having to resort to a database.

    My Octopress plugin makes this integration super easy. Install the plugin, edit 2 files, and you’re off to the races. You can even control which blog posts can be commented on.

    If you’ve never worked with Octopress before it’s well worth a look see. I use it on a couple of my blogs (The Teascript Blog and The Distance Learner) and have been very happy with the performance. Nothing beats static HTML for raw speed.

  • Trouble purchasing iPod Touch January update

    I was initially quite irritated with Apple for choosing to charge existing iPod Touch users like myself a $20 bill to get a hold of their January update. I’m still a bit ticked since it seems quite unfair to punish the early adopters like this, but by last Tuesday I had decided to push past my annoyance and plunk down the $20 for the update.

    Now I can’t seem to purchase it for some reason.

    When I visit the iTunes store or Apple’s web site and click on the link to buy the update, I get sent to a page with information about the update and a single OK button. I click OK and get sent back to the iTunes store home page. Every. Single. Time.

    I’ve called Apple a couple of times on this. They say it’s a bug on their end and that they’re working on it, but they can’t seem to give me an estimated resolution time. Apparently, this problem is only being experienced by a few lucky people, like myself.

    Has Apple lost a sale? Possibly. I want to give them my $20, but they just don’t seem to want it that badly. I may opt to use the 1.1.3 jailbreak instead (when it becomes available for the Touch).

    And I was really looking forward to using the “official” apps…

  • $100 laptop debuts this November

    Watch the inimitable David Pogue for a nice demo. OLPC’s $100 laptop debuts in the U.S. during a two-week window in November. It’s a buy one, get one deal. $400 sends one laptop to a poor kid in a third world country, and one laptop to you.

    Am I getting in on this deal? You betcha. Consider the hackability factor here. Pogue already snuck in a reference to Python in his video. Imagine the cool things that can be done with this device once Ruby is running on it. Not to mention the fact that it’s pretty indestructible.

    An extra $200 to send one to a needy child? What a deal. Count me in. What about you?