High Performance Apps with Go on App Engine

Google I/O, May 2013

David Symonds

Software Engineer, Google

Video

This talk was presented at Google I/O in May 2013.

Overview

Why Go + App Engine

History

Turkey doodle (Nov 2011)

golang.org/s/turkey-doodle

Santa Tracker (Dec 2012)

Gopher Mart

Gopher Mart

Gopher Mart

Gopher Mart

Finding performance bottlenecks

Appstats

Performance Techniques

Defer work

Defer work II

Import "appengine/delay" and transform

    sendReceipt(c, user.Current(c).Email, b.String())
func sendReceipt(c appengine.Context, dst, body string) {

into

    sendReceipt.Call(c, user.Current(c).Email, b.String())
var sendReceipt = delay.Func("send-receipt", func(c appengine.Context, dst, body string) {

Batching

    var items []*Item
    for _, key := range keys {
        item := new(Item)
        if err := datastore.Get(c, key, item); err != nil {
            // ...
        }
        items = append(items, item)
    }

Batching II

    items := make([]Item, len(keys))
    if err := datastore.GetMulti(c, keys, items); err != nil {
        // ...
    }

Caching

Small reads
datastore.Get O(20ms)
memcache.Get O(1ms)
RAM O(1┬Ás)

Concurrency

Concurrency II

    var lists []List
    var items []Item
    _, err := datastore.NewQuery("List").GetAll(c, &lists)
    if err != nil { /* ... */ }
    _, err := datastore.NewQuery("Item").GetAll(c, &items)
    if err != nil { /* ... */ }
    // write response

Concurrency III

    var lists []List
    var items []Item
    errc := make(chan error)
    go func() {
        _, err := datastore.NewQuery("List").GetAll(c, &lists)
        errc <- err
    }()
    go func() {
        _, err := datastore.NewQuery("Item").GetAll(c, &items)
        errc <- err
    }()
    err1, err2 := <-errc, <-errc
    if err1 != nil || err2 != nil { /* ... */ }
    // write response

Control variance

Control variance II

"The Tail at Scale", Dean, Barroso; Commun. ACM 56, 2

Control variance III

func myHandler(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    // ...
    // regular request handling
    // ...

    go memcache.Set(c, &memcache.Item{
        Key:   key,
        Value: data,
    })
}

Control variance IV

func myHandler(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    // ...
    // regular request handling
    // ...

    // Save to memcache, but only wait up to 3ms.
    done := make(chan bool, 1) // NB: buffered
    go func() {
        memcache.Set(c, &memcache.Item{
            Key:   key,
            Value: data,
        })
        done <- true
    }()
    select {
    case <-done:
    case <-time.After(3 * time.Millisecond):
    }
}

Before and After

Baseline:

Defer work:

Batching:

Summary

Finally...

More Go things:

Thank you

David Symonds

Software Engineer, Google

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)