From c00129bfb5966e005821d12b6a02f6ec516e4a6a Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Tue, 17 Nov 2015 19:47:16 +0200 Subject: [PATCH 01/16] Added initial fasthttp benchmark --- .travis.yml | 1 + frameworks/Go/fasthttp/README.md | 16 ++ frameworks/Go/fasthttp/benchmark_config.json | 51 ++++ frameworks/Go/fasthttp/setup.bat | 2 + frameworks/Go/fasthttp/setup.sh | 9 + frameworks/Go/fasthttp/setup_prefork.sh | 9 + frameworks/Go/fasthttp/source_code | 6 + frameworks/Go/fasthttp/src/hello/hello.go | 266 ++++++++++++++++++ frameworks/Go/fasthttp/templates/fortune.html | 14 + frameworks/Go/fasthttp/templates/layout.html | 9 + 10 files changed, 383 insertions(+) create mode 100644 frameworks/Go/fasthttp/README.md create mode 100644 frameworks/Go/fasthttp/benchmark_config.json create mode 100644 frameworks/Go/fasthttp/setup.bat create mode 100755 frameworks/Go/fasthttp/setup.sh create mode 100755 frameworks/Go/fasthttp/setup_prefork.sh create mode 100644 frameworks/Go/fasthttp/source_code create mode 100644 frameworks/Go/fasthttp/src/hello/hello.go create mode 100644 frameworks/Go/fasthttp/templates/fortune.html create mode 100644 frameworks/Go/fasthttp/templates/layout.html diff --git a/.travis.yml b/.travis.yml index b77f5f30997..6e4fb63ccfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ env: - "TESTDIR=Erlang/elli" - "TESTDIR=Go/beego" - "TESTDIR=Go/falcore" + - "TESTDIR=Go/fasthttp" - "TESTDIR=Go/gin" - "TESTDIR=Go/go-raw" - "TESTDIR=Go/goji" diff --git a/frameworks/Go/fasthttp/README.md b/frameworks/Go/fasthttp/README.md new file mode 100644 index 00000000000..906554e139e --- /dev/null +++ b/frameworks/Go/fasthttp/README.md @@ -0,0 +1,16 @@ +# Go Benchmarking Test + +This is the go portion of a [benchmarking test suite](../) comparing a variety of web development platforms. + +### JSON Encoding Test +* [JSON test source](src/hello/hello.go) + +## Versions + +* [fasthttp](https://github.com/valyala/fasthttp) + +## Test URLs + +### JSON Encoding Test + +http://localhost:8080/json diff --git a/frameworks/Go/fasthttp/benchmark_config.json b/frameworks/Go/fasthttp/benchmark_config.json new file mode 100644 index 00000000000..50fe29bbe6f --- /dev/null +++ b/frameworks/Go/fasthttp/benchmark_config.json @@ -0,0 +1,51 @@ +{ + "framework": "go", + "tests": [{ + "default": { + "setup_file": "setup", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortune", + "update_url": "/update?queries=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "MySQL", + "framework": "go", + "language": "Go", + "orm": "Raw", + "platform": "Go", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "fasthttp", + "notes": "", + "versus": "go" + }, + "prefork": { + "setup_file": "setup_prefork", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortune", + "update_url": "/update?queries=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "MySQL", + "framework": "go", + "language": "Go", + "orm": "Raw", + "platform": "Go", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "fasthttp", + "notes": "", + "versus": "go" + } + }] +} diff --git a/frameworks/Go/fasthttp/setup.bat b/frameworks/Go/fasthttp/setup.bat new file mode 100644 index 00000000000..6c900cc2bec --- /dev/null +++ b/frameworks/Go/fasthttp/setup.bat @@ -0,0 +1,2 @@ +set GOPATH=C:\FrameworkBenchmarks\go +go run src\hello\hello.go \ No newline at end of file diff --git a/frameworks/Go/fasthttp/setup.sh b/frameworks/Go/fasthttp/setup.sh new file mode 100755 index 00000000000..ae895d1e116 --- /dev/null +++ b/frameworks/Go/fasthttp/setup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go + +fw_depends go + +go get ./... + +go run src/hello/hello.go & diff --git a/frameworks/Go/fasthttp/setup_prefork.sh b/frameworks/Go/fasthttp/setup_prefork.sh new file mode 100755 index 00000000000..ef67810afd7 --- /dev/null +++ b/frameworks/Go/fasthttp/setup_prefork.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go + +fw_depends go + +go get ./... + +go run src/hello/hello.go -prefork & diff --git a/frameworks/Go/fasthttp/source_code b/frameworks/Go/fasthttp/source_code new file mode 100644 index 00000000000..b9f5c14c87a --- /dev/null +++ b/frameworks/Go/fasthttp/source_code @@ -0,0 +1,6 @@ +./go/src/ +./go/src/hello +./go/src/hello/hello.go +./go/templates/ +./go/templates/fortune.html +./go/templates/layout.html diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go new file mode 100644 index 00000000000..a44a887353b --- /dev/null +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -0,0 +1,266 @@ +package main + +import ( + "bytes" + "database/sql" + "encoding/json" + "flag" + "html/template" + "log" + "math/rand" + "net" + "os" + "os/exec" + "runtime" + "sort" + + _ "github.com/go-sql-driver/mysql" + "github.com/valyala/fasthttp" +) + +type Message struct { + Message string `json:"message"` +} + +type World struct { + Id uint16 `json:"id"` + RandomNumber uint16 `json:"randomNumber"` +} + +type Fortune struct { + Id uint16 `json:"id"` + Message string `json:"message"` +} + +// Databases +const ( + // Go 1.4's sql.DB has scalability problem when using (explicitly reused) prepared statement. + // https://github.com/golang/go/issues/9484 + // + // Using db.Query() instead of stmt.Query() avoid the issue. + // But it makes 3 round trips per query: prepare, execute and close. + // `interpolateParams=true` enables client side parameter interpolation. + // It reduces round trips without prepared statement. + // + // Before Go 1.5 is released, we can see real power of Go with this benchmark. + // After Go 1.5 is released, we can see prepared statement vs interpolation by comparing + // this and another lightweight Go framework. + connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?interpolateParams=true" + worldSelect = "SELECT id, randomNumber FROM World WHERE id = ?" + worldUpdate = "UPDATE World SET randomNumber = ? WHERE id = ?" + fortuneSelect = "SELECT id, message FROM Fortune;" + worldRowCount = 10000 + maxConnectionCount = 256 +) + +const helloWorldString = "Hello, World!" + +var ( + // Templates + tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html")) + + // Database + db *sql.DB + + helloWorldBytes = []byte(helloWorldString) +) + +var prefork = flag.Bool("prefork", false, "use prefork") +var child = flag.Bool("child", false, "is child proc") + +func main() { + var listener net.Listener + flag.Parse() + if !*prefork { + runtime.GOMAXPROCS(runtime.NumCPU()) + } else { + listener = doPrefork() + } + + var err error + db, err = sql.Open("mysql", connectionString) + if err != nil { + log.Fatalf("Error opening database: %v", err) + } + db.SetMaxIdleConns(maxConnectionCount) + + s := &fasthttp.Server{ + Handler: func(ctx *fasthttp.RequestCtx) { + path := ctx.Path() + switch { + case fasthttp.EqualBytesStr(path, "/plaintext"): + plaintextHandler(ctx) + case fasthttp.EqualBytesStr(path, "/json"): + jsonHandler(ctx) + case fasthttp.EqualBytesStr(path, "/db"): + dbHandler(ctx) + case fasthttp.EqualBytesStr(path, "/queries"): + dbHandler(ctx) + case fasthttp.EqualBytesStr(path, "/fortune"): + fortuneHandler(ctx) + default: + ctx.Error("unexpected path", fasthttp.StatusBadRequest) + } + }, + Name: "fasthttp", + } + if !*prefork { + s.ListenAndServe(":8080") + } else { + s.Serve(listener) + } +} + +func doPrefork() (listener net.Listener) { + var err error + var fl *os.File + var tcplistener *net.TCPListener + if !*child { + var addr *net.TCPAddr + addr, err = net.ResolveTCPAddr("tcp", ":8080") + if err != nil { + log.Fatal(err) + } + tcplistener, err = net.ListenTCP("tcp", addr) + if err != nil { + log.Fatal(err) + } + fl, err = tcplistener.File() + if err != nil { + log.Fatal(err) + } + children := make([]*exec.Cmd, runtime.NumCPU()) + for i := range children { + children[i] = exec.Command(os.Args[0], "-prefork", "-child") + children[i].Stdout = os.Stdout + children[i].Stderr = os.Stderr + children[i].ExtraFiles = []*os.File{fl} + err = children[i].Start() + if err != nil { + log.Fatal(err) + } + } + for _, ch := range children { + var err error = ch.Wait() + if err != nil { + log.Print(err) + } + } + os.Exit(0) + } else { + fl = os.NewFile(3, "") + listener, err = net.FileListener(fl) + if err != nil { + log.Fatal(err) + } + runtime.GOMAXPROCS(1) + } + return listener +} + +func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) { + body, err := json.Marshal(v) + if err != nil { + log.Fatalf("Error in json.Marshal: %s", err) + } + ctx.Success("application/json", body) +} + +// Test 1: JSON serialization +func jsonHandler(ctx *fasthttp.RequestCtx) { + jsonMarshal(ctx, &Message{helloWorldString}) +} + +// Test 2: Single database query +func dbHandler(ctx *fasthttp.RequestCtx) { + var world World + err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber) + if err != nil { + log.Fatalf("Error scanning world row: %s", err) + } + + jsonMarshal(ctx, &world) +} + +// Test 3: Multiple database queries +func queriesHandler(ctx *fasthttp.RequestCtx) { + n := ctx.QueryArgs().GetUintOrZero("queries") + if n < 1 { + n = 1 + } else if n > 500 { + n = 500 + } + + world := make([]World, n) + for i := 0; i < n; i++ { + err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber) + if err != nil { + log.Fatalf("Error scanning world row: %s", err) + } + } + + jsonMarshal(ctx, world) +} + +// Test 4: Fortunes +func fortuneHandler(ctx *fasthttp.RequestCtx) { + rows, err := db.Query(fortuneSelect) + if err != nil { + log.Fatalf("Error preparing statement: %v", err) + } + + fortunes := make(Fortunes, 0, 16) + for rows.Next() { //Fetch rows + fortune := Fortune{} + if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil { + log.Fatalf("Error scanning fortune row: %s", err) + } + fortunes = append(fortunes, &fortune) + } + rows.Close() + fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."}) + + sort.Sort(ByMessage{fortunes}) + + var w bytes.Buffer + if err := tmpl.Execute(&w, fortunes); err != nil { + log.Fatalf("Error executing fortune: %s", err) + } + ctx.Success("text/html", w.Bytes()) +} + +// Test 5: Database updates +func updateHandler(ctx *fasthttp.RequestCtx) { + n := ctx.QueryArgs().GetUintOrZero("queries") + if n < 1 { + n = 1 + } else if n > 500 { + n = 500 + } + world := make([]World, n) + for i := 0; i < n; i++ { + if err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil { + log.Fatalf("Error scanning world row: %s", err) + } + world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1) + if _, err := db.Exec(worldUpdate, world[i].RandomNumber, world[i].Id); err != nil { + log.Fatalf("Error updating world row: %s", err) + } + } + + jsonMarshal(ctx, world) +} + +// Test 6: Plaintext +func plaintextHandler(ctx *fasthttp.RequestCtx) { + ctx.Success("text/plain", helloWorldBytes) +} + +type Fortunes []*Fortune + +func (s Fortunes) Len() int { return len(s) } +func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type ByMessage struct{ Fortunes } + +func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message } diff --git a/frameworks/Go/fasthttp/templates/fortune.html b/frameworks/Go/fasthttp/templates/fortune.html new file mode 100644 index 00000000000..d6d0727d85a --- /dev/null +++ b/frameworks/Go/fasthttp/templates/fortune.html @@ -0,0 +1,14 @@ +{{define "content"}} + + + + + +{{range .}} + + + + +{{end}} +
idmessage
{{.Id}}{{.Message}}
+{{end}} diff --git a/frameworks/Go/fasthttp/templates/layout.html b/frameworks/Go/fasthttp/templates/layout.html new file mode 100644 index 00000000000..557151668f1 --- /dev/null +++ b/frameworks/Go/fasthttp/templates/layout.html @@ -0,0 +1,9 @@ + + + +Fortunes + + +{{template "content" .}} + + \ No newline at end of file From 8ede1fd83ecba3742234430d21c913b86bc9e018 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Wed, 18 Nov 2015 14:29:33 +0200 Subject: [PATCH 02/16] Optimize /json and /fortune by removing unnesessary bytes copying --- frameworks/Go/fasthttp/src/hello/hello.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index a44a887353b..7e8900cf6c4 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "database/sql" "encoding/json" "flag" @@ -159,11 +158,10 @@ func doPrefork() (listener net.Listener) { } func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) { - body, err := json.Marshal(v) - if err != nil { - log.Fatalf("Error in json.Marshal: %s", err) + ctx.SetContentType("application/json") + if err := json.NewEncoder(ctx).Encode(v); err != nil { + log.Fatalf("error in json.Encoder.Encode: %s", err) } - ctx.Success("application/json", body) } // Test 1: JSON serialization @@ -222,11 +220,10 @@ func fortuneHandler(ctx *fasthttp.RequestCtx) { sort.Sort(ByMessage{fortunes}) - var w bytes.Buffer - if err := tmpl.Execute(&w, fortunes); err != nil { + ctx.SetContentType("text/html") + if err := tmpl.Execute(ctx, fortunes); err != nil { log.Fatalf("Error executing fortune: %s", err) } - ctx.Success("text/html", w.Bytes()) } // Test 5: Database updates From 53ce2eaf2bae557c9beb6e8715b57ad0681f2731 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Wed, 18 Nov 2015 16:25:20 +0200 Subject: [PATCH 03/16] fasthttp: use prepared statements --- frameworks/Go/fasthttp/src/hello/hello.go | 52 ++++++++++++++--------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 7e8900cf6c4..394c09bc435 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -33,25 +33,17 @@ type Fortune struct { // Databases const ( - // Go 1.4's sql.DB has scalability problem when using (explicitly reused) prepared statement. - // https://github.com/golang/go/issues/9484 - // - // Using db.Query() instead of stmt.Query() avoid the issue. - // But it makes 3 round trips per query: prepare, execute and close. - // `interpolateParams=true` enables client side parameter interpolation. - // It reduces round trips without prepared statement. - // - // Before Go 1.5 is released, we can see real power of Go with this benchmark. - // After Go 1.5 is released, we can see prepared statement vs interpolation by comparing - // this and another lightweight Go framework. connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?interpolateParams=true" - worldSelect = "SELECT id, randomNumber FROM World WHERE id = ?" - worldUpdate = "UPDATE World SET randomNumber = ? WHERE id = ?" - fortuneSelect = "SELECT id, message FROM Fortune;" worldRowCount = 10000 maxConnectionCount = 256 ) +var ( + worldSelectStmt *sql.Stmt + worldUpdateStmt *sql.Stmt + fortuneSelectStmt *sql.Stmt +) + const helloWorldString = "Hello, World!" var ( @@ -79,9 +71,17 @@ func main() { var err error db, err = sql.Open("mysql", connectionString) if err != nil { - log.Fatalf("Error opening database: %v", err) + log.Fatalf("Error opening database: %s", err) + } + if err = db.Ping(); err != nil { + log.Fatalf("Cannot connect to db: %s", err) } db.SetMaxIdleConns(maxConnectionCount) + db.SetMaxOpenConns(maxConnectionCount * 2) + + worldSelectStmt = mustPrepare(db, "SELECT id, randomNumber FROM World WHERE id = ?") + worldUpdateStmt = mustPrepare(db, "UPDATE World SET randomNumber = ? WHERE id = ?") + fortuneSelectStmt = mustPrepare(db, "SELECT id, message FROM Fortune") s := &fasthttp.Server{ Handler: func(ctx *fasthttp.RequestCtx) { @@ -94,9 +94,11 @@ func main() { case fasthttp.EqualBytesStr(path, "/db"): dbHandler(ctx) case fasthttp.EqualBytesStr(path, "/queries"): - dbHandler(ctx) + queriesHandler(ctx) case fasthttp.EqualBytesStr(path, "/fortune"): fortuneHandler(ctx) + case fasthttp.EqualBytesStr(path, "/update"): + updateHandler(ctx) default: ctx.Error("unexpected path", fasthttp.StatusBadRequest) } @@ -110,6 +112,14 @@ func main() { } } +func mustPrepare(db *sql.DB, query string) *sql.Stmt { + stmt, err := db.Prepare(query) + if err != nil { + log.Fatalf("Error when preparing statement %q: %s", query, err) + } + return stmt +} + func doPrefork() (listener net.Listener) { var err error var fl *os.File @@ -172,7 +182,7 @@ func jsonHandler(ctx *fasthttp.RequestCtx) { // Test 2: Single database query func dbHandler(ctx *fasthttp.RequestCtx) { var world World - err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber) + err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber) if err != nil { log.Fatalf("Error scanning world row: %s", err) } @@ -191,7 +201,7 @@ func queriesHandler(ctx *fasthttp.RequestCtx) { world := make([]World, n) for i := 0; i < n; i++ { - err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber) + err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber) if err != nil { log.Fatalf("Error scanning world row: %s", err) } @@ -202,7 +212,7 @@ func queriesHandler(ctx *fasthttp.RequestCtx) { // Test 4: Fortunes func fortuneHandler(ctx *fasthttp.RequestCtx) { - rows, err := db.Query(fortuneSelect) + rows, err := fortuneSelectStmt.Query() if err != nil { log.Fatalf("Error preparing statement: %v", err) } @@ -236,11 +246,11 @@ func updateHandler(ctx *fasthttp.RequestCtx) { } world := make([]World, n) for i := 0; i < n; i++ { - if err := db.QueryRow(worldSelect, rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil { + if err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil { log.Fatalf("Error scanning world row: %s", err) } world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1) - if _, err := db.Exec(worldUpdate, world[i].RandomNumber, world[i].Id); err != nil { + if _, err := worldUpdateStmt.Exec(world[i].RandomNumber, world[i].Id); err != nil { log.Fatalf("Error updating world row: %s", err) } } From c5ff60056bd1bcf4fbd7951ebbe444c812238ecd Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Wed, 18 Nov 2015 16:31:11 +0200 Subject: [PATCH 04/16] fasthttp: fixed GOPATH in setup.bat --- frameworks/Go/fasthttp/setup.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/Go/fasthttp/setup.bat b/frameworks/Go/fasthttp/setup.bat index 6c900cc2bec..23a9a99c3a1 100644 --- a/frameworks/Go/fasthttp/setup.bat +++ b/frameworks/Go/fasthttp/setup.bat @@ -1,2 +1,2 @@ -set GOPATH=C:\FrameworkBenchmarks\go -go run src\hello\hello.go \ No newline at end of file +set GOPATH=C:\FrameworkBenchmarks\Go\fasthttp +go run src\hello\hello.go From bbb8a80024ff01e23e71141586f9e4581f201556 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 17:59:27 +0200 Subject: [PATCH 05/16] fasthttp: use SO_REUSEPORT if -prefork flag is passed. This should improve performance on Peak hardware --- frameworks/Go/fasthttp/src/hello/hello.go | 94 ++++++++++------------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 394c09bc435..41649cc08cd 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -15,6 +15,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/reuseport" ) type Message struct { @@ -56,12 +57,16 @@ var ( helloWorldBytes = []byte(helloWorldString) ) -var prefork = flag.Bool("prefork", false, "use prefork") -var child = flag.Bool("child", false, "is child proc") +var ( + listenAddr = flag.String("listenAddr", ":8080", "Address to listen to") + prefork = flag.Bool("prefork", false, "use prefork") + child = flag.Bool("child", false, "is child proc") +) func main() { - var listener net.Listener flag.Parse() + + var listener net.Listener if !*prefork { runtime.GOMAXPROCS(runtime.NumCPU()) } else { @@ -84,29 +89,11 @@ func main() { fortuneSelectStmt = mustPrepare(db, "SELECT id, message FROM Fortune") s := &fasthttp.Server{ - Handler: func(ctx *fasthttp.RequestCtx) { - path := ctx.Path() - switch { - case fasthttp.EqualBytesStr(path, "/plaintext"): - plaintextHandler(ctx) - case fasthttp.EqualBytesStr(path, "/json"): - jsonHandler(ctx) - case fasthttp.EqualBytesStr(path, "/db"): - dbHandler(ctx) - case fasthttp.EqualBytesStr(path, "/queries"): - queriesHandler(ctx) - case fasthttp.EqualBytesStr(path, "/fortune"): - fortuneHandler(ctx) - case fasthttp.EqualBytesStr(path, "/update"): - updateHandler(ctx) - default: - ctx.Error("unexpected path", fasthttp.StatusBadRequest) - } - }, - Name: "fasthttp", + Handler: mainHandler, + Name: "fasthttp", } if !*prefork { - s.ListenAndServe(":8080") + s.ListenAndServe(*listenAddr) } else { s.Serve(listener) } @@ -120,49 +107,30 @@ func mustPrepare(db *sql.DB, query string) *sql.Stmt { return stmt } -func doPrefork() (listener net.Listener) { - var err error - var fl *os.File - var tcplistener *net.TCPListener +func doPrefork() net.Listener { if !*child { - var addr *net.TCPAddr - addr, err = net.ResolveTCPAddr("tcp", ":8080") - if err != nil { - log.Fatal(err) - } - tcplistener, err = net.ListenTCP("tcp", addr) - if err != nil { - log.Fatal(err) - } - fl, err = tcplistener.File() - if err != nil { - log.Fatal(err) - } children := make([]*exec.Cmd, runtime.NumCPU()) for i := range children { children[i] = exec.Command(os.Args[0], "-prefork", "-child") children[i].Stdout = os.Stdout children[i].Stderr = os.Stderr - children[i].ExtraFiles = []*os.File{fl} - err = children[i].Start() - if err != nil { + if err := children[i].Start(); err != nil { log.Fatal(err) } } for _, ch := range children { - var err error = ch.Wait() - if err != nil { + if err := ch.Wait(); err != nil { log.Print(err) } } os.Exit(0) - } else { - fl = os.NewFile(3, "") - listener, err = net.FileListener(fl) - if err != nil { - log.Fatal(err) - } - runtime.GOMAXPROCS(1) + return nil + } + + runtime.GOMAXPROCS(1) + listener, err := reuseport.Listen("tcp4", *listenAddr) + if err != nil { + log.Fatal(err) } return listener } @@ -174,6 +142,26 @@ func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) { } } +func mainHandler(ctx *fasthttp.RequestCtx) { + path := ctx.Path() + switch { + case fasthttp.EqualBytesStr(path, "/plaintext"): + plaintextHandler(ctx) + case fasthttp.EqualBytesStr(path, "/json"): + jsonHandler(ctx) + case fasthttp.EqualBytesStr(path, "/db"): + dbHandler(ctx) + case fasthttp.EqualBytesStr(path, "/queries"): + queriesHandler(ctx) + case fasthttp.EqualBytesStr(path, "/fortune"): + fortuneHandler(ctx) + case fasthttp.EqualBytesStr(path, "/update"): + updateHandler(ctx) + default: + ctx.Error("unexpected path", fasthttp.StatusBadRequest) + } +} + // Test 1: JSON serialization func jsonHandler(ctx *fasthttp.RequestCtx) { jsonMarshal(ctx, &Message{helloWorldString}) From b85f19f91fddb231bfaad9382293c738cd24efb1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 18:04:44 +0200 Subject: [PATCH 06/16] Explicitly enumerate third-party packages used by fasthttp benchmark --- frameworks/Go/fasthttp/setup.sh | 3 ++- frameworks/Go/fasthttp/setup_prefork.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frameworks/Go/fasthttp/setup.sh b/frameworks/Go/fasthttp/setup.sh index ae895d1e116..ea91500e44a 100755 --- a/frameworks/Go/fasthttp/setup.sh +++ b/frameworks/Go/fasthttp/setup.sh @@ -4,6 +4,7 @@ sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go fw_depends go -go get ./... +go get -u github.com/go-sql-driver/mysql +go get -u github.com/valyala/fasthttp go run src/hello/hello.go & diff --git a/frameworks/Go/fasthttp/setup_prefork.sh b/frameworks/Go/fasthttp/setup_prefork.sh index ef67810afd7..fa509c641e8 100755 --- a/frameworks/Go/fasthttp/setup_prefork.sh +++ b/frameworks/Go/fasthttp/setup_prefork.sh @@ -4,6 +4,7 @@ sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go fw_depends go -go get ./... +go get -u github.com/go-sql-driver/mysql +go get -u github.com/valyala/fasthttp go run src/hello/hello.go -prefork & From 21728a5a00c30da19811dad34c53aa6b050c9477 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 18:38:07 +0200 Subject: [PATCH 07/16] fasthttp: properly set benchmark_config.json params --- frameworks/Go/fasthttp/benchmark_config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frameworks/Go/fasthttp/benchmark_config.json b/frameworks/Go/fasthttp/benchmark_config.json index 50fe29bbe6f..e84b7744f50 100644 --- a/frameworks/Go/fasthttp/benchmark_config.json +++ b/frameworks/Go/fasthttp/benchmark_config.json @@ -1,5 +1,5 @@ { - "framework": "go", + "framework": "fasthttp", "tests": [{ "default": { "setup_file": "setup", @@ -13,7 +13,7 @@ "approach": "Realistic", "classification": "Platform", "database": "MySQL", - "framework": "go", + "framework": "fasthttp", "language": "Go", "orm": "Raw", "platform": "Go", @@ -36,14 +36,14 @@ "approach": "Realistic", "classification": "Platform", "database": "MySQL", - "framework": "go", + "framework": "fasthttp", "language": "Go", "orm": "Raw", "platform": "Go", "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "fasthttp", + "display_name": "fasthttp-prefork", "notes": "", "versus": "go" } From 8da9a61572f6d388d1863ceba71f01295eca7640 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 18:46:16 +0200 Subject: [PATCH 08/16] fasthttp: updated README.md --- frameworks/Go/fasthttp/README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/frameworks/Go/fasthttp/README.md b/frameworks/Go/fasthttp/README.md index 906554e139e..22368548d6f 100644 --- a/frameworks/Go/fasthttp/README.md +++ b/frameworks/Go/fasthttp/README.md @@ -1,16 +1,14 @@ -# Go Benchmarking Test +# [fasthttp](https://github.com/valyala/fasthttp) (GoLang) Benchmarking Test -This is the go portion of a [benchmarking test suite](../) comparing a variety of web development platforms. +This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms. -### JSON Encoding Test -* [JSON test source](src/hello/hello.go) - -## Versions - -* [fasthttp](https://github.com/valyala/fasthttp) +"Fasthttp is a fast http package for Go." ## Test URLs -### JSON Encoding Test - -http://localhost:8080/json + http://localhost:8080/json + http://localhost:8080/db + http://localhost:8080/queries?queries=[1-500] + http://localhost:8080/fortunes + http://localhost:8080/updates?queries=[1-500] + http://localhost:8080/plaintext From ae151145d6df37d4d6b494574b69feb9d0b06787 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 19:19:15 +0200 Subject: [PATCH 09/16] fasthttp: code cleanup --- frameworks/Go/fasthttp/src/hello/hello.go | 90 ++++++++++++----------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 41649cc08cd..e526add5315 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -135,13 +135,6 @@ func doPrefork() net.Listener { return listener } -func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) { - ctx.SetContentType("application/json") - if err := json.NewEncoder(ctx).Encode(v); err != nil { - log.Fatalf("error in json.Encoder.Encode: %s", err) - } -} - func mainHandler(ctx *fasthttp.RequestCtx) { path := ctx.Path() switch { @@ -169,33 +162,21 @@ func jsonHandler(ctx *fasthttp.RequestCtx) { // Test 2: Single database query func dbHandler(ctx *fasthttp.RequestCtx) { - var world World - err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber) - if err != nil { - log.Fatalf("Error scanning world row: %s", err) - } - - jsonMarshal(ctx, &world) + var w World + fetchRandomWorld(&w) + jsonMarshal(ctx, &w) } // Test 3: Multiple database queries func queriesHandler(ctx *fasthttp.RequestCtx) { - n := ctx.QueryArgs().GetUintOrZero("queries") - if n < 1 { - n = 1 - } else if n > 500 { - n = 500 - } + n := getQueriesCount(ctx) - world := make([]World, n) + worlds := make([]World, n) for i := 0; i < n; i++ { - err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber) - if err != nil { - log.Fatalf("Error scanning world row: %s", err) - } + fetchRandomWorld(&worlds[i]) } - jsonMarshal(ctx, world) + jsonMarshal(ctx, worlds) } // Test 4: Fortunes @@ -206,12 +187,12 @@ func fortuneHandler(ctx *fasthttp.RequestCtx) { } fortunes := make(Fortunes, 0, 16) - for rows.Next() { //Fetch rows - fortune := Fortune{} - if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil { + for rows.Next() { + var f Fortune + if err := rows.Scan(&f.Id, &f.Message); err != nil { log.Fatalf("Error scanning fortune row: %s", err) } - fortunes = append(fortunes, &fortune) + fortunes = append(fortunes, &f) } rows.Close() fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."}) @@ -226,24 +207,19 @@ func fortuneHandler(ctx *fasthttp.RequestCtx) { // Test 5: Database updates func updateHandler(ctx *fasthttp.RequestCtx) { - n := ctx.QueryArgs().GetUintOrZero("queries") - if n < 1 { - n = 1 - } else if n > 500 { - n = 500 - } - world := make([]World, n) + n := getQueriesCount(ctx) + + worlds := make([]World, n) for i := 0; i < n; i++ { - if err := worldSelectStmt.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil { - log.Fatalf("Error scanning world row: %s", err) - } - world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1) - if _, err := worldUpdateStmt.Exec(world[i].RandomNumber, world[i].Id); err != nil { + w := &worlds[i] + fetchRandomWorld(w) + w.RandomNumber = uint16(randomWorldNum()) + if _, err := worldUpdateStmt.Exec(w.RandomNumber, w.Id); err != nil { log.Fatalf("Error updating world row: %s", err) } } - jsonMarshal(ctx, world) + jsonMarshal(ctx, worlds) } // Test 6: Plaintext @@ -251,6 +227,34 @@ func plaintextHandler(ctx *fasthttp.RequestCtx) { ctx.Success("text/plain", helloWorldBytes) } +func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) { + ctx.SetContentType("application/json") + if err := json.NewEncoder(ctx).Encode(v); err != nil { + log.Fatalf("error in json.Encoder.Encode: %s", err) + } +} + +func fetchRandomWorld(w *World) { + n := randomWorldNum() + if err := worldSelectStmt.QueryRow(n).Scan(&w.Id, &w.RandomNumber); err != nil { + log.Fatalf("Error scanning world row: %s", err) + } +} + +func randomWorldNum() int { + return rand.Intn(worldRowCount) + 1 +} + +func getQueriesCount(ctx *fasthttp.RequestCtx) int { + n := ctx.QueryArgs().GetUintOrZero("queries") + if n < 1 { + n = 1 + } else if n > 500 { + n = 500 + } + return n +} + type Fortunes []*Fortune func (s Fortunes) Len() int { return len(s) } From 3d07ea6d0adca6ae8776438089817689d361212f Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 19:24:15 +0200 Subject: [PATCH 10/16] fasthttp: spread available db connections between worker processes in prefork mode --- frameworks/Go/fasthttp/src/hello/hello.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index e526add5315..35ada888b90 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -81,8 +81,13 @@ func main() { if err = db.Ping(); err != nil { log.Fatalf("Cannot connect to db: %s", err) } - db.SetMaxIdleConns(maxConnectionCount) - db.SetMaxOpenConns(maxConnectionCount * 2) + + dbConnCount := maxConnectionCount + if *prefork { + dbConnCount = (dbConnCount + runtime.NumCPU()-1) / runtime.NumCPU() + } + db.SetMaxIdleConns(dbConnCount) + db.SetMaxOpenConns(dbConnCount * 2) worldSelectStmt = mustPrepare(db, "SELECT id, randomNumber FROM World WHERE id = ?") worldUpdateStmt = mustPrepare(db, "UPDATE World SET randomNumber = ? WHERE id = ?") From 8d137b4e00094917f8947b4dbaac1dbbd2d89248 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 19:37:01 +0200 Subject: [PATCH 11/16] Update world rows in a transaction --- frameworks/Go/fasthttp/src/hello/hello.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 35ada888b90..e95b59196a3 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -84,7 +84,7 @@ func main() { dbConnCount := maxConnectionCount if *prefork { - dbConnCount = (dbConnCount + runtime.NumCPU()-1) / runtime.NumCPU() + dbConnCount = (dbConnCount + runtime.NumCPU() - 1) / runtime.NumCPU() } db.SetMaxIdleConns(dbConnCount) db.SetMaxOpenConns(dbConnCount * 2) @@ -219,10 +219,22 @@ func updateHandler(ctx *fasthttp.RequestCtx) { w := &worlds[i] fetchRandomWorld(w) w.RandomNumber = uint16(randomWorldNum()) - if _, err := worldUpdateStmt.Exec(w.RandomNumber, w.Id); err != nil { - log.Fatalf("Error updating world row: %s", err) + } + + txn, err := db.Begin() + if err != nil { + log.Fatalf("Error starting transaction: %s", err) + } + stmt := txn.Stmt(worldUpdateStmt) + for i := 0; i < n; i++ { + w := &worlds[i] + if _, err := stmt.Exec(w.RandomNumber, w.Id); err != nil { + log.Fatalf("Error updating world row %d: %s", i, err) } } + if err = txn.Commit(); err != nil { + log.Fatalf("Error when commiting world rows: %s", err) + } jsonMarshal(ctx, worlds) } From e39aa67fa918fcbfed58369813cd8b8395c7a3cc Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 20 Nov 2015 20:02:03 +0200 Subject: [PATCH 12/16] fasthttp: code prettifying --- frameworks/Go/fasthttp/src/hello/hello.go | 98 +++++++++++------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index e95b59196a3..2a59e065d18 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -66,16 +66,8 @@ var ( func main() { flag.Parse() - var listener net.Listener - if !*prefork { - runtime.GOMAXPROCS(runtime.NumCPU()) - } else { - listener = doPrefork() - } - var err error - db, err = sql.Open("mysql", connectionString) - if err != nil { + if db, err = sql.Open("mysql", connectionString); err != nil { log.Fatalf("Error opening database: %s", err) } if err = db.Ping(); err != nil { @@ -97,47 +89,10 @@ func main() { Handler: mainHandler, Name: "fasthttp", } - if !*prefork { - s.ListenAndServe(*listenAddr) - } else { - s.Serve(listener) - } -} - -func mustPrepare(db *sql.DB, query string) *sql.Stmt { - stmt, err := db.Prepare(query) - if err != nil { - log.Fatalf("Error when preparing statement %q: %s", query, err) - } - return stmt -} - -func doPrefork() net.Listener { - if !*child { - children := make([]*exec.Cmd, runtime.NumCPU()) - for i := range children { - children[i] = exec.Command(os.Args[0], "-prefork", "-child") - children[i].Stdout = os.Stdout - children[i].Stderr = os.Stderr - if err := children[i].Start(); err != nil { - log.Fatal(err) - } - } - for _, ch := range children { - if err := ch.Wait(); err != nil { - log.Print(err) - } - } - os.Exit(0) - return nil - } - - runtime.GOMAXPROCS(1) - listener, err := reuseport.Listen("tcp4", *listenAddr) - if err != nil { - log.Fatal(err) + ln := getListener() + if err = s.Serve(ln); err != nil { + log.Fatalf("Error when serving incoming connections: %s", err) } - return listener } func mainHandler(ctx *fasthttp.RequestCtx) { @@ -280,3 +235,48 @@ func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } type ByMessage struct{ Fortunes } func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message } + +func mustPrepare(db *sql.DB, query string) *sql.Stmt { + stmt, err := db.Prepare(query) + if err != nil { + log.Fatalf("Error when preparing statement %q: %s", query, err) + } + return stmt +} + +func getListener() net.Listener { + if !*prefork { + runtime.GOMAXPROCS(runtime.NumCPU()) + ln, err := net.Listen("tcp4", *listenAddr) + if err != nil { + log.Fatal(err) + } + return ln + } + + if !*child { + children := make([]*exec.Cmd, runtime.NumCPU()) + for i := range children { + children[i] = exec.Command(os.Args[0], "-prefork", "-child") + children[i].Stdout = os.Stdout + children[i].Stderr = os.Stderr + if err := children[i].Start(); err != nil { + log.Fatal(err) + } + } + for _, ch := range children { + if err := ch.Wait(); err != nil { + log.Print(err) + } + } + os.Exit(0) + panic("unreachable") + } + + runtime.GOMAXPROCS(1) + ln, err := reuseport.Listen("tcp4", *listenAddr) + if err != nil { + log.Fatal(err) + } + return ln +} From 2c1a83a980494996c4ab3f221b37bfad31d8ee42 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Sun, 22 Nov 2015 03:26:28 +0200 Subject: [PATCH 13/16] fasthttp: fixed 'Deadlock found when trying to get lock; try restarting transaction' error by sorting rows by id before updating them in a transaction --- frameworks/Go/fasthttp/src/hello/hello.go | 25 ++++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 2a59e065d18..7796e58dcc5 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -34,7 +34,7 @@ type Fortune struct { // Databases const ( - connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?interpolateParams=true" + connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world" worldRowCount = 10000 maxConnectionCount = 256 ) @@ -146,18 +146,18 @@ func fortuneHandler(ctx *fasthttp.RequestCtx) { log.Fatalf("Error preparing statement: %v", err) } - fortunes := make(Fortunes, 0, 16) + fortunes := make([]Fortune, 0, 16) for rows.Next() { var f Fortune if err := rows.Scan(&f.Id, &f.Message); err != nil { log.Fatalf("Error scanning fortune row: %s", err) } - fortunes = append(fortunes, &f) + fortunes = append(fortunes, f) } rows.Close() - fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."}) + fortunes = append(fortunes, Fortune{Message: "Additional fortune added at request time."}) - sort.Sort(ByMessage{fortunes}) + sort.Sort(FortunesByMessage(fortunes)) ctx.SetContentType("text/html") if err := tmpl.Execute(ctx, fortunes); err != nil { @@ -176,6 +176,8 @@ func updateHandler(ctx *fasthttp.RequestCtx) { w.RandomNumber = uint16(randomWorldNum()) } + // sorting is required for insert deadlock prevention. + sort.Sort(WorldsByID(worlds)) txn, err := db.Begin() if err != nil { log.Fatalf("Error starting transaction: %s", err) @@ -227,14 +229,17 @@ func getQueriesCount(ctx *fasthttp.RequestCtx) int { return n } -type Fortunes []*Fortune +type FortunesByMessage []Fortune -func (s Fortunes) Len() int { return len(s) } -func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s FortunesByMessage) Len() int { return len(s) } +func (s FortunesByMessage) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s FortunesByMessage) Less(i, j int) bool { return s[i].Message < s[j].Message } -type ByMessage struct{ Fortunes } +type WorldsByID []World -func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message } +func (w WorldsByID) Len() int { return len(w) } +func (w WorldsByID) Swap(i, j int) { w[i], w[j] = w[j], w[i] } +func (w WorldsByID) Less(i, j int) bool { return w[i].Id < w[j].Id } func mustPrepare(db *sql.DB, query string) *sql.Stmt { stmt, err := db.Prepare(query) From 27bbc81c2fe06d5604e7f22595d6be12680b476d Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 30 Nov 2015 18:38:05 +0200 Subject: [PATCH 14/16] fasthttp: updated stale error message --- frameworks/Go/fasthttp/src/hello/hello.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frameworks/Go/fasthttp/src/hello/hello.go b/frameworks/Go/fasthttp/src/hello/hello.go index 7796e58dcc5..6a6d804d7f3 100644 --- a/frameworks/Go/fasthttp/src/hello/hello.go +++ b/frameworks/Go/fasthttp/src/hello/hello.go @@ -32,7 +32,6 @@ type Fortune struct { Message string `json:"message"` } -// Databases const ( connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world" worldRowCount = 10000 @@ -48,10 +47,8 @@ var ( const helloWorldString = "Hello, World!" var ( - // Templates tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html")) - // Database db *sql.DB helloWorldBytes = []byte(helloWorldString) @@ -143,7 +140,7 @@ func queriesHandler(ctx *fasthttp.RequestCtx) { func fortuneHandler(ctx *fasthttp.RequestCtx) { rows, err := fortuneSelectStmt.Query() if err != nil { - log.Fatalf("Error preparing statement: %v", err) + log.Fatalf("Error selecting db data: %v", err) } fortunes := make([]Fortune, 0, 16) From 798c6b61a71d56fbc33c603ee00e2491182f7022 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Wed, 2 Dec 2015 19:49:11 +0200 Subject: [PATCH 15/16] Updated Go version from 1.4.2 to 1.5.1 --- toolset/setup/linux/languages/go.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolset/setup/linux/languages/go.sh b/toolset/setup/linux/languages/go.sh index cd53641e9bb..9df00558656 100644 --- a/toolset/setup/linux/languages/go.sh +++ b/toolset/setup/linux/languages/go.sh @@ -5,7 +5,7 @@ RETCODE=$(fw_exists ${IROOT}/go.installed) source $IROOT/go.installed return 0; } -VERSION=1.4.2 +VERSION=1.5.1 GOROOT=$IROOT/go fw_get -O https://storage.googleapis.com/golang/go$VERSION.linux-amd64.tar.gz From 145f2e416c24eeae53e00297d735919d55c76713 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Tue, 8 Dec 2015 09:46:42 +0200 Subject: [PATCH 16/16] Updated go version to 1.5.2 --- toolset/setup/linux/languages/go.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolset/setup/linux/languages/go.sh b/toolset/setup/linux/languages/go.sh index 9df00558656..5e874156443 100644 --- a/toolset/setup/linux/languages/go.sh +++ b/toolset/setup/linux/languages/go.sh @@ -5,7 +5,7 @@ RETCODE=$(fw_exists ${IROOT}/go.installed) source $IROOT/go.installed return 0; } -VERSION=1.5.1 +VERSION=1.5.2 GOROOT=$IROOT/go fw_get -O https://storage.googleapis.com/golang/go$VERSION.linux-amd64.tar.gz