Saturday, September 30, 2017

Profiling and optimizing Go web applications

Profiling and optimizing Go web applications

Install Graphviz for generating a PDF file:

# apt-get install graphviz

Use hey as the benchmark tool:

# go get -u github.com/rakyll/hey
# hey -n 100000 -c 10 http://localhost:8080

Sample code:

package main

import (
 "fmt"
 "log"
 "net/http"
 _ "net/http/pprof" // here be dragons
)

func main() {
 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello World!")
 })
 log.Fatal(http.ListenAndServe(":8080", nil))
}

If your web application is using a custom mux (HTTP request multiplexer), you will need to register a few pprof HTTP endpoints manually:

package main

import (
    "net/http"
    "net/http/pprof"
)

func hiHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hi"))
}

func main() {
    r := http.NewServeMux()
    r.HandleFunc("/", hiHandler)

    // Register pprof handlers
    r.HandleFunc("/debug/pprof/", pprof.Index)
    r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    r.HandleFunc("/debug/pprof/profile", pprof.Profile)
    r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    r.HandleFunc("/debug/pprof/trace", pprof.Trace)

    http.ListenAndServe(":8080", r)
}

CPU profile:

http://localhost:8080/debug/pprof/profile

Memory profile:

http://localhost:8080/debug/pprof/heap

Goroutine blocking profile:

http://localhost:8080/debug/pprof/block

To look at the holders of contended mutexes, after calling runtime.SetMutexProfileFraction in your program:

http://localhost:8080/debug/pprof/mutex

All goroutines with stack traces:

http://localhost:8080/debug/pprof/goroutine?debug=1

Take a trace:

http://localhost:8080/debug/pprof/trace

To view all available profiles, open:

http://localhost:8080/debug/pprof/

To look at a 30-second CPU profile:

# go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
(pprof) top
(pprof) web
(pprof) exit

Note: Run go tool pprof -h 2>&1 | less for more information.

# go tool pprof -text http://localhost:8080/debug/pprof/profile?seconds=10 | tee cpu.txt

Note: output to a text file.

# go tool pprof -pdf http://localhost:8080/debug/pprof/profile?seconds=10 > cpu.pdf

Note: output to a PDF file.

# go tool pprof -tree http://localhost:8080/debug/pprof/profile?seconds=10 > cpu.txt

Note: Outputs a text rendering of call graph.

# go tool pprof -web http://localhost:8080/debug/pprof/profile?seconds=10

Note: Visualize graph through web browser.

To collect a 5-second execution trace:

# curl -o trace.out http://192.168.1.1:8080/debug/pprof/trace?seconds=10

# go tool trace -http="127.0.0.1:6060" trace.out

Note: You can run these two commands above on a client machine (e.g., Windows)

Reference:

https://golang.org/pkg/net/http/pprof/

https://blog.golang.org/2011/06/profiling-go-programs.html

http://artem.krylysov.com/blog/2017/03/13/profiling-and-optimizing-go-web-applications/

http://mmcloughlin.com/posts/your-pprof-is-showing

https://github.com/zmap

http://blog.ralch.com/tutorial/golang-performance-and-memory-analysis/

Saturday, September 16, 2017

Slice chunking in Go

Slice chunking in Go

package main

import "fmt"

var (
 logs   = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
 numCPU = 3
)

func main() {

 var divided [][]string

 chunkSize := (len(logs) + numCPU - 1) / numCPU

 for i := 0; i < len(logs); i += chunkSize {
  end := i + chunkSize

  if end > len(logs) {
   end = len(logs)
  }

  divided = append(divided, logs[i:end])
 }

 fmt.Printf("%#v\n", divided)
}

Reference:

https://stackoverflow.com/questions/35179656/slice-chunking-in-go

Saturday, September 9, 2017

Why is a Goroutine’s stack infinite high cpu spikes:

Why is a Goroutine’s stack infinite high cpu spikes:

Empty loop:

for{
}

uses 100% of a CPU Core.

The proper way to wait for some operation depending to the use case you may use:

- sync.WaitGroup like this
- select {}
- channels
- time.Sleep
- time.After

One of the key features of Goroutines is their cost; they are cheap to create in terms of initial memory footprint (as opposed to the 1 to 8 megabytes with a traditional POSIX thread) and their stack grows and shrinks as necessary. This allows a Goroutine to start with a single 4096 byte stack which grows and shrinks as needed without the risk of ever running out.
There is however one detail I have withheld until now, which links the accidental use of a recursive function to a serious case of memory exhaustion for your operating system, and that is, when new stack pages are needed, they are allocated from the heap.
As your infinite function continues to call itself, new stack pages are allocated from the heap, permitting the function to continue to call itself over and over again. Fairly quickly the size of the heap will exceed the amount of free physical memory in your machine, at which point swapping will soon make your machine unusable.
The size of the heap available to Go programs depends on a lot of things, including the architecture of your CPU and your operating system, but it generally represents an amount of memory that exceeds the physical memory of your machine, so your machine is likely to swap heavily before your program ever exhausts its heap.

