9. Be Careful!
// can return nil, and that's not an error!
resourceA, err := OpenResourceA()
if err != nil {
return nil, err
}
defer resourceA.Close() // panic on nil????
10. One Solution:
// can return nil, and that's not an error!
resourceA, err := OpenResourceA()
if err != nil {
return nil, err
}
defer func(){
if resourceA != nil {
resourceA.Close()
}
}()
11. Make deferred Methods nil-Safe
// Don't forget: resource might be nil!
func (resource *Resource) Close() {
if resource != nil {
// ... clean up
}
}
12. Much Better!
// can return nil, and that's not an error!
resourceA, err := OpenResourceA()
if err != nil {
return nil, err
}
defer resourceA.Close() // will never panic!
14. Channel Axioms
1. A send to a nil channel blocks forever
2. A receive from a nil channel blocks forever
3. A send to a closed channel panics
4. A receive from a closed channel returns the zero value immediately
http://dave.cheney.net/2014/03/19/channel-axioms
17. You Can Recover From Panics
func main() {
fmt.Println("result: ", divide(1, 0))
}
func divide(a, b int) int {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
return a / b
} Run
20. Go's Race Detector:
Reports when variable access is not synchronized
Crashes with a full stack trace, including the read and write goroutines
Should be used in unit tests, development, and testing environments
21. Race Condition Code
func main() {
c := make(chan bool)
m := make(map[string]string)
go func() {
m["1"] = "a" // First conflicting access.
c <- true
}()
m["2"] = "b" // Second conflicting access.
<-c
for k, v := range m {
fmt.Println(k, v)
}
} Run
22. Race Detector Output:
$ go run -race race.go
==================
WARNING: DATA RACE
Write by goroutine 5:
runtime.mapassign1()
/usr/local/go/src/runtime/hashmap.go:383 +0x0
main.func·001()
/go/src/github.com/jaehue/go-talk/gophercon-korea-2015/race.go:10 +0xa3
Previous write by main goroutine:
runtime.mapassign1()
/usr/local/go/src/runtime/hashmap.go:383 +0x0
main.main()
/go/src/github.com/jaehue/go-talk/gophercon-korea-2015/race.go:13 +0x22e
Goroutine 5 (running) created at:
main.main()
/go/src/github.com/jaehue/go-talk/gophercon-korea-2015/race.go:12 +0x1ac
==================
2 b
1 a
Found 1 data race(s)
exit status 66
23. Enable Race Detection:
$ go test -race mypkg // to test the package
$ go run -race mysrc.go // to run the source file
$ go build -race mycmd // to build the command
$ go install -race mypkg // to install the package
30. What to Watch
How much memory does the service use when idle?
How much memory per connection?
Does the system reclaim memory that's no longer used?
What's the garbage collector doing? GODEBUG=gctrace=1
Where is memory allocated? (PPROF)
35. Use PPROF To Tell You:
How many goroutines when nobody is connected?
How many goroutines per connection?
Are all goroutines cleaned up after all connections close?
44. Managing Service Version
Version is stored in a global variable, set by your build script
In code:
var ServiceVersion string
Build script:
$ go build -ldflags
> "-X main.ServiceVersion 1.0.231.fd80bea-20150814.160801"
46. Keep Good Logs!
Create a semi-unique string per request
Use this request string as a prefix in all log entries
Always log at least the start and end of a request