AppEngine and Resource Performance: Why you need a CDN
Google’s AppEngine is not a CDN (or Content Delivery Network).
How do I know this? Well I’m in a process of setting up a new site in anticipation of going out and doing my own thing (nothing finalized quite yet). While not really the point of this article it was the trigger for investigating AppEngine performance with regards to load times of resource files (images, css, etc).
While the results aren’t suprising, it definitely highlights the continued need for a CDN (even if you read the occasional post that tries to tell you that using AppEngine as a CDN is possible).
All this performance comparison business began when I installed the fairly bleeding edge AppEngine CMS Vosao. At first, I put most of the site specific CSS (core BlueTrip CSS files were served statically) into Vosao resources, and the site performed ok, but probably not quite as fast as I expected.
Then I remembered the blog post on 22 Michaels about their SEO strategy, and I remembered they are using AppEngine in their development citing performance as one as the primary reasons. Having a look at the code for their Shoes of Prey site (shoes aren’t really my thing, but tech is) it appears likely that their CSS, javascript and image resources are being served from static folders; and their site performance is quite acceptable.
So I moved my site specific resources out of the CMS and into static files and got an increase in performance. This was probably only in the order of a 100 – 200ms improvement for each file, but this definitely adds up.
Well, once you start down a performance investigation, it’s pretty hard to stop, so then I began to wonder how AppEngine performs against the likes of Rackspace CloudFiles. Well to say it’s like chalk and cheese would be an understatement. You will see some posts around the web suggesting how you can use AppEngine like a Content Delivery Network – just ignore them, they are just being silly. There is nothing CDN like AppEngine at all (at least not at the moment). It’s a great service and I love it, but my understanding is that there are two datacentre’s serving AppEngine applications – a primary and a backup, not a globally distributed network of edge servers. If you think I’m exaggerating have a look at the resource load times that were returned using Pingdom’s Full Page Test Tool for my 52Kb test image:

One point of note is that initial test for the Rackspace CDN from Pingdom returned a much higher time for the image when I first requested the result, and this I expect was due to the need for the image to be moved to the closest server in the CDN for subsequent deliveries. I’m actually suspecting that with response times like that, the Pingdom servers must be hosted very close to the Rackspace edge nodes indeed. I can attest to faster ping and load times on images for Rackspace hosting than CloudFront from my desktop here in Australia though also.
Feel free to have a look at the archived tests too using the following links:
Vosao managed resource Sidelab site check
AppEngine static file Sidelab site check
Amazon CloudFront CDN Sidelab site check
RackSpace CloudFiles CDN Sidelab site check
If you look at these results, you will see there is lots of work around combining CSS and javascript files, creating CSS sprites, etc. still to do; but you will also see the big change for that one particular 52Kb image. Just wait until the other files are in the RackSpace cloud.
For those of you who have gotten this far in the article, and followed a few links might have checked out my new under construction site. If not, feel free to check it out at www.sidelab.com. Like I said though, it is definitely in the construction stage and still some way away from being finished.
Update (03/03/2010)
At this stage, I am also trialling Amazon CloudFront as RackspaceCloud does not yet support using a CNAME record to point to your CloudFiles container. This is a feature that is coming in the near future, but as I require that support right now, I am going to use CloudFront to get started with. The reason I found the need to use CNAME records is that downstream filtering proxies (IronPort, etc.) may choose to filter out traffic they see coming from hosting sites based on the domain name alone. As such images I was serving off the rackspace CDN were being blocked on the site – certainly not ideal and something I would like to avoid.
Affiliate Disclaimer
Also please note, that Distractable is associated with a Rackspace affiliate program; and while the tests were conducted using RackSpace CloudFiles any genuine Content Delivery Network would offer similar performance improvements.
We believe in full disclosure, and our full list of associated affiliates is available for review.


