Cameron Stokes's Blog

[ 'technologist', 'beer lover', 'foodie', 'traveler' ]

Compressed Content, Amazon S3, and CloudFront

I recently published a static website to Amazon Simple Storage Service (Amazon S3) and started looking for ways to improve performance. There weren’t any inherent performance problems using S3 by itself but I wanted to squeeze out every bit of performance I could. I first looked at typical website performance techniques such as minifying files, putting stylesheets at the top, and scripts at the bottom, but wanted to keep going. My next steps were to GZIP components and use a content delivery network. Fortunately Amazon makes both of these easy with S3 and Amazon CloudFront.

Storing GZIP content on S3 takes a mere 3 steps:

  1. GZIP content
  2. Upload to S3
  3. Set metadata on content

For my small site I ran GZIP on all of my files manually at the command-line, but this could be easily scripted. From the command-line it looks like this:

1
2
3
$ gzip -c site/index.html > compressed/index.html
$ gzip -c site/_packed/application.js > compressed/_packed/application.js
$ gzip -c site/_packed/application.css > compressed/_packed/application.css 

After uploading the files to S3 I needed to set their metadata to indicate they’re GZIP encoded. In the S3 tab within the Amazon Management Console, this is done by selecting each object and going to the Metadata tab on an object’s properties and adding the Content-Encoding key and value shown here:

At this point you can test your compressed content by opening one of your files in your browser and making sure it renders appropriately.

Distributing content on the Amazon CloudFront CDN takes even fewer steps:

  1. Setup CloudFront distribution
  2. Setup DNS CNAME record (such as www.example.com)

Back in the AWS Management Console, in the CloundFront tab, click Create Distribution and select your S3 bucket from above as your origin. Enter the CNAME you plan to use and your Default Root Object and click through the rest of the wizard. At this point Amazon distributes your configuration to its CloudFront network, which seems to take about 5-10 minutes on average. Meanwhile, Amazon will assign your CloudFront Domain Name which you should set CNAME to in your DNS provider. You’re done…

At this point your website content is compressed, stored in a bucket on S3 and distributed on CloudFront. If you use Amazon’s Route53 DNS service, your entire site is running in the cloud.

Cursory tests show significant improvements across each level of changes:

To summarize, hosting a static website on S3 and CloudFront from Amazon Web Services is not only easy but also can drastically improve performance of your site.

(If you use Amazon’s Route53 DNS service, I find www.interstate53.com to be the best management interface out there currently.)

Tasting: Grand Teton Sheep Eater

Sheep Eater Scotch Ale from Grand Teton Brewing Co. is a scotch ale from the brewery’s Cellar Reserve series. Per the brewery website, it is brewed ” to showcase the role of malt in the flavor of beer” and is brewed in adherence to the German Beer Purity Law Reinheitsgebot. Having never heard of the brewery, I was excited to try the beer.

Beer Facts

Name: Sheep Eater Scotch Ale

Style: Scotch Ale

Brewery: Grand Teton brewing Co.

Released: One-time in 2009

Availability: 1-liter, flip-top bottles

Description:

Sheep Eater Scotch Ale was brewed to showcase the role of malt in the flavor of beer and brewed in strict adherence to the Reinheitsgebot, or German Beer Purity Law. Scotch Ales are some of the world’s most flavorful beers. Scotland’s cold, blustery climate lends itself to the growing of barley and oats, but not to the production of hops, which are almost always added sparingly. The yeast must work at cooler temperatures than is customary for ales, resulting in maltier, cleaner, less fruity or estery, beers. The color often comes from black roasted malt, which imparts some dryness, but because of the lower attenuation and hopping rates, Scottish ales are almost always slightly sweet and incredibly drinkable. Ours was brewed with black roasted barley, biscuit and peat-smoked malt. It was gently hopped, fermented cool and aged cold for weeks for smoothness. It is copper-brown in color, with some sweet maltiness and plenty of body. Try it with ham, roast pork or chicken, roasted vegetables or venison, crème brulee or, most traditionally, Scottish butter shortbread cookies.

Hops: ?

Malts: Black roasted barley, biscuit malt, peat-smoked malt

OG: ?

FG: ?

