blob: 51143f875be491c5369a3e76ca74ed38510ad2b3 [file] [log] [blame]
High Performance Apps with Go on App Engine
Google I/O, May 2013
David Symonds
Software Engineer, Google
dsymonds@golang.org
* Video
This talk was presented at Google I/O in May 2013.
.link http://www.youtube.com/watch?v=fc25ihfXhbg Watch the talk on YouTube
* Overview
- Why Go + App Engine
- History, Status
- Gopher Mart
- Finding performance bottlenecks
- Defer work
- Batching
- Caching
- Concurrency
- Control variance
* Why Go + App Engine
- Go compiles to native code
- App Engine is an auto-scaling low-maintenance platform
- Fastest runtime on App Engine: starts fast, runs fast
.image highperf/aegopher.jpg
* History
- Go runtime for App Engine unveiled at Google I/O 2011
- Steady growth, several high-profile users
.image highperf/appenginegophercolor.jpg
* Turkey doodle (Nov 2011)
.image highperf/turkey.png
- Written by a Go newcomer, launched in under 24 hours
- Half the latency of a Python 2.7 version
- Launched on google.com front page
[[http://golang.org/s/turkey-doodle][golang.org/s/turkey-doodle]]
* Santa Tracker (Dec 2012)
.html highperf/santaembed.html
- Nearly 5000 queries per second
- Did not make any children cry
.image highperf/santagraph.png
* Gopher Mart
* Gopher Mart
- Imagine your app is the Gopher Mart, the one-stop-shop for all your gopher needs
.image highperf/gophermart.png
* Gopher Mart
- Each checkout gopher is an app instance
- Each shopping gopher is a user request
- The scheduler directs requests to instances
- A checkout gopher can deal with one customer at a time
- Checkout gophers (instances) can be hired/fired, but there's overhead
.image highperf/gophermart2.png
* Gopher Mart
- Gophers waiting in queues get grumpy
- Let's make it fast
* Finding performance bottlenecks
- _Understand_why_your_checkout_gophers_might_work_slowly_
- Measure first
.image highperf/gopherrulespanner.png
- Measure periodically; performance characteristics changes over time
- Go 1.1 brings lots of performance improvements
* Appstats
- Appstats traces API RPCs, shows timeline
- [[http://github.com/mjibson/appstats][github.com/mjibson/appstats]]
- Gopher Mart baseline:
.image highperf/appstats1.png
* Performance Techniques
* Defer work
- Not all work needs to be done during the request
- Use `appengine/taskqueue` or `appengine/delay` to move non-critical work outside the request scope
- _Gopher_Mart_can_replace_slow_ `mail.Send` _with_quick_ `taskqueue.Add`
* Defer work II
Import `"appengine/delay"` and transform
.code highperf/mart/1/mart.go /sendReceipt/
.code highperf/mart/1/mart.go /func sendReceipt/
into
.code highperf/mart/2/mart.go /sendReceipt/
.code highperf/mart/2/mart.go /var sendReceipt/
.image highperf/appstats2.png
* Batching
- _Buying_10_boxes_of_Gopher_Flakes_is_easier_than_buying_a_single_box_10_times_
- _Calling_for_a_price_check_on_three_items_is_faster_than_three_separate_price_checks_
- Use `GetMulti` instead of `Get`, `PutMulti` instead of `Put`, etc.
.code highperf/mart/2/mart.go /Dumb load/+1,/Print items/-1
* Batching II
.code highperf/mart/3/mart.go /Batch get/+1,/}/
.image highperf/appstats3.png
* Caching
- _Checkout_gophers_can_make_notes_and_memorize_things_
- Using datastore is fine, but it can be slow
- Using memcache is good (shared among instances), but remember it can disappear
- Using local memory is okay too, but it can disappear as well
.html highperf/cachingembed.html
* Concurrency
- _A_price_check_may_delay_dealing_with_a_shopping_gopher_
- RPC-bound requests are very common, and often least-cacheable
- _Get_your_checkout_gopher_doing_something_else_while_waiting_
* Concurrency II
.code highperf/concurrency.go.notouch /func serial/+1,/^}/-1
* Concurrency III
- Run queries concurrently
- Easy way to speed up RPC-bound requests
.code highperf/concurrency.go.notouch /func parallel/+1,/^}/-1
- Visit Sameer Ajmani's talk (today, 4:25PM, Room 7)
* Control variance
- _Some_shopping_gophers_can_take_much_longer_than_others_
- Variance in request processing is very common (e.g. 99% take 10ms, 1% take 100ms)
- Some requests are harder than others
- Infrastructure is reliable, but not perfect
* Control variance II
- Effects of variance can cascade
- Variable requests make scheduling harder, requires more instances (billed)
- We want to control variance
[[http://cacm.acm.org/magazines/2013/2/160173-the-tail-at-scale/fulltext]["The Tail at Scale", Dean, Barroso; Commun. ACM 56, 2]]
* Control variance III
- For example, storing in memcache is usually an optimisation, and thus optional
- Bound time spent on optional work
- First approach: save to memcache asynchronously
.code highperf/longtail.go /long_tail_memcache_bad/+1,/^}/
- Problem: Response will be returned to user as soon as handler returns, and outstanding API calls will be canceled
* Control variance IV
- Solution: Use Go's concurrency primitives to timeout waiting
.code highperf/longtail.go /long_tail_memcache_good/+1,/^}/
* Before and After
Baseline:
.image highperf/appstats1.png
Defer work:
.image highperf/appstats2.png
Batching:
.image highperf/appstats3.png
* Summary
- Finding performance bottlenecks
- Defer work
- Batching
- Caching
- Concurrency
- Control variance
* Finally...
- [[http://golang.org][golang.org]]
- [[http://developers.google.com/appengine/docs/go/][developers.google.com/appengine/docs/go/]]
- [[http://golang.org/s/io13-ae-talk][golang.org/s/io13-ae-talk]] (this talk, plus gophermart app)
- [[http://github.com/mjibson/appstats][github.com/mjibson/appstats]]
More Go things:
- _Go_in_Production_ (_office_hours_), 3:30PM-4:15PM, Cloud Sandbox
- _Advanced_Go_Concurrency_Patterns_, 4:25PM, Room 7
- _Fireside_Chat_with_the_Go_Team_, 5:20PM, Room 2
- _Go_App_Engine_ (_office_hours_), 1:45PM-2:30PM *tomorrow*, Cloud Sandbox