Hi Damon
Nice post. I have to say I don’t have a lot of experience with CDNs – however, if I can make a really simple suggestion to improve page load speed, it would be to use a JavaScript/CSS compressor to concatenate all of your files into a single master JavaScript or CSS file.
That way you’re only serving 2 HTTP requests – 1 for CSS and 1 for JavaScript – instead of something like the 8 or 9 you’re currently doing.
We use YUI Compressor on Shoes of Prey – and we’ve built it into our deploy script so it all happens magically when we’re launching a new version. On a dev machine we serve out all of the individual files to make it easier to debug.
I also set my default cache expiration in AppEngine to 5 days for static files. This may actually be the default, I can’t remember….
Hope that helps!
Mike
Hey Mike,
Yep was getting around to the CSS + JS compression (I mention it but admittedly it’s right at the end of the post). In reality, I knew I was doing this a bit back to front but I’m the kind of guy that will read the last page in a book so I know where I’m going (well, not really, but that’s about the best analogy I had).
If you get the time would be really interested in hearing more about your deploy script, as I notice it appends a build ID or something to both the CSS and JS resources as part of the deployment, which I’m guessing prevents later pain with cached versions of those files.
Thanks for the response, and hope business continues to grow for you guys over @ Shoes of Prey.
Cheers,
Damon.
Ah, right – I missed that part!
An example deploy script is below. Actually, the IDs are just fake. If you change the ID part of the URL to a new ID, it will still work! And, yes, it is purely for cache busting purposes.
The dynamic ID is achieved by serving the CSS etc like so in the app.yaml file:
- url: /css/prod/([^-]+)-.*
static_files: static/css/\1.css
upload: static/css/(.*)
Okay, here is the deploy script (there is a separate tests script that will exit if the tests don’t pass).
== AppEngine deploy script ==
#!/bin/sh
export APPLICATION_ID=”my_app_id”
cd $MY_APP_DIR
# The tests all must run successfully.
./tests.sh
echo “** PUBLIC CSS…\n”
cat static/css/base.css \
static/css/file1.css \
static/css/file2.css \
static/css/etc.css > /tmp/precompile.css.tmp;
java -jar /usr/bin/yuicompressor-2.4.2.jar \
–type css /tmp/precompile.css.tmp \
-o static/css/prod.css
echo “\tCSS has been compiled (static/css/prod.css)\n”
echo “** PUBLIC JS…\n”
cat static/js/mootools-1.2.3-core.js \
static/js/base.js \
static/js/file1.js \
static/js/file2.js \
static/js/etc.js > /tmp/precompile.js.tmp
java -jar /usr/bin/yuicompressor-2.4.2.jar \
–type js /tmp/precompile.js.tmp \
-o static/js/prod.js
echo “\tJS has been compiled (static/js/prod.js)\n”
echo “** DEPLOYING…\n”
appcfg.py update $MY_APP_DIR
echo “** DONE!\n”
echo “** CLEANUP…”
rm /tmp/precompile.css.tmp
rm /tmp/precompile.js.tmp
rm $MY_APP_DIR/static/css/prod.css
rm $MY_APP_DIR/static/js/prod.js
Actually, that app.yaml line looks more complicated than it needs to be. I think it could just be:
- url: /css/prod/.*
static_files: static/css/prod.css
upload: static/css/prod.css
Thanks for the deploy script Mike – very slick as per the app.yaml tricks to serve that cache busting (like the term) CSS and JS.
I had written some groovy code for doing similar concatenation of javascript files in a project I was working on that was very javascript heavy. I’ve posted the groovy snippet as a github gist, but it’s definitely a bit raw still. I might try and clean it up and integrate it with the YUI compressor and then write a quick post on how I intend to use it.
Cheers,
Damon.
At first, the test showed about 1.2 sec with pingdom ftp
but then it drastically reduced to 950ms and then to 450ms. And my image is a lot bigger than yours. Its 244.3kb. I use appspot. something is better than nothing.
Thanks for the info Damon. I’m doing a bit of work in trying to get my sites quicker and am investigating the CloudFront route.
By the way, from a Sydney users perspective, http://www.sidelab.com loads incredibly quickly.
Thanks for the feedback Gary – glad the site loads up nice and quick (admittedly it’s not doing anything too fancy at the moment). Actually since writing this post, it’s possible CloudFront has gotten a little bit faster again for us here in Australia with the Singapore / Hong Kong edge locations coming online. Difference is probably minimal from the Japan edge nodes we would have been served from previously, but every little bit helps.