IBUs: ?

ABV: 7.5%

Tasting

Serving: From a 1-liter, flip-top bottle into a Dogfish Head Signature Glass.

Appearance: Pours an opaque, copper brown. One finger of head off the first pour.

Aroma: Sweet, roasty, and smokey malt. No discernible hops or alcohol aroma.

Mouthfeel: Thick mouthfeel, but not syrupy. Medium to low amount of carbonation, the perfect amount.

Taste: Sweet malts, but not cloying. Just the right amount of roasty, smokey malt flavor. Raisins and toffee after taste. Slight alcohol presence.

Conclusion

For a brewery I’ve never heard of in the middle of nowhere Idaho, I’m damn impressed. Every aspect of the beer was spot on: the mouthfeel, complexities of flavor, and even carbonation, surprising for a flip-top bottle.

Food pairings: A hearty meal. Shepherd’s pie, meatloaf, etc.

Cellar-able: I would say yes, but Grand Teton’s Cellar Master says to drink it now. (Link)

Comparable beers: Founders Dirty Bastard.

The night we tried Sheep Eater Scotch Ale we paired it with homemade shepherd’s pie. It was a perfect combination.

Coincidentally, on our drive from Atlanta to Seattle in April we drove right by Grand Teton Brewing Co. in Victor, ID. Had known this beforehand I would have made sure they were open they were open. I’m now on the lookout for other Grand Teton beers to try, but unfortunately was told by local beer store that they no longer distribute to Washington. Hopefully this isn’t true.

Links

Fast Twitter Widget for Wordpress

tl;dr version… for loading your Twitter stream on your Wordpress blog, Reliable Twitter is a fast and reliable way to do it without impacting performance and usability of your blog.

I noticed my blog performing slowly recently and, knowing that performance impacts site engagement, I set out to fix it.

In particular, the main column loaded quickly but my Twitter stream in the right column would hang.  A quick look showed that the Twitter widget I was using, Twitter Widget Pro, was loading the stream on the server and that response times from Twitter were likely the culprit. I began looking for an AJAX-based widget knowing that loading the data asynchronously could help initial page load time without impacting usability of my blog. After too much searching, I found an AJAX-based widget, coincidentally called Reliable Twitter, that fit the bill. After installing Reliable Twitter, I immediately noticed page loads were quicker and after a bit of time Pingdom confirmed this.

Conflicting Getter Definitions With Groovy and Jackson

I’ve been piecing together Groovy and Jackson JSON Processor for a recent project (both making the project a breeze to get up and running) and quickly ran into an exception that at first glance may be a bit confusing but the fix was quite simple. The exception was:

1
2
 java.lang.IllegalArgumentException: Conflicting getter definitions for property "error": pix.Response#isError(0 params) vs pix.Response#getError(0 params)
  at ... 

The reason for the exception is that Groovy automatically generates isProperty() and getProperty() methods for boolean properties on beans. Groovy is a little too clever in this case and Jackson gives up when it sees both.

Assuming a simple Groovy bean:

1
2
3
4
5
6
 public class GroovyBean {

  boolean error = false
  String name

}

The Groovy compiler really turns this into:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 public class GroovyBean {

  boolean error = false
  String name

  def getError() {
    return error
  }

  def isError() {
    return error
  }

  ...

}

The fix is to explicitly define a getError() method in our GroovyBean class. This way the Groovy compiler won’t automatically generate both getters. (Unfortunately, the Groovy compiler still generates getError() if you define isError() so we’re stuck with getError().) Our new GroovyBean would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
 public class GroovyBean {

  boolean error = false
  String name

  def getError() {
    return error
  }

  ...

}

After this change our GroovyBean is serialized into this:

{"name":"","error":true}

I like simple fixes.

The Past 3 Months

In the past 3 months I have…

  • Flown over 45,000 miles through 10 airports
  • Driven over 1,000 miles across Europe
  • Driven over 3,000 miles across the U.S.
  • Driven through 11 states
  • Visited 10 countries across 3 continents
  • Spent over 40 nights in hotels
  • Moved from Atlanta to Seattle

I am now settled into a new home and despite the stress of the extra travel and cross-country move, it’s all been worth it.