Reference:

https://stackoverflow.com/questions/39493692/difference-between-the-main-goroutine-and-spawned-goroutines-of-a-go-program

http://dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite

Trouble reading packets from a socket in Go

Trouble reading packets from a socket in Go

buf needs to have a definite size. 0-length slice won't work.

Declare it as:

var buf = make([]byte, 1024)

func handleClient(conn net.Conn) {
        defer conn.Close()

        var buf [512]byte

        for {
                n, err := conn.Read(buf[0:])

                if err != nil {
                        fmt.Printf("%v\n", err.Error())
                        return
                }

                fmt.Printf("Got: %d, %s\n", n, buf)

                str := strings.Repeat("A", 16000000)
                _, err2 := conn.Write([]byte(str))

                if err2 != nil {
                        fmt.Printf("%v\n", err.Error())
                        return
                }
        }
}

Reference:

https://stackoverflow.com/questions/2270670/trouble-reading-from-a-socket-in-go

Why can not I copy a slice with copy in golang?

Why can not I copy a slice with copy in golang?

The builtin copy(dst, src) copies min(len(dst), len(src)) elements.

Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).

So if your dst is empty (len(dst) == 0), nothing will be copied.

Try:

tmp := make([]int, len(arr))

Reference:

https://stackoverflow.com/questions/30182538/why-can-not-i-copy-a-slice-with-copy-in-golang

https://golang.org/ref/spec#Appending_and_copying_slices

docker exec is not working in cron

docker exec is not working in cron

The docker exec command says it needs "pseudo terminal and runs in interactive mode" (-it flags) while cron doesn't attach to any TTYs.

Try using the -d flag instead of -it:

# /bin/sh -c "/usr/bin/docker exec -u root -d exp_mongo_1 /bin/sh /backup.sh"

Reference:

https://stackoverflow.com/questions/37089033/docker-exec-is-not-working-in-cron

Statically compiled Go programs, always, even with cgo, using musl

Statically compiled Go programs, always, even with cgo, using musl

Reference:

https://dominik.honnef.co/posts/2015/06/go-musl/

Friday, September 8, 2017

Generic way to duplicate Go slices?

Generic way to duplicate Go slices?

One simple statement to make a shallow copy of a slice:

b := append([]T(nil), a...)

Note: The append to a nil slice translates into a make and copy.

Which is equivalent to:

b := make([]T, len(a))
copy(b, a)

Reference:

https://stackoverflow.com/questions/26433156/generic-way-to-duplicate-go-slices

cannot assign to struct field in map error

cannot assign to struct field in map error

data["p1"] isn't quite a regular addressable value: hashmaps can grow at runtime, and then their values get moved around in memory, and the old locations become outdated. If values in maps were treated as regular addressable values, those internals of the map implementation would get exposed.

So, instead, data["p1"] is a slightly different thing called a "map index expression" in the spec; if you search the spec for the phrase "index expression" you'll see you can do certain things with them, like read them, assign to them, and use them in increment/decrement expressions (for numeric types). But you can't do everything. They could have chosen to implement more special cases than they did, but I'm guessing they didn't just to keep things simple.

Issue code:

package main

import (
        "fmt"
)

type Person struct {
        Name string
}

func main() {
        data := map[string]Person{
                "p1": Person{},
        }

        data["p1"].Name = "Jun"

        fmt.Printf("%v\n", data)
}

Solution:

The solution is to make the map value a regular old pointer.

package main

import (
        "encoding/json"
        "fmt"
)

type Person struct {
        Name string
}

func main() {
        data := map[string]*Person{
                "p1": &Person{},
        }

        (*data["p1"]).Name = "Jun" // Or simply data["p1"].Name = "Jun"

        dataJSON, _ := json.MarshalIndent(data, "", "  ")
        fmt.Printf("%s\n", dataJSON)
}

Reference:

https://stackoverflow.com/questions/32751537/why-do-i-get-a-cannot-assign-error-when-setting-value-to-a-struct-as-a-value-i

Print line number for debugging in Go

Print line number for debugging in Go

Method 1:

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)

Method 2:

package main

import (
        "log"
        "runtime"
)

func MyFunc() {
        FancyHandleError()
}

func FancyHandleError() {
        // notice that we're using 1, so it will actually log the where
        // the error happened, 0 = this function, we don't want that.
        pc, fn, line, _ := runtime.Caller(1)
        log.Printf("[error] in %s[%s:%d]", runtime.FuncForPC(pc).Name(), fn, line)
}

func main() {
        MyFunc()
}

Method 3:

package main

import (
        //"log"
        //"runtime"
        "runtime/debug"
)

func MyFunc() {
        FancyHandleError()
}

func FancyHandleError() {
        debug.PrintStack()
}

func main() {
        MyFunc()
}

Reference:

https://golang.org/pkg/log/#pkg-constants

https://golang.org/pkg/runtime/debug/#PrintStack

http://stackoverflow.com/questions/24809287/how-do-you-get-a-golang-program-to-print-the-line-number-of-the-error-it-just-ca