{"id":2749,"date":"2012-06-08T18:37:15","date_gmt":"2012-06-08T18:37:15","guid":{"rendered":"http:\/\/www.gubatron.com\/blog\/?p=2749"},"modified":"2012-06-08T18:37:15","modified_gmt":"2012-06-08T18:37:15","slug":"deploying-html5-apps-on-cloudfront-with-efficient-invalidation-requests","status":"publish","type":"post","link":"https:\/\/www.gubatron.com\/blog\/deploying-html5-apps-on-cloudfront-with-efficient-invalidation-requests\/","title":{"rendered":"Deploying HTML5 apps on CloudFront with efficient invalidation requests"},"content":{"rendered":"<p>So you decided to build your next web app\/site using nothing but HTML5 and Javascript.<br \/>\nNo server side processing for anything related to UI.<\/p>\n<p>This means you will be coding a lot of JavaScript.<\/p>\n<p>Wouldn&#8217;t it be nice to put all that static HTML and JS on your CloudFront CDN and not deal with web servers?<\/p>\n<p>Once you deploy it&#8217;s nice, but Amazon Cloudfront is not meant for you to be invalidating objects all the time, they actually charge you $0.005 per each invalidated file.<\/p>\n<p>They tell you to version your files, but if you&#8217;re going for a no webserver based deployment, 100% cloudfront deployment, you can&#8217;t play around with url rewrites or redirects, so you&#8217;ll want to invalidate the files that have changed.<\/p>\n<p>Here&#8217;s a script to invalidate only the files that have changed.<\/p>\n<p><strong>Our configuration<\/strong><br \/>\nCloudfront pointed to a custom origin.<\/p>\n<p>The custom origin web server Document Root points to<br \/>\n\/var\/www\/mysite\/production<\/p>\n<p>&#8216;production&#8217; is a symlink pointing to a folder inside \/var\/www\/mysite, in there we have copies of the entire website saved under version numbers, for example<\/p>\n<p>[bash]\/var\/www\/mysite\/1.0.1<br \/>\n\/var\/www\/mysite\/1.0.2<br \/>\n\/var\/www\/mysite\/1.0.3 -&gt; production\/<br \/>\n\/var\/www\/mysite\/1.0.4[\/bash]<\/p>\n<p>What our script does, is it uses rsync to compare the files that changed between the current production directory (in this case 1.0.3 with the next version 1.0.4), and dump the new files on a temporary folder \/var\/www\/mysite\/tmp<\/p>\n<p>We then create a list with all the files in the \/tmp folder, and for each 1,000 files that have changed we send Cloudfront an invalidation request and move the &#8216;production&#8217; symlink to the new version.<\/p>\n<p>Instead of invalidating every file (which would be costly), we just invalidate what changed, and we don&#8217;t have to change any of our code to reflect version numbers.<\/p>\n<p>All of this is handled by command line tools that let us do this in one step (one step deployment)<\/p>\n<p>Here&#8217;s some of the python code we use to get the list of files that changed in order to build the invalidation request with the minimum files necessary so Jeff Bezos doesn&#8217;t take all our money.<\/p>\n<p>[python]<br \/>\ndef getFilesToInvalidate(config, newVersion):<br \/>\n    &#8221;&#8217;Compares the files in the new version folder with what&#8217;s currently in production.<br \/>\n    returns a list of the files that changed or got removed.&#8221;&#8217;<br \/>\n    checkKey(config,&#8217;website.exports.path&#8217;)<\/p>\n<p>    oldFiles = config[&#8216;website.exports.path&#8217;] + os.path.sep + &#8216;production&#8217;<br \/>\n    diffFiles = config[&#8216;website.exports.path&#8217;] + os.path.sep + &#8216;diff&#8217;<\/p>\n<p>    #the ..\/NEW_VERSION is important, the command needs relative paths to work<br \/>\n    #rsync -av &#8211;compare-dest=..\/NEW_VERSION \/path\/to\/OLD_VERSION \/path\/to\/TEMP_CHANGED<br \/>\n    rmDiffCmd = &#8216;rm -fr &#8216; + diffFiles<br \/>\n    cmd = &#8216;rsync -av &#8211;compare-dest=..\/%s %s\/ %s&#8217; % (newVersion,<br \/>\n                                                    oldFiles,<br \/>\n                                                    diffFiles)<br \/>\n    print cmd<br \/>\n    os.system(rmDiffCmd)<br \/>\n    os.system(cmd)<br \/>\n    diffList = getFilesInPath(diffFiles)<br \/>\n    result = []<br \/>\n    for i in xrange(len(diffList)):<br \/>\n        if diffList[i].find(&#8216;#&#8217;)!=-1:<br \/>\n            continue<br \/>\n        result.append(diffList[i].replace(diffFiles,&#8221;))<br \/>\n    os.system(rmDiffCmd)<\/p>\n<p>    return result<br \/>\n[\/python]<\/p>\n<p>Then use the &#8216;result&#8217; list to <a href=\"http:\/\/tomatohater.com\/2010\/10\/22\/cloudfront-object-invalidation-python\/\" target=\"_blank\" rel=\"nofollow\">create your CloudFront invalidation request<\/a>.<\/p>\n<p>Screw server side processing to render web pages, clients can do it now with client-side templating and javascript.<br \/>\nAnd oh Amazon, you guys need to step up your CloudFront Console features, it&#8217;d be awesome to invalidate paths and what not, it&#8217;s ultimately about what the customer needs.<\/p>\n<p>Here&#8217;s toast to a faster cloud, cheers.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So you decided to build your next web app\/site using nothing but HTML5 and Javascript. No server side processing for anything related to UI. This means you will be coding a lot of JavaScript. Wouldn&#8217;t it be nice to put all that static HTML and JS on your CloudFront CDN and not deal with web [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[15,1],"tags":[1156,1167,1168,1162,1166,1163,1165,1164],"class_list":["post-2749","post","type-post","status-publish","format-standard","hentry","category-code","category-uncategorized","tag-aws","tag-cdn","tag-cloud-development","tag-cloudfront","tag-frugal","tag-invalidation-request","tag-minimal","tag-optimal"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5Unzf-Il","jetpack-related-posts":[{"id":2747,"url":"https:\/\/www.gubatron.com\/blog\/s3cmd-how-to-put-files-with-expiration-on-s3cloudfront-aws-s3\/","url_meta":{"origin":2749,"position":0},"title":"s3cmd &#8211; how to put files with expiration on S3\/CloudFront #AWS #s3","author":"gubatron","date":"May 23, 2012","format":false,"excerpt":"[bash]s3cmd put --add-header \"Cache-Control: 604800\" -P .\/* s3:\/\/mybucket.com\/path\/to\/files\/[\/bash] If you have your CloudFront Distribution pointed to your S3 bucket, files will expire after the number of seconds specified in Cache-Control have passed. You will need the wonderful s3cmd tool","rel":"","context":"In \"aws\"","block_context":{"text":"aws","link":"https:\/\/www.gubatron.com\/blog\/tag\/aws\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1324,"url":"https:\/\/www.gubatron.com\/blog\/html-5-is-out-and-about\/","url_meta":{"origin":2749,"position":1},"title":"HTML 5 is out and about","author":"gubatron","date":"July 11, 2009","format":false,"excerpt":"I believe that the <canvas>, <audio>and <video> tags will make the web a pretty exciting place. A lot of Flash components will be rewritten or converted into Javascript+HTML5 Object components making available more reusable elements for a graphical and interactive web, all being open sourced (javascript) and with no extra\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/www.gubatron.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":4038,"url":"https:\/\/www.gubatron.com\/blog\/will-using-nice-20-on-nginx-and-php-fpm-make-my-wordpress-site-go-faster\/","url_meta":{"origin":2749,"position":2},"title":"Will using `nice -20` on nginx and php-fpm make my WordPress site go faster?","author":"gubatron","date":"June 7, 2023","format":false,"excerpt":"The nice command in Unix and Linux systems is used to alter the scheduling priority of processes. A lower nice value means higher priority. However, using nice -20 to set the highest priority for your PHP-FPM and Nginx processes isn't likely to have a significant impact on your WordPress site's\u2026","rel":"","context":"In &quot;Linux&quot;","block_context":{"text":"Linux","link":"https:\/\/www.gubatron.com\/blog\/category\/linux\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.gubatron.com\/blog\/wp-content\/uploads\/2022\/12\/progress_image_100_7fdc7b72-6c19-42f5-affe-d055d02d6f8e.webp?fit=1024%2C1024&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.gubatron.com\/blog\/wp-content\/uploads\/2022\/12\/progress_image_100_7fdc7b72-6c19-42f5-affe-d055d02d6f8e.webp?fit=1024%2C1024&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.gubatron.com\/blog\/wp-content\/uploads\/2022\/12\/progress_image_100_7fdc7b72-6c19-42f5-affe-d055d02d6f8e.webp?fit=1024%2C1024&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.gubatron.com\/blog\/wp-content\/uploads\/2022\/12\/progress_image_100_7fdc7b72-6c19-42f5-affe-d055d02d6f8e.webp?fit=1024%2C1024&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":1385,"url":"https:\/\/www.gubatron.com\/blog\/iterative-sierpinksi-fractal-drawn-on-html5-canvas\/","url_meta":{"origin":2749,"position":3},"title":"Iterative Sierpinksi Fractal (drawn on HTML5 Canvas)","author":"gubatron","date":"September 5, 2009","format":false,"excerpt":"So maybe you've seen this very famous fractal called the Sierpinksi Fractal. A few days ago I heard that you could create this fractal pattern by simply plotting out of a random sequence of mid points of an ever growing set of these mid points. So curiosity added with my\u2026","rel":"","context":"In &quot;Geeklife&quot;","block_context":{"text":"Geeklife","link":"https:\/\/www.gubatron.com\/blog\/category\/geeklife\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2121,"url":"https:\/\/www.gubatron.com\/blog\/playing-with-basics-of-html5-canvas\/","url_meta":{"origin":2749,"position":4},"title":"Playing with basics of HTML5 Canvas","author":"gubatron","date":"December 18, 2010","format":false,"excerpt":"Here's a snippet of something I did tonight to play a bit with the Canvas and 2d Graphics context objects in javascript. [html] <html> <head> <title>Playing with Canvas<\/title> <\/head> <body style=\"padding:0 0; margin: 0 0; background:black\"> <canvas id=\"myCanvas\" width=\"1024\" height=\"1024\" ><\/canvas> <script type=\"text\/javascript\"> \/\/convert to CSS friendly Hex String function\u2026","rel":"","context":"In &quot;Geeklife&quot;","block_context":{"text":"Geeklife","link":"https:\/\/www.gubatron.com\/blog\/category\/geeklife\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2126,"url":"https:\/\/www.gubatron.com\/blog\/animating-a-game-like-sky-with-html5-canvas\/","url_meta":{"origin":2749,"position":5},"title":"Animating a game-like sky with HTML5 Canvas","author":"gubatron","date":"December 28, 2010","format":false,"excerpt":"Try it | View Source Again playing a little more with HTML5 and Canvas animation. This time around the result is a little more pleasing to the eye, based on some ideas I have for a little arcade game I want to make I've created a gradient blue sky and\u2026","rel":"","context":"In &quot;AJAX&quot;","block_context":{"text":"AJAX","link":"https:\/\/www.gubatron.com\/blog\/category\/ajax\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts\/2749","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/comments?post=2749"}],"version-history":[{"count":2,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts\/2749\/revisions"}],"predecessor-version":[{"id":2752,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts\/2749\/revisions\/2752"}],"wp:attachment":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/media?parent=2749"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/categories?post=2749"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/tags?post=2749"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}