EightMegs' Page_


Why Go's Web App Tutorial Sucks


The first time I used Go professionally was to build a niche logistics web app which took customer bookings and dispatched drivers to fulfill the orders. I had done a few side projects in Go and felt relatively confident about making a web app once I'd followed the tutorial in the documentation which had the reader create a functional Wiki in under a hundred lines of code. What the tutorial failed to mention was that Go's default HTTP server lacked a time-out for interrupted connections.

func main() {
http.HandleFunc("/view/", makeHandler(viewHandler))
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))

log.Fatal(http.ListenAndServe(":8080", nil))
}

This is the offending snippet from Go's web app tutorial. It's very neat and well laid out, and really sells you on the idea that Go is the simplest and easiest way to get your web app done on time. The one crucial thing that this tutorial fails to mention is that if you use http.ListenAndServe, or any permutations thereof in actual production code, your app is a ticking time bomb that will detonate without warning when the dead connections of phone users who walked out of WiFi range or those of people who kicked out their power cables mid-request finally consume the last of your available file handles and the HTTP library decides to fail silently.

Your application is, at this point, a zombie. The listening function doesn't return an error when it can't open a new socket and neither does the database driver, they just print a plain-text error message to stderr and do... nothing. One ceases to serve pages and the other stops executing queries. In my case, the issue came to my attention when I plugged in my phone and a stream of exasperated messages flooded in from my manager complaining of an internal server error.

As soon as I could, I remoted in and restarted the application to keep things running while I tracked down the root cause. When I say I, I really mean the #go-nuts IRC channel. It ended up being a relatively easy fix, all it entailed was creating a new HTTP server struct with sensible time-outs set. If only the tutorial could've mentioned that.

//All of this pain could've been avoided if the tutorial's main had six extra lines.

func main() {
 http.HandleFunc("/view/", makeHandler(viewHandler))
 http.HandleFunc("/edit/", makeHandler(editHandler))
 http.HandleFunc("/save/", makeHandler(saveHandler))

 server:=&http.Server {
  ReadTimeout: 30*time.Second,
  WriteTimeout: 10*time.Second,
  Addr: ":8080",
 }

 log.Fatal(server.ListenAndServe())
}