Pārlūkot izejas kodu

deleted some go test; updated go vet to 1.12 in fasthttp, go-std, iris; added evio tests (#4506)

chunariov 6 gadi atpakaļ
vecāks
revīzija
e0baec9135
75 mainītis faili ar 2890 papildinājumiem un 1432 dzēšanām
  1. 9 0
      frameworks/Go/evio/README.md
  2. 41 0
      frameworks/Go/evio/benchmark_config.json
  3. 12 0
      frameworks/Go/evio/evio-stdlib.dockerfile
  4. 12 0
      frameworks/Go/evio/evio.dockerfile
  5. 6 0
      frameworks/Go/evio/src/go.mod
  6. 4 0
      frameworks/Go/evio/src/go.sum
  7. 225 0
      frameworks/Go/evio/src/main.go
  8. 1 1
      frameworks/Go/fasthttp/fasthttp-postgresql-prefork.dockerfile
  9. 1 1
      frameworks/Go/fasthttp/fasthttp-postgresql.dockerfile
  10. 1 1
      frameworks/Go/fasthttp/fasthttp-prefork.dockerfile
  11. 1 1
      frameworks/Go/fasthttp/fasthttp.dockerfile
  12. 1 1
      frameworks/Go/go-std/README.md
  13. 43 39
      frameworks/Go/go-std/benchmark_config.json
  14. 16 0
      frameworks/Go/go-std/go-mgo-prefork.dockerfile
  15. 15 0
      frameworks/Go/go-std/go-mgo.dockerfile
  16. 0 13
      frameworks/Go/go-std/go-mongo-prefork.dockerfile
  17. 0 13
      frameworks/Go/go-std/go-mongo.dockerfile
  18. 0 12
      frameworks/Go/go-std/go-my-interpolate.dockerfile
  19. 11 7
      frameworks/Go/go-std/go-my-prefork.dockerfile
  20. 11 7
      frameworks/Go/go-std/go-my.dockerfile
  21. 0 18
      frameworks/Go/go-std/go-pg-prefork-quicktemplate.dockerfile
  22. 0 18
      frameworks/Go/go-std/go-pg-prefork.dockerfile
  23. 0 18
      frameworks/Go/go-std/go-pg-quicktemplate.dockerfile
  24. 0 18
      frameworks/Go/go-std/go-pg.dockerfile
  25. 16 0
      frameworks/Go/go-std/go-pgx-easyjson.dockerfile
  26. 16 0
      frameworks/Go/go-std/go-pgx-prefork-easyjson.dockerfile
  27. 16 0
      frameworks/Go/go-std/go-pgx-prefork-quicktemplate.dockerfile
  28. 16 0
      frameworks/Go/go-std/go-pgx-prefork.dockerfile
  29. 16 0
      frameworks/Go/go-std/go-pgx-quicktemplate.dockerfile
  30. 16 0
      frameworks/Go/go-std/go-pgx.dockerfile
  31. 0 11
      frameworks/Go/go-std/go-prefork.dockerfile
  32. 12 7
      frameworks/Go/go-std/go.dockerfile
  33. 17 0
      frameworks/Go/go-std/src/go.mod
  34. 34 0
      frameworks/Go/go-std/src/go.sum
  35. 138 0
      frameworks/Go/go-std/src/handlers/handlers.go
  36. 65 0
      frameworks/Go/go-std/src/handlers/handlers_easyjson.go
  37. 6 0
      frameworks/Go/go-std/src/handlers/message.go
  38. 91 0
      frameworks/Go/go-std/src/handlers/message_easyjson.go
  39. 68 0
      frameworks/Go/go-std/src/main.go
  40. 0 244
      frameworks/Go/go-std/src/mongo/main.go
  41. 0 363
      frameworks/Go/go-std/src/mysql/main.go
  42. 0 316
      frameworks/Go/go-std/src/postgres/main.go
  43. 52 0
      frameworks/Go/go-std/src/prefork.go
  44. 0 102
      frameworks/Go/go-std/src/simple/main.go
  45. 74 0
      frameworks/Go/go-std/src/storage/db.go
  46. 91 0
      frameworks/Go/go-std/src/storage/mgo.go
  47. 205 0
      frameworks/Go/go-std/src/storage/mysql.go
  48. 149 0
      frameworks/Go/go-std/src/storage/pgx.go
  49. 140 0
      frameworks/Go/go-std/src/storage/pq.go
  50. 10 0
      frameworks/Go/go-std/src/storage/world.go
  51. 168 0
      frameworks/Go/go-std/src/storage/world_easyjson.go
  52. 39 0
      frameworks/Go/go-std/src/templates/fortune.go
  53. 102 0
      frameworks/Go/go-std/src/templates/fortune_easyjson.go
  54. 0 7
      frameworks/Go/go-std/src/templates/fortunes.qtpl
  55. 81 0
      frameworks/Go/go-std/src/templates/fortunes.qtpl.go
  56. 20 5
      frameworks/Go/iris/benchmark_config.json
  57. 17 0
      frameworks/Go/iris/iris-pgx-prefork.dockerfile
  58. 17 0
      frameworks/Go/iris/iris-pgx-quicktemplate-prefork.dockerfile
  59. 8 5
      frameworks/Go/iris/iris-pgx-quicktemplate.dockerfile
  60. 8 5
      frameworks/Go/iris/iris-pgx.dockerfile
  61. 7 4
      frameworks/Go/iris/iris-prefork.dockerfile
  62. 7 4
      frameworks/Go/iris/iris.dockerfile
  63. 0 101
      frameworks/Go/iris/src/db.go
  64. 61 0
      frameworks/Go/iris/src/go.mod
  65. 171 0
      frameworks/Go/iris/src/go.sum
  66. 78 64
      frameworks/Go/iris/src/handlers.go
  67. 18 15
      frameworks/Go/iris/src/main.go
  68. 4 2
      frameworks/Go/iris/src/prefork.go
  69. 53 0
      frameworks/Go/iris/src/storage/db.go
  70. 148 0
      frameworks/Go/iris/src/storage/pgx.go
  71. 140 0
      frameworks/Go/iris/src/storage/pq.go
  72. 1 1
      frameworks/Go/iris/src/storage/world.go
  73. 3 1
      frameworks/Go/iris/src/templates/fortune.go
  74. 0 7
      frameworks/Go/iris/src/templates/fortunes.qtpl
  75. 81 0
      frameworks/Go/iris/src/templates/fortunes.qtpl.go

+ 9 - 0
frameworks/Go/evio/README.md

@@ -0,0 +1,9 @@
+# [fasthttp](https://github.com/valyala/fasthttp) (GoLang) Benchmarking Test
+
+This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
+
+"Golang Evio"
+
+## Test URLs
+
+    http://localhost:8080/plaintext

+ 41 - 0
frameworks/Go/evio/benchmark_config.json

@@ -0,0 +1,41 @@
+{
+  "framework": "evio",
+  "tests": [{
+    "default": {
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Go",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Evio",
+      "notes": "",
+      "versus": "go"
+    },
+    "stdlib": {
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Go",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Evio",
+      "notes": "",
+      "versus": "go"
+    }
+  }]
+}

+ 12 - 0
frameworks/Go/evio/evio-stdlib.dockerfile

@@ -0,0 +1,12 @@
+FROM golang:1.12
+
+ENV GO111MODULE on
+WORKDIR /evio
+
+COPY ./src /evio
+
+RUN go mod download
+
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -stdlib

+ 12 - 0
frameworks/Go/evio/evio.dockerfile

@@ -0,0 +1,12 @@
+FROM golang:1.12
+
+ENV GO111MODULE on
+WORKDIR /evio
+
+COPY ./src /evio
+
+RUN go mod download
+
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app

+ 6 - 0
frameworks/Go/evio/src/go.mod

@@ -0,0 +1,6 @@
+module evio
+
+require (
+	github.com/kavu/go_reuseport v1.4.0 // indirect
+	github.com/tidwall/evio v1.0.2
+)

+ 4 - 0
frameworks/Go/evio/src/go.sum

@@ -0,0 +1,4 @@
+github.com/kavu/go_reuseport v1.4.0 h1:YIp/96RZ3sJfn0LN+FFkkXIq3H3dfVOdRUtNejhDcxc=
+github.com/kavu/go_reuseport v1.4.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
+github.com/tidwall/evio v1.0.2 h1:vhPp5nVS5PZfR0CfV5ixqDEf+BKQFcaR1W1XeLLkvRE=
+github.com/tidwall/evio v1.0.2/go.mod h1:cYtY49LddNrlpsOmW7qJnqM8B2gOjrFrzT8+Fnb/GKs=

+ 225 - 0
frameworks/Go/evio/src/main.go

@@ -0,0 +1,225 @@
+// Copyright 2017 Joshua J Baker. All rights reserved.
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/tidwall/evio"
+)
+
+var res string
+
+type request struct {
+	proto, method string
+	path, query   string
+	head, body    string
+	remoteAddr    string
+}
+
+func main() {
+	var port int
+	var loops int
+	var unixsocket string
+	var stdlib bool
+
+	flag.StringVar(&unixsocket, "unixsocket", "", "unix socket")
+	flag.IntVar(&port, "port", 8080, "server port")
+	flag.BoolVar(&stdlib, "stdlib", false, "use stdlib")
+	flag.IntVar(&loops, "loops", 0, "num loops")
+	flag.Parse()
+
+	res = "Hello, World!"
+
+	var events evio.Events
+	events.NumLoops = loops
+
+	events.Serving = func(srv evio.Server) (action evio.Action) {
+		log.Printf("http server started on port %d (loops: %d)", port, srv.NumLoops)
+		if unixsocket != "" {
+			log.Printf("http server started at %s", unixsocket)
+		}
+		if stdlib {
+			log.Printf("http server is using stdlib")
+		}
+		return
+	}
+
+	events.Opened = func(c evio.Conn) (out []byte, opts evio.Options, action evio.Action) {
+		c.SetContext(&evio.InputStream{})
+		return
+	}
+
+	events.Closed = func(c evio.Conn, err error) (action evio.Action) {
+		return
+	}
+
+	events.Data = func(c evio.Conn, in []byte) (out []byte, action evio.Action) {
+		if in == nil {
+			return
+		}
+		is := c.Context().(*evio.InputStream)
+		data := is.Begin(in)
+
+		// buf := bufio.NewReader(bytes.NewReader(data))
+		// req, err := http.ReadRequest(buf)
+		// if err != nil {
+		// 	log.Println(err)
+		// 	is.End(data)
+		// 	return
+		// }
+		// t := &http.Response{
+		// 	Status:        "200 OK",
+		// 	StatusCode:    200,
+		// 	Proto:         "HTTP/1.1",
+		// 	ProtoMajor:    1,
+		// 	ProtoMinor:    1,
+		// 	Body:          ioutil.NopCloser(bytes.NewBufferString(res)),
+		// 	ContentLength: int64(len(res)),
+		// 	Request:       req,
+		// 	Header:        make(http.Header, 0),
+		// }
+		// resp := bytes.NewBuffer(nil)
+		// t.Write(resp)
+		// out = resp.Bytes()
+
+		// process the pipeline
+		var req request
+		for {
+			leftover, err := parsereq(data, &req)
+			if err != nil {
+				// bad thing happened
+				out = appendresp(out, "500 Error", "", err.Error()+"\n")
+				action = evio.Close
+				break
+			} else if len(leftover) == len(data) {
+				// request not ready, yet
+				break
+			}
+			// handle the request
+			req.remoteAddr = c.RemoteAddr().String()
+			out = appendhandle(out, &req)
+			data = leftover
+		}
+		is.End(data)
+		return
+	}
+	var ssuf string
+	if stdlib {
+		ssuf = "-net"
+	}
+	// We at least want the single http address.
+	addrs := []string{fmt.Sprintf("tcp"+ssuf+"://:%d", port)}
+	if unixsocket != "" {
+		addrs = append(addrs, fmt.Sprintf("unix"+ssuf+"://%s", unixsocket))
+	}
+	// Start serving!
+	log.Fatal(evio.Serve(events, addrs...))
+}
+
+// appendhandle handles the incoming request and appends the response to
+// the provided bytes, which is then returned to the caller.
+func appendhandle(b []byte, req *request) []byte {
+	return appendresp(b, "200 OK", "", res)
+}
+
+// appendresp will append a valid http response to the provide bytes.
+// The status param should be the code plus text such as "200 OK".
+// The head parameter should be a series of lines ending with "\r\n" or empty.
+func appendresp(b []byte, status, head, body string) []byte {
+	b = append(b, "HTTP/1.1"...)
+	b = append(b, ' ')
+	b = append(b, status...)
+	b = append(b, '\r', '\n')
+	b = append(b, "Server: evio\r\n"...)
+	b = append(b, "Content-Type: text/plain\r\n"...)
+	b = append(b, "Date: "...)
+	b = time.Now().AppendFormat(b, "Mon, 02 Jan 2006 15:04:05 GMT")
+	b = append(b, '\r', '\n')
+	if len(body) > 0 {
+		b = append(b, "Content-Length: "...)
+		b = strconv.AppendInt(b, int64(len(body)), 10)
+		b = append(b, '\r', '\n')
+	}
+	b = append(b, head...)
+	b = append(b, '\r', '\n')
+	if len(body) > 0 {
+		b = append(b, body...)
+	}
+	return b
+}
+
+// parsereq is a very simple http request parser. This operation
+// waits for the entire payload to be buffered before returning a
+// valid request.
+func parsereq(data []byte, req *request) (leftover []byte, err error) {
+	sdata := string(data)
+	var i, s int
+	var top string
+	var clen int
+	var q = -1
+	// method, path, proto line
+	for ; i < len(sdata); i++ {
+		if sdata[i] == ' ' {
+			req.method = sdata[s:i]
+			for i, s = i+1, i+1; i < len(sdata); i++ {
+				if sdata[i] == '?' && q == -1 {
+					q = i - s
+				} else if sdata[i] == ' ' {
+					if q != -1 {
+						req.path = sdata[s:q]
+						req.query = req.path[q+1 : i]
+					} else {
+						req.path = sdata[s:i]
+					}
+					for i, s = i+1, i+1; i < len(sdata); i++ {
+						if sdata[i] == '\n' && sdata[i-1] == '\r' {
+							req.proto = sdata[s:i]
+							i, s = i+1, i+1
+							break
+						}
+					}
+					break
+				}
+			}
+			break
+		}
+	}
+	if req.proto == "" {
+		return data, fmt.Errorf("malformed request")
+	}
+	top = sdata[:s]
+	for ; i < len(sdata); i++ {
+		if i > 1 && sdata[i] == '\n' && sdata[i-1] == '\r' {
+			line := sdata[s : i-1]
+			s = i + 1
+			if line == "" {
+				req.head = sdata[len(top)+2 : i+1]
+				i++
+				if clen > 0 {
+					if len(sdata[i:]) < clen {
+						break
+					}
+					req.body = sdata[i : i+clen]
+					i += clen
+				}
+				return data[i:], nil
+			}
+			if strings.HasPrefix(line, "Content-Length:") {
+				n, err := strconv.ParseInt(strings.TrimSpace(line[len("Content-Length:"):]), 10, 64)
+				if err == nil {
+					clen = int(n)
+				}
+			}
+		}
+	}
+	// not enough data
+	return data, nil
+}

+ 1 - 1
frameworks/Go/fasthttp/fasthttp-postgresql-prefork.dockerfile

@@ -1,4 +1,4 @@
-FROM golang:1.11.5
+FROM golang:1.12
 
 ADD ./ /fasthttp
 WORKDIR /fasthttp

+ 1 - 1
frameworks/Go/fasthttp/fasthttp-postgresql.dockerfile

@@ -1,4 +1,4 @@
-FROM golang:1.11.5
+FROM golang:1.12
 
 ADD ./ /fasthttp
 WORKDIR /fasthttp

+ 1 - 1
frameworks/Go/fasthttp/fasthttp-prefork.dockerfile

@@ -1,4 +1,4 @@
-FROM golang:1.11.5
+FROM golang:1.12
 
 ADD ./ /fasthttp
 WORKDIR /fasthttp

+ 1 - 1
frameworks/Go/fasthttp/fasthttp.dockerfile

@@ -1,4 +1,4 @@
-FROM golang:1.11.5
+FROM golang:1.12
 
 ADD ./ /fasthttp
 WORKDIR /fasthttp

+ 1 - 1
frameworks/Go/go-std/README.md

@@ -2,7 +2,7 @@
 
 This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
 
-"Golang standard library. For database tests Postgres was used."
+"Golang standard library. For database tests PostgreSQL, MySQL and MongoDB were used."
 
 ## Test URLs
 

+ 43 - 39
frameworks/Go/go-std/benchmark_config.json

@@ -20,13 +20,15 @@
       "notes": "",
       "versus": "go"
     },
-    "prefork": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
+    "pgx": {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "None",
+      "database": "Postgres",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -35,19 +37,19 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Go-prefork",
+      "display_name": "Go",
       "notes": "",
       "versus": "go"
     },
-    "mongo": {
+    "pgx-easyjson": {
+      "json_url": "/json",
       "db_url": "/db",
       "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
       "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "MongoDB",
+      "database": "Postgres",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -57,18 +59,15 @@
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Go",
-      "notes": "mongodb implementation for go net/http",
+      "notes": "",
       "versus": "go"
     },
-    "mongo-prefork": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
+    "pgx-quicktemplate": {
+      "fortune_url": "/fortune-quick",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "MongoDB",
+      "database": "Postgres",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -78,10 +77,12 @@
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Go",
-      "notes": "mongodb implementation for go net/http",
+      "notes": "",
       "versus": "go"
     },
-    "pg": {
+    "pgx-prefork": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "fortune_url": "/fortune",
@@ -102,8 +103,11 @@
       "notes": "",
       "versus": "go"
     },
-    "pg-quicktemplate": {
-      "fortune_url": "/fortune-quick",
+    "pgx-prefork-easyjson": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
@@ -120,11 +124,8 @@
       "notes": "",
       "versus": "go"
     },
-    "pg-prefork": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
+    "pgx-prefork-quicktemplate": {
+      "fortune_url": "/fortune-quick",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
@@ -141,12 +142,15 @@
       "notes": "",
       "versus": "go"
     },
-    "pg-prefork-quicktemplate": {
-      "fortune_url": "/fortune-quick",
+    "my": {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "Postgres",
+      "database": "MySQL",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -159,7 +163,7 @@
       "notes": "",
       "versus": "go"
     },
-    "my": {
+    "my-prefork": {
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "fortune_url": "/fortune",
@@ -180,7 +184,7 @@
       "notes": "",
       "versus": "go"
     },
-    "my-prefork": {
+    "mgo": {
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "fortune_url": "/fortune",
@@ -188,7 +192,7 @@
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "MySQL",
+      "database": "MongoDB",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -198,18 +202,18 @@
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Go",
-      "notes": "",
+      "notes": "mongodb implementation for go net/http",
       "versus": "go"
     },
-    "my-interpolate": {
-      "db_url": "/dbInterpolate",
-      "query_url": "/queriesInterpolate?queries=",
-      "fortune_url": "/fortuneInterpolate",
-      "update_url": "/updateInterpolate?queries=",
+    "mgo-prefork": {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "MySQL",
+      "database": "MongoDB",
       "framework": "None",
       "language": "Go",
       "flavor": "None",
@@ -218,8 +222,8 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Go-interpolate",
-      "notes": "",
+      "display_name": "Go",
+      "notes": "mongodb implementation for go net/http",
       "versus": "go"
     }
   }]

+ 16 - 0
frameworks/Go/go-std/go-mgo-prefork.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db mgo -prefork -db_connection_string "tfb-database"

+ 15 - 0
frameworks/Go/go-std/go-mgo.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db mgo -db_connection_string "tfb-database"

+ 0 - 13
frameworks/Go/go-std/go-mongo-prefork.dockerfile

@@ -1,13 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/mongo/ /go-std
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN apt update -yqq && apt install -yqq libsasl2-dev
-RUN go get gopkg.in/mgo.v2
-RUN go build -o hello_mongo .
-CMD ./hello_mongo -prefork

+ 0 - 13
frameworks/Go/go-std/go-mongo.dockerfile

@@ -1,13 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/mongo/ /go-std
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN apt update -yqq && apt install -yqq libsasl2-dev
-RUN go get gopkg.in/mgo.v2
-RUN go build -o hello_mongo .
-CMD ./hello_mongo

+ 0 - 12
frameworks/Go/go-std/go-my-interpolate.dockerfile

@@ -1,12 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/mysql/ /go-std
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go get github.com/go-sql-driver/mysql
-RUN go build -o hello_mysql .
-CMD ./hello_mysql

+ 11 - 7
frameworks/Go/go-std/go-my-prefork.dockerfile

@@ -1,12 +1,16 @@
 FROM golang:1.11.5
 
-ADD ./src/mysql/ /go-std
+ENV GO111MODULE on
 WORKDIR /go-std
 
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
+COPY ./src /go-std
 
-RUN go get github.com/go-sql-driver/mysql
-RUN go build -o hello_mysql .
-CMD ./hello_mysql -prefork
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db mysql -prefork -db_connection_string "benchmarkdbuser:benchmarkdbpass@tcp(tfb-database:3306)/hello_world?interpolateParams=true"

+ 11 - 7
frameworks/Go/go-std/go-my.dockerfile

@@ -1,12 +1,16 @@
 FROM golang:1.11.5
 
-ADD ./src/mysql/ /go-std
+ENV GO111MODULE on
 WORKDIR /go-std
 
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
+COPY ./src /go-std
 
-RUN go get github.com/go-sql-driver/mysql
-RUN go build -o hello_mysql .
-CMD ./hello_mysql
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db mysql -db_connection_string "benchmarkdbuser:benchmarkdbpass@tcp(tfb-database:3306)/hello_world?interpolateParams=true"

+ 0 - 18
frameworks/Go/go-std/go-pg-prefork-quicktemplate.dockerfile

@@ -1,18 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/postgres/ /go-std
-ADD ./src/templates/ /go-std/src/templates
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go get github.com/lib/pq
-RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-
-RUN qtc -file src/templates/fortunes.qtpl
-RUN go build -o hello_postgres .
-
-CMD ./hello_postgres -prefork

+ 0 - 18
frameworks/Go/go-std/go-pg-prefork.dockerfile

@@ -1,18 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/postgres/ /go-std
-ADD ./src/templates/ /go-std/src/templates
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go get github.com/lib/pq
-RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-
-RUN qtc -file src/templates/fortunes.qtpl
-RUN go build -o hello_postgres .
-
-CMD ./hello_postgres -prefork

+ 0 - 18
frameworks/Go/go-std/go-pg-quicktemplate.dockerfile

@@ -1,18 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/postgres/ /go-std
-ADD ./src/templates/ /go-std/src/templates
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go get github.com/lib/pq
-RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-
-RUN qtc -file src/templates/fortunes.qtpl
-RUN go build -o hello_postgres .
-
-CMD ./hello_postgres

+ 0 - 18
frameworks/Go/go-std/go-pg.dockerfile

@@ -1,18 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/postgres/ /go-std
-ADD ./src/templates/ /go-std/src/templates
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go get github.com/lib/pq
-RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-
-RUN qtc -file src/templates/fortunes.qtpl
-RUN go build -o hello_postgres .
-
-CMD ./hello_postgres

+ 16 - 0
frameworks/Go/go-std/go-pgx-easyjson.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx -easyjson

+ 16 - 0
frameworks/Go/go-std/go-pgx-prefork-easyjson.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx -prefork -easyjson

+ 16 - 0
frameworks/Go/go-std/go-pgx-prefork-quicktemplate.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx -prefork

+ 16 - 0
frameworks/Go/go-std/go-pgx-prefork.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx -prefork

+ 16 - 0
frameworks/Go/go-std/go-pgx-quicktemplate.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx

+ 16 - 0
frameworks/Go/go-std/go-pgx.dockerfile

@@ -0,0 +1,16 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /go-std
+
+COPY ./src /go-std
+
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -db pgx

+ 0 - 11
frameworks/Go/go-std/go-prefork.dockerfile

@@ -1,11 +0,0 @@
-FROM golang:1.11.5
-
-ADD ./src/simple/ /go-std
-WORKDIR /go-std
-
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN go build -o hello .
-CMD ./hello -prefork

+ 12 - 7
frameworks/Go/go-std/go.dockerfile

@@ -1,11 +1,16 @@
-FROM golang:1.11.5
+FROM golang:1.12
 
-ADD ./src/simple/ /go-std
+ENV GO111MODULE on
 WORKDIR /go-std
 
-RUN mkdir bin
-ENV GOPATH /go-std
-ENV PATH ${GOPATH}/bin:${PATH}
+COPY ./src /go-std
 
-RUN go build -o hello .
-CMD ./hello
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go get -u github.com/mailru/easyjson/...
+RUN go mod download
+
+RUN go generate ./templates
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app

+ 17 - 0
frameworks/Go/go-std/src/go.mod

@@ -0,0 +1,17 @@
+module go-std/src
+
+require (
+	github.com/cockroachdb/apd v1.1.0 // indirect
+	github.com/go-sql-driver/mysql v1.4.1
+	github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
+	github.com/jackc/pgx v3.3.0+incompatible
+	github.com/kr/pretty v0.1.0 // indirect
+	github.com/lib/pq v1.0.0
+	github.com/mailru/easyjson v0.0.0-20190221075403-6243d8e04c3f
+	github.com/pkg/errors v0.8.1 // indirect
+	github.com/satori/go.uuid v1.2.0 // indirect
+	github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
+	github.com/valyala/quicktemplate v1.0.2
+	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
+	gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce
+)

+ 34 - 0
frameworks/Go/go-std/src/go.sum

@@ -0,0 +1,34 @@
+github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
+github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
+github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mailru/easyjson v0.0.0-20190221075403-6243d8e04c3f h1:B6PQkurxGG1rqEX96oE14gbj8bqvYC5dtks9r5uGmlE=
+github.com/mailru/easyjson v0.0.0-20190221075403-6243d8e04c3f/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v0.0.0-20180905170723-c6fd90e432cc/go.mod h1:+g/po7GqyG5E+1CNgquiIxJnsXEi5vwFn5weFujbO78=
+github.com/valyala/quicktemplate v1.0.2 h1:ZeVRKan1W/25u5f9ilDo5HWtdxD+QxR10WrZwxqLM54=
+github.com/valyala/quicktemplate v1.0.2/go.mod h1:KZAB+RlYlfNtBUGQMzIrnE8uuNgD2SbUn5CpZyod0sk=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=

+ 138 - 0
frameworks/Go/go-std/src/handlers/handlers.go

@@ -0,0 +1,138 @@
+package handlers
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"sort"
+	"strconv"
+
+	"go-std/src/storage"
+	"go-std/src/templates"
+)
+
+// var (
+// 	// Database
+// 	worldSelectPrepared   *sql.Stmt
+// 	worldUpdatePrepared   *sql.Stmt
+// 	fortuneSelectPrepared *sql.Stmt
+// 	maxConnections        = runtime.NumCPU()
+// )
+
+func queriesParam(r *http.Request) int {
+	q, err := strconv.Atoi(r.URL.Query().Get("queries"))
+	if err != nil || q < 1 {
+		q = 1
+	} else if q > 500 {
+		q = 500
+	}
+	return q
+}
+
+// JSONHandler . Test 1: JSON serialization
+func JSONHandler(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Server", "Go")
+	w.Header().Set("Content-Type", "application/json")
+	json.NewEncoder(w).Encode(&Message{"Hello, World!"})
+}
+
+// DBHandler . Test 2: Single database query
+func DBHandler(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		world, err := db.GetOneRandomWorld()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		w.Header().Set("Server", "Go")
+		w.Header().Set("Content-Type", "application/json")
+		json.NewEncoder(w).Encode(&world)
+	}
+}
+
+// QueriesHandler . Test 3: Multiple database queries
+func QueriesHandler(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		q := queriesParam(r)
+		results := make([]storage.World, q)
+
+		var err error
+		for i := 0; i < q; i++ {
+			results[i], err = db.GetOneRandomWorld()
+			if err != nil {
+				log.Println(err)
+			}
+		}
+
+		w.Header().Set("Server", "Go")
+		w.Header().Set("Content-Type", "application/json")
+		json.NewEncoder(w).Encode(results)
+	}
+}
+
+// FortuneHandler . Test 4: Fortunes
+func FortuneHandler(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		fortunes, err := db.GetFortunes()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
+
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
+
+		w.Header().Set("Server", "Go")
+		w.Header().Set("Content-Type", "text/html; charset=utf-8")
+		if err := templates.FortuneTemplate.Execute(w, fortunes); err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		}
+	}
+}
+
+// FortuneQuickHandler . Test 4: Fortunes
+func FortuneQuickHandler(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		fortunes, err := db.GetFortunes()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
+
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
+
+		w.Header().Set("Server", "Go")
+		w.Header().Set("Content-Type", "text/html; charset=utf-8")
+		templates.WriteFortunePage(w, fortunes)
+	}
+}
+
+// UpdateHandler . Test 5: Database updates
+func UpdateHandler(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		q := queriesParam(r)
+
+		worlds, err := db.UpdateRandomWorlds(q)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		w.Header().Set("Server", "Go")
+		w.Header().Set("Content-Type", "application/json")
+		json.NewEncoder(w).Encode(worlds)
+	}
+}
+
+// PlaintextHandler . Test 6: Plaintext
+func PlaintextHandler(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Server", "Go")
+	w.Header().Set("Content-Type", "text/plain")
+	w.Write([]byte("Hello, World!"))
+}

+ 65 - 0
frameworks/Go/go-std/src/handlers/handlers_easyjson.go

@@ -0,0 +1,65 @@
+package handlers
+
+import (
+	"log"
+	"net/http"
+
+	"go-std/src/storage"
+
+	"github.com/mailru/easyjson"
+)
+
+// JSONHandlerEasyJSON . Test 1: JSON serialization
+func JSONHandlerEasyJSON(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Server", "Go")
+	easyjson.MarshalToHTTPResponseWriter(&Message{"Hello, World!"}, w)
+}
+
+// DBHandlerEasyJSON . Test 2: Single database query
+func DBHandlerEasyJSON(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		world, err := db.GetOneRandomWorld()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		w.Header().Set("Server", "Go")
+		easyjson.MarshalToHTTPResponseWriter(&world, w)
+	}
+}
+
+// QueriesHandlerEasyJSON . Test 3: Multiple database queries
+func QueriesHandlerEasyJSON(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		q := queriesParam(r)
+		worlds := make([]storage.World, q)
+
+		var err error
+		for i := 0; i < q; i++ {
+			worlds[i], err = db.GetOneRandomWorld()
+			if err != nil {
+				log.Println(err)
+			}
+		}
+
+		w.Header().Set("Server", "Go")
+		easyjson.MarshalToHTTPResponseWriter(storage.Worlds(worlds), w)
+	}
+}
+
+// UpdateHandlerEasyJSON . Test 5: Database updates
+func UpdateHandlerEasyJSON(db storage.DB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		q := queriesParam(r)
+
+		worlds, err := db.UpdateRandomWorlds(q)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		w.Header().Set("Server", "Go")
+		easyjson.MarshalToHTTPResponseWriter(storage.Worlds(worlds), w)
+	}
+}

+ 6 - 0
frameworks/Go/go-std/src/handlers/message.go

@@ -0,0 +1,6 @@
+package handlers
+
+// Message struct
+type Message struct {
+	Message string `json:"message"`
+}

+ 91 - 0
frameworks/Go/go-std/src/handlers/message_easyjson.go

@@ -0,0 +1,91 @@
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+
+package handlers
+
+import (
+	json "encoding/json"
+
+	easyjson "github.com/mailru/easyjson"
+	jlexer "github.com/mailru/easyjson/jlexer"
+	jwriter "github.com/mailru/easyjson/jwriter"
+)
+
+// suppress unused package warning
+var (
+	_ *json.RawMessage
+	_ *jlexer.Lexer
+	_ *jwriter.Writer
+	_ easyjson.Marshaler
+)
+
+func easyjson89aae3efDecodeGoStdSrc(in *jlexer.Lexer, out *Message) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "message":
+			out.Message = string(in.String())
+		default:
+			in.SkipRecursive()
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjson89aae3efEncodeGoStdSrc(out *jwriter.Writer, in Message) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"message\":"
+		if first {
+			first = false
+			out.RawString(prefix[1:])
+		} else {
+			out.RawString(prefix)
+		}
+		out.String(string(in.Message))
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v Message) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjson89aae3efEncodeGoStdSrc(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v Message) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjson89aae3efEncodeGoStdSrc(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *Message) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjson89aae3efDecodeGoStdSrc(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *Message) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjson89aae3efDecodeGoStdSrc(l, v)
+}

+ 68 - 0
frameworks/Go/go-std/src/main.go

@@ -0,0 +1,68 @@
+package main
+
+import (
+	"flag"
+	"log"
+	"net"
+	"net/http"
+	"runtime"
+
+	"go-std/src/handlers"
+	"go-std/src/storage"
+)
+
+func main() {
+	// init flags
+	bindHost := flag.String("bind", ":8080", "set bind host")
+	prefork := flag.Bool("prefork", false, "use prefork")
+	easyjson := flag.Bool("easyjson", false, "use easyjson")
+	child := flag.Bool("child", false, "is child proc")
+	dbDriver := flag.String("db", "none", "db connection driver [values: pq || pgx || mysql || mgo || none]")
+	dbConnectionString := flag.String("db_connection_string",
+		"host=tfb-database user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world sslmode=disable",
+		"db connection string")
+	flag.Parse()
+
+	// check for prefork
+	var listener net.Listener
+	if *prefork {
+		listener = doPrefork(*child, *bindHost)
+	} else {
+		runtime.GOMAXPROCS(runtime.NumCPU())
+	}
+
+	// init database with appropriate driver
+	db, err := storage.InitDB(*dbDriver, *dbConnectionString)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// init handlers
+	http.HandleFunc("/plaintext", handlers.PlaintextHandler)
+	if *easyjson {
+		http.HandleFunc("/json", handlers.JSONHandlerEasyJSON)
+	} else {
+		http.HandleFunc("/json", handlers.JSONHandler)
+	}
+	if db != nil {
+		defer db.Close()
+		http.HandleFunc("/fortune", handlers.FortuneHandler(db))
+		http.HandleFunc("/fortune-quick", handlers.FortuneQuickHandler(db))
+		if *easyjson {
+			http.HandleFunc("/db", handlers.DBHandlerEasyJSON(db))
+			http.HandleFunc("/queries", handlers.QueriesHandlerEasyJSON(db))
+			http.HandleFunc("/update", handlers.UpdateHandlerEasyJSON(db))
+		} else {
+			http.HandleFunc("/db", handlers.DBHandler(db))
+			http.HandleFunc("/queries", handlers.QueriesHandler(db))
+			http.HandleFunc("/update", handlers.UpdateHandler(db))
+		}
+	}
+
+	// start server
+	if *prefork {
+		http.Serve(listener, nil)
+	} else {
+		http.ListenAndServe(*bindHost, nil)
+	}
+}

+ 0 - 244
frameworks/Go/go-std/src/mongo/main.go

@@ -1,244 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"flag"
-	"html/template"
-	"log"
-	"math/rand"
-	"net"
-	"net/http"
-	"os"
-	"os/exec"
-	"runtime"
-	"sort"
-	"strconv"
-
-	mgo "gopkg.in/mgo.v2"
-	"gopkg.in/mgo.v2/bson"
-)
-
-const (
-	connectionString = "tfb-database"
-	worldRowCount    = 10000
-	fortuneHTML      = `<!DOCTYPE html>
-<html>
-<head>
-<title>Fortunes</title>
-</head>
-<body>
-<table>
-<tr>
-<th>id</th>
-<th>message</th>
-</tr>
-{{range .}}
-<tr>
-<td>{{.Id}}</td>
-<td>{{.Message}}</td>
-</tr>
-{{end}}
-</table>
-</body>
-</html>`
-)
-
-var (
-	tmpl = template.Must(template.New("fortune.html").Parse(fortuneHTML))
-
-	database *mgo.Database
-	fortunes *mgo.Collection
-	worlds   *mgo.Collection
-)
-
-type Message struct {
-	Message string `json:"message"`
-}
-
-type World struct {
-	Id           uint16 `bson:"_id" json:"id"`
-	RandomNumber uint16 `bson:"randomNumber" json:"randomNumber"`
-}
-
-type Fortune struct {
-	Id      uint16 `bson:"_id" json:"id"`
-	Message string `bson:"message" json:"message"`
-}
-
-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
-}
-
-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()
-	}
-
-	session, err := mgo.Dial(connectionString)
-	if err != nil {
-		log.Fatalf("Error opening database: %v", err)
-	}
-	defer session.Close()
-	database = session.DB("hello_world")
-	worlds = database.C("world")
-	fortunes = database.C("fortune")
-	http.HandleFunc("/db", dbHandler)
-	http.HandleFunc("/fortune", fortuneHandler)
-	http.HandleFunc("/queries", queriesHandler)
-	http.HandleFunc("/update", updateHandler)
-
-	if !*prefork {
-		http.ListenAndServe(":8080", nil)
-	} else {
-		http.Serve(listener, nil)
-	}
-}
-
-func doPrefork() net.Listener {
-	var listener net.Listener
-	if !*child {
-		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()/2)
-		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 {
-			if err := ch.Wait(); err != nil {
-				log.Print(err)
-			}
-		}
-		os.Exit(0)
-	} else {
-		var err error
-		listener, err = net.FileListener(os.NewFile(3, ""))
-		if err != nil {
-			log.Fatal(err)
-		}
-	}
-	return listener
-}
-
-// Helper for random numbers
-func getRandomNumber() uint16 {
-	return uint16(rand.Intn(worldRowCount) + 1)
-}
-
-// Test 2: Single database query
-func dbHandler(w http.ResponseWriter, r *http.Request) {
-	var world World
-	query := bson.M{"_id": getRandomNumber()}
-	if err := worlds.Find(query).One(&world); err != nil {
-		log.Fatalf("Error finding world with id: %s", err.Error())
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Server", "Go")
-	json.NewEncoder(w).Encode(&world)
-}
-
-// Helper for getting the "queries" parameter
-func getQueriesParam(r *http.Request) int {
-	n := 1
-	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
-		n, _ = strconv.Atoi(nStr)
-	}
-
-	if n < 1 {
-		n = 1
-	} else if n > 500 {
-		n = 500
-	}
-
-	return n
-}
-
-// Test 3: Multiple database queries
-func queriesHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-	world := make([]World, n)
-
-	for i := 0; i < n; i++ {
-		query := bson.M{"_id": getRandomNumber()}
-		if err := worlds.Find(query).One(&world[i]); err != nil {
-			log.Fatalf("Error finding world with id: %s", err.Error())
-		}
-	}
-
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Server", "Go")
-	json.NewEncoder(w).Encode(world)
-}
-
-// Test 4: Fortunes
-func fortuneHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	w.Header().Set("Server", "Go")
-	f := make(Fortunes, 16)
-	if err := fortunes.Find(nil).All(&f); err == nil {
-		f = append(f, Fortune{Message: "Additional fortune added at request time."})
-		sort.Sort(ByMessage{f})
-		if err := tmpl.Execute(w, f); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-		}
-	} else {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}
-}
-
-// Test 5: Database updates
-func updateHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-	world := make([]World, n)
-
-	for i := 0; i < n; i++ {
-		query := bson.M{"_id": getRandomNumber()}
-		if err := worlds.Find(query).One(&world[i]); err != nil {
-			log.Fatalf("Error finding world with id: %s", err.Error())
-		}
-		world[i].RandomNumber = getRandomNumber()
-		update := bson.M{"$set": bson.M{"randomNumber": world[i].RandomNumber}}
-		if err := worlds.Update(query, update); err != nil {
-			log.Fatalf("Error updating world with id: %s", err.Error())
-		}
-	}
-
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Server", "Go")
-	json.NewEncoder(w).Encode(&world)
-}

+ 0 - 363
frameworks/Go/go-std/src/mysql/main.go

@@ -1,363 +0,0 @@
-package main
-
-import (
-	"database/sql"
-	"encoding/json"
-	"flag"
-	"html/template"
-	"log"
-	"math/rand"
-	"net"
-	"net/http"
-	"os"
-	"os/exec"
-	"runtime"
-	"sort"
-	"strconv"
-
-	_ "github.com/go-sql-driver/mysql"
-)
-
-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"`
-}
-
-const (
-	// Content
-	helloWorldString = "Hello, World!"
-	fortuneHTML      = `<!DOCTYPE html>
-<html>
-<head>
-<title>Fortunes</title>
-</head>
-<body>
-<table>
-<tr>
-<th>id</th>
-<th>message</th>
-</tr>
-{{range .}}
-<tr>
-<td>{{.Id}}</td>
-<td>{{.Message}}</td>
-</tr>
-{{end}}
-</table>
-</body>
-</html>`
-
-	// Databases
-	//
-	// `interpolateParams=true` enables client side parameter interpolation.
-	// It reduces round trips without prepared statement.
-	//
-	// We can see difference between prepared statement and interpolation by comparing go-std-mysql and go-std-mysql-interpolate
-	connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(tfb-database: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
-	maxConnections   = 256
-)
-
-var (
-	helloWorldBytes = []byte(helloWorldString)
-
-	// Templates
-	tmpl = template.Must(template.New("fortune.html").Parse(fortuneHTML))
-
-	// Database
-	db                    *sql.DB
-	worldSelectPrepared   *sql.Stmt
-	worldUpdatePrepared   *sql.Stmt
-	fortuneSelectPrepared *sql.Stmt
-)
-
-var prefork = flag.Bool("prefork", false, "use prefork")
-var child = flag.Bool("child", false, "is child proc")
-
-func initDB() {
-	var err error
-	db, err = sql.Open("mysql", connectionString)
-	if err != nil {
-		log.Fatalf("Error opening database: %v", err)
-	}
-	db.SetMaxIdleConns(maxConnections)
-	db.SetMaxOpenConns(maxConnections)
-
-	worldSelectPrepared, err = db.Prepare(worldSelect)
-	if err != nil {
-		log.Fatal(err)
-	}
-	worldUpdatePrepared, err = db.Prepare(worldUpdate)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fortuneSelectPrepared, err = db.Prepare(fortuneSelect)
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-func main() {
-	var listener net.Listener
-	flag.Parse()
-	if !*prefork {
-		runtime.GOMAXPROCS(runtime.NumCPU())
-	} else {
-		listener = doPrefork()
-	}
-
-	initDB()
-
-	http.HandleFunc("/db", dbHandler)
-	http.HandleFunc("/dbInterpolate", dbInterpolateHandler)
-	http.HandleFunc("/queries", queriesHandler)
-	http.HandleFunc("/queriesInterpolate", queriesInterpolateHandler)
-	http.HandleFunc("/fortune", fortuneHandler)
-	http.HandleFunc("/fortuneInterpolate", fortuneInterpolateHandler)
-	http.HandleFunc("/update", updateHandler)
-	http.HandleFunc("/updateInterpolate", updateInterpolateHandler)
-	if !*prefork {
-		http.ListenAndServe(":8080", nil)
-	} else {
-		http.Serve(listener, nil)
-	}
-}
-
-func doPrefork() net.Listener {
-	var listener net.Listener
-	if !*child {
-		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()/2)
-		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 {
-			if err := ch.Wait(); err != nil {
-				log.Print(err)
-			}
-		}
-		os.Exit(0)
-	} else {
-		var err error
-		listener, err = net.FileListener(os.NewFile(3, ""))
-		if err != nil {
-			log.Fatal(err)
-		}
-	}
-	return listener
-}
-
-func getQueriesParam(r *http.Request) int {
-	n := 1
-	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
-		n, _ = strconv.Atoi(nStr)
-	}
-
-	if n < 1 {
-		n = 1
-	} else if n > 500 {
-		n = 500
-	}
-	return n
-}
-
-// Test 1: JSON serialization
-func jsonHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(&Message{helloWorldString})
-}
-
-// Test 2: Single database query
-func dbHandler(w http.ResponseWriter, r *http.Request) {
-	var world World
-	err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
-	if err != nil {
-		log.Fatalf("Error scanning world row: %s", err.Error())
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(&world)
-}
-
-func dbInterpolateHandler(w http.ResponseWriter, r *http.Request) {
-	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.Error())
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(&world)
-}
-
-// Test 3: Multiple database queries
-func queriesHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	world := make([]World, n)
-	for i := 0; i < n; i++ {
-		err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
-		if err != nil {
-			log.Fatalf("Error scanning world row: %v", err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(world)
-}
-
-func queriesInterpolateHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	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: %v", err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(world)
-}
-
-// Test 4: Fortunes
-func fortuneHandler(w http.ResponseWriter, r *http.Request) {
-	rows, err := fortuneSelectPrepared.Query()
-	if err != nil {
-		log.Fatalf("Error preparing statement: %v", err)
-	}
-
-	fortunes := fetchFortunes(rows)
-	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
-
-	sort.Sort(ByMessage{fortunes})
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	if err := tmpl.Execute(w, fortunes); err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}
-}
-
-func fortuneInterpolateHandler(w http.ResponseWriter, r *http.Request) {
-	rows, err := db.Query(fortuneSelect)
-	if err != nil {
-		log.Fatalf("Error preparing statement: %v", err)
-	}
-
-	fortunes := fetchFortunes(rows)
-	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
-
-	sort.Sort(ByMessage{fortunes})
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	if err := tmpl.Execute(w, fortunes); err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}
-}
-
-func fetchFortunes(rows *sql.Rows) Fortunes {
-	defer rows.Close()
-
-	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.Error())
-		}
-		fortunes = append(fortunes, &fortune)
-	}
-	return fortunes
-}
-
-// Test 5: Database updates
-func updateHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	world := make([]World, n)
-	for i := 0; i < n; i++ {
-		if err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
-			log.Fatalf("Error scanning world row: %v", err)
-		}
-		world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
-		if _, err := worldUpdatePrepared.Exec(world[i].RandomNumber, world[i].Id); err != nil {
-			log.Fatalf("Error updating world row: %v", err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	encoder := json.NewEncoder(w)
-	encoder.Encode(world)
-}
-
-func updateInterpolateHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	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: %v", 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: %v", err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	encoder := json.NewEncoder(w)
-	encoder.Encode(world)
-}
-
-// Test 6: Plaintext
-func plaintextHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/plain")
-	w.Write(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 }

+ 0 - 316
frameworks/Go/go-std/src/postgres/main.go

@@ -1,316 +0,0 @@
-package main
-
-import (
-	"database/sql"
-	"encoding/json"
-	"flag"
-	"html/template"
-	"log"
-	"math/rand"
-	"net"
-	"net/http"
-	"os"
-	"os/exec"
-	"runtime"
-	"sort"
-	"strconv"
-	"templates"
-
-	_ "github.com/lib/pq"
-)
-
-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"`
-}
-
-const (
-	// Content
-	fortuneHTML = `<!DOCTYPE html>
-<html>
-<head>
-<title>Fortunes</title>
-</head>
-<body>
-<table>
-<tr>
-<th>id</th>
-<th>message</th>
-</tr>
-{{range .}}
-<tr>
-<td>{{.Id}}</td>
-<td>{{.Message}}</td>
-</tr>
-{{end}}
-</table>
-</body>
-</html>`
-
-	// Database
-	connectionString = "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world?sslmode=disable"
-	worldSelect      = "SELECT id, randomNumber FROM World WHERE id = $1"
-	worldUpdate      = "UPDATE World SET randomNumber = $1 WHERE id = $2"
-	fortuneSelect    = "SELECT id, message FROM Fortune"
-	worldRowCount    = 10000
-	maxConnections   = 256
-)
-
-var (
-	// Templates
-	tmpl = template.Must(template.New("fortune.html").Parse(fortuneHTML))
-
-	// Database
-	db                    *sql.DB
-	worldSelectPrepared   *sql.Stmt
-	worldUpdatePrepared   *sql.Stmt
-	fortuneSelectPrepared *sql.Stmt
-)
-
-var prefork = flag.Bool("prefork", false, "use prefork")
-var child = flag.Bool("child", false, "is child proc")
-
-func initDB() {
-	var err error
-	db, err = sql.Open("postgres", connectionString)
-	if err != nil {
-		log.Fatalf("Error opening database: %v", err)
-	}
-	db.SetMaxIdleConns(maxConnections)
-	db.SetMaxOpenConns(maxConnections)
-
-	worldSelectPrepared, err = db.Prepare(worldSelect)
-	if err != nil {
-		log.Fatal(err)
-	}
-	worldUpdatePrepared, err = db.Prepare(worldUpdate)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fortuneSelectPrepared, err = db.Prepare(fortuneSelect)
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-func main() {
-	var listener net.Listener
-	flag.Parse()
-	if !*prefork {
-		runtime.GOMAXPROCS(runtime.NumCPU())
-	} else {
-		listener = doPrefork()
-	}
-
-	initDB()
-
-	http.HandleFunc("/db", dbHandler)
-	http.HandleFunc("/queries", queriesHandler)
-	http.HandleFunc("/fortune", fortuneHandler)
-	http.HandleFunc("/fortune-quick", fortuneQuickHandler)
-	http.HandleFunc("/update", updateHandler)
-
-	if !*prefork {
-		http.ListenAndServe(":8080", nil)
-	} else {
-		http.Serve(listener, nil)
-	}
-}
-
-func doPrefork() net.Listener {
-	var listener net.Listener
-	if !*child {
-		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()/2)
-		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 {
-			if err := ch.Wait(); err != nil {
-				log.Print(err)
-			}
-		}
-		os.Exit(0)
-	} else {
-		var err error
-		listener, err = net.FileListener(os.NewFile(3, ""))
-		if err != nil {
-			log.Fatal(err)
-		}
-	}
-	return listener
-}
-
-func getQueriesParam(r *http.Request) int {
-	n := 1
-	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
-		n, _ = strconv.Atoi(nStr)
-	}
-
-	if n < 1 {
-		n = 1
-	} else if n > 500 {
-		n = 500
-	}
-	return n
-}
-
-// Test 2: Single database query
-func dbHandler(w http.ResponseWriter, r *http.Request) {
-	var world World
-	err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
-	if err != nil {
-		log.Fatalf("Error scanning world row: %s", err.Error())
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(&world)
-}
-
-// Test 3: Multiple database queries
-func queriesHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	world := make([]World, n)
-	for i := 0; i < n; i++ {
-		err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
-		if err != nil {
-			log.Fatalf("Error scanning world row: %v", err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(world)
-}
-
-// Test 4: Fortunes
-func fortuneHandler(w http.ResponseWriter, r *http.Request) {
-	rows, err := fortuneSelectPrepared.Query()
-	if err != nil {
-		log.Fatalf("Error preparing statement: %v", err)
-	}
-	defer rows.Close()
-
-	fortunes := make(Fortunes, 0, 16)
-	for rows.Next() {
-		fortune := Fortune{}
-		if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
-			log.Fatalf("Error scanning fortune row: %s", err.Error())
-		}
-		fortunes = append(fortunes, &fortune)
-	}
-	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
-
-	sort.Sort(ByMessage{fortunes})
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	if err := tmpl.Execute(w, fortunes); err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}
-}
-
-// Test 4: Fortunes
-func fortuneQuickHandler(w http.ResponseWriter, r *http.Request) {
-	rows, err := fortuneSelectPrepared.Query()
-	if err != nil {
-		log.Fatalf("Error preparing statement: %v", err)
-	}
-	defer rows.Close()
-
-	fortunes := make([]templates.Fortune, 0, 16)
-	for rows.Next() { // Fetch rows
-		fortune := templates.Fortune{}
-		if err := rows.Scan(&fortune.ID, &fortune.Message); err != nil {
-			log.Fatalf("Error scanning fortune row: %s", err.Error())
-		}
-		fortunes = append(fortunes, fortune)
-	}
-	fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
-
-	sort.Slice(fortunes, func(i, j int) bool {
-		return fortunes[i].Message < fortunes[j].Message
-	})
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	templates.WriteFortunePage(w, fortunes)
-}
-
-// Test 5: Database updates
-func updateHandler(w http.ResponseWriter, r *http.Request) {
-	n := getQueriesParam(r)
-
-	world := make([]World, n)
-	for i := 0; i < n; i++ {
-		if err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
-			log.Fatalf("Error scanning world row: %v", err)
-		}
-		world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
-	}
-
-	if len(world) > 0 {
-		// against deadlocks
-		sort.Slice(world, func(i, j int) bool {
-			return world[i].Id < world[j].Id
-		})
-
-		tx, err := db.Begin()
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		for i := 0; i < n; i++ {
-			if _, err := tx.Stmt(worldUpdatePrepared).Exec(world[i].RandomNumber, world[i].Id); err != nil {
-				log.Printf("Error updating world row: %v\n", err)
-				tx.Rollback()
-			}
-		}
-
-		if err := tx.Commit(); err != nil {
-			log.Fatal(err)
-		}
-	}
-
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	encoder := json.NewEncoder(w)
-	encoder.Encode(world)
-}
-
-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 }

+ 52 - 0
frameworks/Go/go-std/src/prefork.go

@@ -0,0 +1,52 @@
+package main
+
+import (
+	"log"
+	"net"
+	"os"
+	"os/exec"
+	"runtime"
+)
+
+func doPrefork(child bool, toBind string) net.Listener {
+	var listener net.Listener
+	if !child {
+		addr, err := net.ResolveTCPAddr("tcp", toBind)
+		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 {
+			log.Println(append(os.Args[1:], "-child"))
+			children[i] = exec.Command(os.Args[0], append(os.Args[1:], "-child")...)
+			children[i].Stdout = os.Stdout
+			children[i].Stderr = os.Stderr
+			children[i].ExtraFiles = []*os.File{fl}
+			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)
+	} else {
+		var err error
+		listener, err = net.FileListener(os.NewFile(3, ""))
+		if err != nil {
+			log.Fatal(err)
+		}
+		runtime.GOMAXPROCS(1)
+	}
+	return listener
+}

+ 0 - 102
frameworks/Go/go-std/src/simple/main.go

@@ -1,102 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"flag"
-	"log"
-	"net"
-	"net/http"
-	"os"
-	"os/exec"
-	"runtime"
-)
-
-type Message struct {
-	Message string `json:"message"`
-}
-
-const (
-	helloWorldString = "Hello, World!"
-)
-
-var (
-	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()
-	}
-
-	http.HandleFunc("/json", jsonHandler)
-	http.HandleFunc("/plaintext", plaintextHandler)
-
-	if !*prefork {
-		http.ListenAndServe(":8080", nil)
-	} else {
-		http.Serve(listener, nil)
-	}
-}
-
-func doPrefork() net.Listener {
-	var listener net.Listener
-	if !*child {
-		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()/2)
-		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 {
-			if err := ch.Wait(); err != nil {
-				log.Print(err)
-			}
-		}
-		os.Exit(0)
-	} else {
-		var err error
-		listener, err = net.FileListener(os.NewFile(3, ""))
-		if err != nil {
-			log.Fatal(err)
-		}
-	}
-	return listener
-}
-
-// Test 1: JSON serialization
-func jsonHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(&Message{helloWorldString})
-}
-
-// Test 6: Plaintext
-func plaintextHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Server", "Go")
-	w.Header().Set("Content-Type", "text/plain")
-	w.Write(helloWorldBytes)
-}

+ 74 - 0
frameworks/Go/go-std/src/storage/db.go

@@ -0,0 +1,74 @@
+package storage
+
+import (
+	"errors"
+	"fmt"
+	"runtime"
+
+	"go-std/src/templates"
+)
+
+const (
+	selectQueryStrPostgre  = "SELECT id, randomNumber FROM World WHERE id = $1"
+	updateQueryStrPostgre  = "UPDATE World SET randomNumber = $1 WHERE id = $2"
+	fortuneQueryStrPostgre = "SELECT id, message FROM Fortune"
+)
+
+const (
+	selectQueryStrMySQL  = "SELECT id, randomNumber FROM World WHERE id = ?"
+	updateQueryStrMySQL  = "UPDATE World SET randomNumber = ? WHERE id = ?"
+	fortuneQueryStrMySQL = "SELECT id, message FROM Fortune"
+)
+
+var (
+	worldsCount = 10000
+)
+
+// DB is interface for
+type DB interface {
+	GetOneRandomWorld() (World, error)
+	UpdateRandomWorlds(queries int) ([]World, error)
+	GetFortunes() ([]templates.Fortune, error)
+	Close()
+}
+
+// InitDB with appropriate driver
+func InitDB(dbDriver, dbConnectionString string) (DB, error) {
+	var err error
+	var db DB
+	if dbDriver == "pq" {
+		db, err = NewPqDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening postgresql database with pq driver: %s", err)
+		}
+	} else if dbDriver == "pgx" {
+		db, err = NewPgxDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening postgresql database with pgx driver: %s", err)
+		}
+	} else if dbDriver == "mysql" {
+		db, err = NewMySQLDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening mysql database: %s", err)
+		}
+	} else if dbDriver == "mgo" {
+		db, err = NewMongoDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening mongo database with mgo driver: %s", err)
+		}
+	} else if dbDriver == "none" {
+		db = nil
+	} else {
+		return nil, errors.New("Can't recognize DB driver type")
+	}
+
+	return db, nil
+}

+ 91 - 0
frameworks/Go/go-std/src/storage/mgo.go

@@ -0,0 +1,91 @@
+package storage
+
+import (
+	"fmt"
+	"log"
+	"math/rand"
+
+	"go-std/src/templates"
+
+	mgo "gopkg.in/mgo.v2"
+	"gopkg.in/mgo.v2/bson"
+)
+
+// Mongo struct
+type Mongo struct {
+	session *mgo.Session
+	// collections
+	database *mgo.Database
+	fortunes *mgo.Collection
+	worlds   *mgo.Collection
+}
+
+// Connect create connection and ping db
+func (mongo *Mongo) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	session, err := mgo.Dial(dbConnectionString)
+	if err != nil {
+		log.Fatalf("Error opening database: %v", err)
+	}
+
+	mongo.database = session.DB("hello_world")
+	mongo.worlds = mongo.database.C("world")
+	mongo.fortunes = mongo.database.C("fortune")
+
+	return nil
+}
+
+// Close connect to db
+func (mongo *Mongo) Close() {
+	mongo.session.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (mongo Mongo) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err = mongo.worlds.Find(bson.M{"_id": queryID}).One(&w); err != nil {
+		err = fmt.Errorf("error finding world with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (mongo Mongo) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = mongo.GetOneRandomWorld()
+	}
+
+	for _, selectedWorld := range selectedWorlds {
+		selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+		if err := mongo.worlds.Update(
+			bson.M{"_id": selectedWorld.ID},
+			bson.M{"$set": bson.M{"randomNumber": selectedWorld.RandomNumber}},
+		); err != nil {
+			log.Fatalf("Error updating world with id: %s", err.Error())
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes finds all fortunes from table
+func (mongo Mongo) GetFortunes() ([]templates.Fortune, error) {
+	fortunes := make([]templates.Fortune, 0, 16)
+
+	if err := mongo.fortunes.Find(nil).All(&fortunes); err != nil {
+		return nil, err
+	}
+
+	return fortunes, nil
+}
+
+// NewMongoDB creates new connection to mongodb with mgo driver
+func NewMongoDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var mongo Mongo
+	if err := mongo.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &mongo, nil
+}

+ 205 - 0
frameworks/Go/go-std/src/storage/mysql.go

@@ -0,0 +1,205 @@
+package storage
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+	"math/rand"
+	"sort"
+
+	"go-std/src/templates"
+
+	_ "github.com/go-sql-driver/mysql" // postgresql import
+)
+
+// MySQL struct
+type MySQL struct {
+	db *sql.DB
+	// prepare statements
+	selectStmt  *sql.Stmt
+	updateStmt  *sql.Stmt
+	fortuneStmt *sql.Stmt
+}
+
+// Connect create connection and ping db
+func (mysql *MySQL) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var err error
+	mysql.db, err = sql.Open("mysql", dbConnectionString)
+	if err != nil {
+		return err
+	}
+
+	err = mysql.db.Ping()
+	if err != nil {
+		return err
+	}
+
+	mysql.db.SetMaxOpenConns(maxConnectionsInPool)
+	mysql.db.SetMaxIdleConns(maxConnectionsInPool)
+
+	if mysql.selectStmt, err = mysql.mustPrepare(selectQueryStrMySQL); err != nil {
+		return err
+	}
+	if mysql.fortuneStmt, err = mysql.mustPrepare(fortuneQueryStrMySQL); err != nil {
+		return err
+	}
+	if mysql.updateStmt, err = mysql.mustPrepare(updateQueryStrMySQL); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Close connect to db
+func (mysql *MySQL) Close() {
+	mysql.db.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (mysql MySQL) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err = mysql.selectStmt.QueryRow(queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
+		err = fmt.Errorf("error scanning world row with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (mysql MySQL) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = mysql.GetOneRandomWorld()
+	}
+
+	if len(selectedWorlds) > 0 {
+		// against deadlocks
+		sort.Slice(selectedWorlds, func(i, j int) bool {
+			return selectedWorlds[i].ID < selectedWorlds[j].ID
+		})
+
+		tx, err := mysql.db.Begin()
+		if err != nil {
+			return selectedWorlds, err
+		}
+
+		for _, selectedWorld := range selectedWorlds {
+			selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+			if _, err := tx.Stmt(mysql.updateStmt).Exec(selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
+				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
+				tx.Rollback()
+			}
+		}
+
+		if err := tx.Commit(); err != nil {
+			tx.Rollback()
+			return selectedWorlds, err
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes selects all fortunes from table
+func (mysql MySQL) GetFortunes() ([]templates.Fortune, error) {
+	rows, err := mysql.fortuneStmt.Query()
+	defer rows.Close()
+	if err != nil {
+		return nil, fmt.Errorf("can't query fortunes: %s", err)
+	}
+
+	fortunes := make([]templates.Fortune, 0, 16)
+	var fortune templates.Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Printf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+
+	return fortunes, nil
+}
+
+func (mysql MySQL) mustPrepare(query string) (*sql.Stmt, error) {
+	stmt, err := mysql.db.Prepare(query)
+	if err != nil {
+		log.Printf("Error when preparing statement %q: %s\n", query, err)
+		return nil, err
+	}
+	return stmt, nil
+}
+
+// NewMySQLDB creates new connection to postgres db with MySQL driver
+func NewMySQLDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var mysql MySQL
+	if err := mysql.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &mysql, nil
+}
+
+// func dbInterpolateHandler(w http.ResponseWriter, r *http.Request) {
+// 	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.Error())
+// 	}
+
+// 	w.Header().Set("Server", "Go")
+// 	w.Header().Set("Content-Type", "application/json")
+// 	json.NewEncoder(w).Encode(&world)
+// }
+
+// func queriesInterpolateHandler(w http.ResponseWriter, r *http.Request) {
+// 	n := getQueriesParam(r)
+
+// 	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: %v", err)
+// 		}
+// 	}
+
+// 	w.Header().Set("Server", "Go")
+// 	w.Header().Set("Content-Type", "application/json")
+// 	json.NewEncoder(w).Encode(world)
+// }
+
+// func fortuneInterpolateHandler(w http.ResponseWriter, r *http.Request) {
+// 	rows, err := db.Query(fortuneSelect)
+// 	if err != nil {
+// 		log.Fatalf("Error preparing statement: %v", err)
+// 	}
+
+// 	fortunes := fetchFortunes(rows)
+// 	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
+
+// 	sort.Sort(ByMessage{fortunes})
+// 	w.Header().Set("Server", "Go")
+// 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+// 	if err := tmpl.Execute(w, fortunes); err != nil {
+// 		http.Error(w, err.Error(), http.StatusInternalServerError)
+// 	}
+// }
+
+// func updateInterpolateHandler(w http.ResponseWriter, r *http.Request) {
+// 	n := getQueriesParam(r)
+
+// 	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: %v", 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: %v", err)
+// 		}
+// 	}
+
+// 	w.Header().Set("Server", "Go")
+// 	w.Header().Set("Content-Type", "application/json")
+// 	encoder := json.NewEncoder(w)
+// 	encoder.Encode(world)
+// }

+ 149 - 0
frameworks/Go/go-std/src/storage/pgx.go

@@ -0,0 +1,149 @@
+package storage
+
+import (
+	"fmt"
+	"log"
+	"math/rand"
+	"sort"
+
+	"go-std/src/templates"
+
+	"github.com/jackc/pgx"
+)
+
+// PGX struct
+type PGX struct {
+	db *pgx.ConnPool
+	// prepare statements
+	selectStmt  *pgx.PreparedStatement
+	updateStmt  *pgx.PreparedStatement
+	fortuneStmt *pgx.PreparedStatement
+}
+
+// Connect create connection and ping db
+func (psql *PGX) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var parsedConfig pgx.ConnConfig
+	var err error
+	parsedConfig, err = pgx.ParseConnectionString(dbConnectionString)
+	if err != nil {
+		return err
+	}
+
+	var config pgx.ConnPoolConfig
+	config.User = parsedConfig.User
+	config.Host = parsedConfig.Host
+	config.Password = parsedConfig.Password
+	config.Database = parsedConfig.Database
+
+	config.MaxConnections = maxConnectionsInPool
+
+	config.AfterConnect = func(conn *pgx.Conn) error {
+		var err error
+		if psql.selectStmt, err = mustPrepare(conn, "selectStmt", selectQueryStrPostgre); err != nil {
+			return err
+		}
+		if psql.updateStmt, err = mustPrepare(conn, "updateStmt", updateQueryStrPostgre); err != nil {
+			return err
+		}
+		if psql.fortuneStmt, err = mustPrepare(conn, "fortuneStmt", fortuneQueryStrPostgre); err != nil {
+			return err
+		}
+		return nil
+	}
+
+	psql.db, err = pgx.NewConnPool(config)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Close connect to db
+func (psql *PGX) Close() {
+	psql.db.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (psql PGX) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err = psql.db.QueryRow("selectStmt", queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
+		err = fmt.Errorf("error scanning world row with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (psql PGX) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = psql.GetOneRandomWorld()
+	}
+
+	if len(selectedWorlds) > 0 {
+		// against deadlocks
+		sort.Slice(selectedWorlds, func(i, j int) bool {
+			return selectedWorlds[i].ID < selectedWorlds[j].ID
+		})
+
+		tx, err := psql.db.Begin()
+		if err != nil {
+			return selectedWorlds, err
+		}
+
+		for _, selectedWorld := range selectedWorlds {
+			selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+			if _, err := tx.Exec("updateStmt", selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
+				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
+				tx.Rollback()
+			}
+		}
+
+		if err := tx.Commit(); err != nil {
+			tx.Rollback()
+			return selectedWorlds, err
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes selects all fortunes from table
+func (psql PGX) GetFortunes() ([]templates.Fortune, error) {
+	rows, err := psql.db.Query("fortuneStmt")
+	defer rows.Close()
+	if err != nil {
+		return nil, fmt.Errorf("can't query fortunes: %s", err)
+	}
+
+	fortunes := make([]templates.Fortune, 0, 16)
+	var fortune templates.Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Printf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+
+	return fortunes, nil
+}
+
+func mustPrepare(db *pgx.Conn, name, query string) (*pgx.PreparedStatement, error) {
+	stmt, err := db.Prepare(name, query)
+	if err != nil {
+		log.Printf("Error when preparing statement %q: %s\n", query, err)
+		return nil, err
+	}
+	return stmt, nil
+}
+
+// NewPgxDB creates new connection to postgres db with pgx driver
+func NewPgxDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var psql PGX
+	if err := psql.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &psql, nil
+}

+ 140 - 0
frameworks/Go/go-std/src/storage/pq.go

@@ -0,0 +1,140 @@
+package storage
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+	"math/rand"
+	"sort"
+
+	"go-std/src/templates"
+
+	_ "github.com/lib/pq" // postgresql import
+)
+
+// PQ struct
+type PQ struct {
+	db *sql.DB
+	// prepare statements
+	selectStmt  *sql.Stmt
+	updateStmt  *sql.Stmt
+	fortuneStmt *sql.Stmt
+}
+
+// Connect create connection and ping db
+func (psql *PQ) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var err error
+	psql.db, err = sql.Open("postgres", dbConnectionString)
+	if err != nil {
+		return err
+	}
+
+	err = psql.db.Ping()
+	if err != nil {
+		return err
+	}
+
+	psql.db.SetMaxOpenConns(maxConnectionsInPool)
+	psql.db.SetMaxIdleConns(maxConnectionsInPool)
+
+	if psql.selectStmt, err = psql.mustPrepare(selectQueryStrPostgre); err != nil {
+		return err
+	}
+	if psql.fortuneStmt, err = psql.mustPrepare(fortuneQueryStrPostgre); err != nil {
+		return err
+	}
+	if psql.updateStmt, err = psql.mustPrepare(updateQueryStrPostgre); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Close connect to db
+func (psql *PQ) Close() {
+	psql.db.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (psql PQ) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err = psql.selectStmt.QueryRow(queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
+		err = fmt.Errorf("error scanning world row with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (psql PQ) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = psql.GetOneRandomWorld()
+	}
+
+	if len(selectedWorlds) > 0 {
+		// against deadlocks
+		sort.Slice(selectedWorlds, func(i, j int) bool {
+			return selectedWorlds[i].ID < selectedWorlds[j].ID
+		})
+
+		tx, err := psql.db.Begin()
+		if err != nil {
+			return selectedWorlds, err
+		}
+
+		for _, selectedWorld := range selectedWorlds {
+			selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+			if _, err := tx.Stmt(psql.updateStmt).Exec(selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
+				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
+				tx.Rollback()
+			}
+		}
+
+		if err := tx.Commit(); err != nil {
+			tx.Rollback()
+			return selectedWorlds, err
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes selects all fortunes from table
+func (psql PQ) GetFortunes() ([]templates.Fortune, error) {
+	rows, err := psql.fortuneStmt.Query()
+	defer rows.Close()
+	if err != nil {
+		return nil, fmt.Errorf("can't query fortunes: %s", err)
+	}
+
+	fortunes := make([]templates.Fortune, 0, 16)
+	var fortune templates.Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Printf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+
+	return fortunes, nil
+}
+
+func (psql PQ) mustPrepare(query string) (*sql.Stmt, error) {
+	stmt, err := psql.db.Prepare(query)
+	if err != nil {
+		log.Printf("Error when preparing statement %q: %s\n", query, err)
+		return nil, err
+	}
+	return stmt, nil
+}
+
+// NewPqDB creates new connection to postgres db with pq driver
+func NewPqDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var psql PQ
+	if err := psql.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &psql, nil
+}

+ 10 - 0
frameworks/Go/go-std/src/storage/world.go

@@ -0,0 +1,10 @@
+package storage
+
+//easyjson:json
+type World struct {
+	ID           int `json:"id"`
+	RandomNumber int `json:"randomnumber"`
+}
+
+//easyjson:json
+type Worlds []World

+ 168 - 0
frameworks/Go/go-std/src/storage/world_easyjson.go

@@ -0,0 +1,168 @@
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+
+package storage
+
+import (
+	json "encoding/json"
+	easyjson "github.com/mailru/easyjson"
+	jlexer "github.com/mailru/easyjson/jlexer"
+	jwriter "github.com/mailru/easyjson/jwriter"
+)
+
+// suppress unused package warning
+var (
+	_ *json.RawMessage
+	_ *jlexer.Lexer
+	_ *jwriter.Writer
+	_ easyjson.Marshaler
+)
+
+func easyjson4da0dabeDecodeGoStdSrcStorage(in *jlexer.Lexer, out *Worlds) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		in.Skip()
+		*out = nil
+	} else {
+		in.Delim('[')
+		if *out == nil {
+			if !in.IsDelim(']') {
+				*out = make(Worlds, 0, 4)
+			} else {
+				*out = Worlds{}
+			}
+		} else {
+			*out = (*out)[:0]
+		}
+		for !in.IsDelim(']') {
+			var v1 World
+			(v1).UnmarshalEasyJSON(in)
+			*out = append(*out, v1)
+			in.WantComma()
+		}
+		in.Delim(']')
+	}
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjson4da0dabeEncodeGoStdSrcStorage(out *jwriter.Writer, in Worlds) {
+	if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
+		out.RawString("null")
+	} else {
+		out.RawByte('[')
+		for v2, v3 := range in {
+			if v2 > 0 {
+				out.RawByte(',')
+			}
+			(v3).MarshalEasyJSON(out)
+		}
+		out.RawByte(']')
+	}
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v Worlds) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjson4da0dabeEncodeGoStdSrcStorage(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v Worlds) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjson4da0dabeEncodeGoStdSrcStorage(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *Worlds) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjson4da0dabeDecodeGoStdSrcStorage(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *Worlds) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjson4da0dabeDecodeGoStdSrcStorage(l, v)
+}
+func easyjson4da0dabeDecodeGoStdSrcStorage1(in *jlexer.Lexer, out *World) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "id":
+			out.ID = int(in.Int())
+		case "randomnumber":
+			out.RandomNumber = int(in.Int())
+		default:
+			in.SkipRecursive()
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjson4da0dabeEncodeGoStdSrcStorage1(out *jwriter.Writer, in World) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"id\":"
+		if first {
+			first = false
+			out.RawString(prefix[1:])
+		} else {
+			out.RawString(prefix)
+		}
+		out.Int(int(in.ID))
+	}
+	{
+		const prefix string = ",\"randomnumber\":"
+		if first {
+			first = false
+			out.RawString(prefix[1:])
+		} else {
+			out.RawString(prefix)
+		}
+		out.Int(int(in.RandomNumber))
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v World) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjson4da0dabeEncodeGoStdSrcStorage1(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v World) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjson4da0dabeEncodeGoStdSrcStorage1(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *World) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjson4da0dabeDecodeGoStdSrcStorage1(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *World) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjson4da0dabeDecodeGoStdSrcStorage1(l, v)
+}

+ 39 - 0
frameworks/Go/go-std/src/templates/fortune.go

@@ -0,0 +1,39 @@
+package templates
+
+import "html/template"
+
+//go:generate qtc
+
+const (
+	fortuneHTML = `<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .}}
+<tr>
+<td>{{.ID}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+</body>
+</html>`
+)
+
+var (
+	// FortuneTemplate ...
+	FortuneTemplate = template.Must(template.New("fortune.html").Parse(fortuneHTML))
+)
+
+// Fortune struct
+type Fortune struct {
+	ID      int    `json:"id,omitempty"`
+	Message string `json:"message,omitempty"`
+}

+ 102 - 0
frameworks/Go/go-std/src/templates/fortune_easyjson.go

@@ -0,0 +1,102 @@
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+
+package templates
+
+import (
+	json "encoding/json"
+	easyjson "github.com/mailru/easyjson"
+	jlexer "github.com/mailru/easyjson/jlexer"
+	jwriter "github.com/mailru/easyjson/jwriter"
+)
+
+// suppress unused package warning
+var (
+	_ *json.RawMessage
+	_ *jlexer.Lexer
+	_ *jwriter.Writer
+	_ easyjson.Marshaler
+)
+
+func easyjson2f1218d5DecodeGoStdSrcTemplates(in *jlexer.Lexer, out *Fortune) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "id":
+			out.ID = int(in.Int())
+		case "message":
+			out.Message = string(in.String())
+		default:
+			in.SkipRecursive()
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjson2f1218d5EncodeGoStdSrcTemplates(out *jwriter.Writer, in Fortune) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	if in.ID != 0 {
+		const prefix string = ",\"id\":"
+		if first {
+			first = false
+			out.RawString(prefix[1:])
+		} else {
+			out.RawString(prefix)
+		}
+		out.Int(int(in.ID))
+	}
+	if in.Message != "" {
+		const prefix string = ",\"message\":"
+		if first {
+			first = false
+			out.RawString(prefix[1:])
+		} else {
+			out.RawString(prefix)
+		}
+		out.String(string(in.Message))
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v Fortune) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjson2f1218d5EncodeGoStdSrcTemplates(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v Fortune) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjson2f1218d5EncodeGoStdSrcTemplates(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *Fortune) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjson2f1218d5DecodeGoStdSrcTemplates(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *Fortune) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjson2f1218d5DecodeGoStdSrcTemplates(l, v)
+}

+ 0 - 7
frameworks/Go/go-std/src/templates/fortunes.qtpl

@@ -1,10 +1,3 @@
-{% code
-type Fortune struct {
-	ID int32
-	Message string
-}
-%}
-
 {% func FortunePage(rows []Fortune) %}<!DOCTYPE html>
 <html>
 <head>

+ 81 - 0
frameworks/Go/go-std/src/templates/fortunes.qtpl.go

@@ -0,0 +1,81 @@
+// This file is automatically generated by qtc from "fortunes.qtpl".
+// See https://github.com/valyala/quicktemplate for details.
+
+//line fortunes.qtpl:1
+package templates
+
+//line fortunes.qtpl:1
+import (
+	qtio422016 "io"
+
+	qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line fortunes.qtpl:1
+var (
+	_ = qtio422016.Copy
+	_ = qt422016.AcquireByteBuffer
+)
+
+//line fortunes.qtpl:1
+func StreamFortunePage(qw422016 *qt422016.Writer, rows []Fortune) {
+	//line fortunes.qtpl:1
+	qw422016.N().S(`<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+`)
+	//line fortunes.qtpl:9
+	for _, r := range rows {
+		//line fortunes.qtpl:9
+		qw422016.N().S(`
+<tr><td>`)
+		//line fortunes.qtpl:10
+		qw422016.N().D(int(r.ID))
+		//line fortunes.qtpl:10
+		qw422016.N().S(`</td><td>`)
+		//line fortunes.qtpl:10
+		qw422016.E().S(r.Message)
+		//line fortunes.qtpl:10
+		qw422016.N().S(`</td></tr>
+`)
+		//line fortunes.qtpl:11
+	}
+	//line fortunes.qtpl:11
+	qw422016.N().S(`
+</table>
+</body>
+</html>
+`)
+//line fortunes.qtpl:15
+}
+
+//line fortunes.qtpl:15
+func WriteFortunePage(qq422016 qtio422016.Writer, rows []Fortune) {
+	//line fortunes.qtpl:15
+	qw422016 := qt422016.AcquireWriter(qq422016)
+	//line fortunes.qtpl:15
+	StreamFortunePage(qw422016, rows)
+	//line fortunes.qtpl:15
+	qt422016.ReleaseWriter(qw422016)
+//line fortunes.qtpl:15
+}
+
+//line fortunes.qtpl:15
+func FortunePage(rows []Fortune) string {
+	//line fortunes.qtpl:15
+	qb422016 := qt422016.AcquireByteBuffer()
+	//line fortunes.qtpl:15
+	WriteFortunePage(qb422016, rows)
+	//line fortunes.qtpl:15
+	qs422016 := string(qb422016.B)
+	//line fortunes.qtpl:15
+	qt422016.ReleaseByteBuffer(qb422016)
+	//line fortunes.qtpl:15
+	return qs422016
+//line fortunes.qtpl:15
+}

+ 20 - 5
frameworks/Go/iris/benchmark_config.json

@@ -4,6 +4,23 @@
     "default": {
       "json_url": "/json",
       "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "None",
+      "language": "Go",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Iris",
+      "notes": "",
+      "versus": "go"
+    },
+    "pgx": {
       "fortune_url": "/fortune",
       "db_url": "/db",
       "query_url": "/queries?queries=",
@@ -24,9 +41,7 @@
       "notes": "",
       "versus": "go"
     },
-    "prefork": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
+    "pgx-prefork": {
       "fortune_url": "/fortune",
       "db_url": "/db",
       "query_url": "/queries?queries=",
@@ -47,7 +62,7 @@
       "notes": "",
       "versus": "go"
     },
-    "quicktemplate": {
+    "pgx-quicktemplate": {
       "fortune_url": "/fortune-quick",
       "port": 8080,
       "approach": "Realistic",
@@ -65,7 +80,7 @@
       "notes": "",
       "versus": "go"
     },
-    "quicktemplate-prefork": {
+    "pgx-quicktemplate-prefork": {
       "fortune_url": "/fortune-quick",
       "port": 8080,
       "approach": "Realistic",

+ 17 - 0
frameworks/Go/iris/iris-pgx-prefork.dockerfile

@@ -0,0 +1,17 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
+
+RUN go get github.com/kataras/iris
+RUN go get github.com/lib/pq
+RUN go get github.com/valyala/quicktemplate
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
+
+RUN go generate ./templates
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -prefork -db pgx

+ 17 - 0
frameworks/Go/iris/iris-pgx-quicktemplate-prefork.dockerfile

@@ -0,0 +1,17 @@
+FROM golang:1.11.5
+
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
+
+RUN go get github.com/kataras/iris
+RUN go get github.com/lib/pq
+RUN go get github.com/valyala/quicktemplate
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
+
+RUN go generate ./templates
+RUN go build -ldflags="-s -w" -o app .
+
+CMD ./app -prefork -db pgx

+ 8 - 5
frameworks/Go/iris/iris-quicktemplate-prefork.dockerfile → frameworks/Go/iris/iris-pgx-quicktemplate.dockerfile

@@ -1,14 +1,17 @@
 FROM golang:1.11.5
 
-WORKDIR /go/src/app
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
 
 RUN go get github.com/kataras/iris
 RUN go get github.com/lib/pq
 RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-COPY src/. .
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
 
-RUN qtc
+RUN go generate ./templates
 RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app -prefork
+CMD ./app -db pgx

+ 8 - 5
frameworks/Go/iris/iris-quicktemplate.dockerfile → frameworks/Go/iris/iris-pgx.dockerfile

@@ -1,14 +1,17 @@
 FROM golang:1.11.5
 
-WORKDIR /go/src/app
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
 
 RUN go get github.com/kataras/iris
 RUN go get github.com/lib/pq
 RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-COPY src/. .
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
 
-RUN qtc
+RUN go generate ./templates
 RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app
+CMD ./app -db pgx

+ 7 - 4
frameworks/Go/iris/iris-prefork.dockerfile

@@ -1,14 +1,17 @@
 FROM golang:1.11.5
 
-WORKDIR /go/src/app
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
 
 RUN go get github.com/kataras/iris
 RUN go get github.com/lib/pq
 RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-COPY src/. .
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
 
-RUN qtc
+RUN go generate ./templates
 RUN go build -ldflags="-s -w" -o app .
 
 CMD ./app -prefork

+ 7 - 4
frameworks/Go/iris/iris.dockerfile

@@ -1,14 +1,17 @@
 FROM golang:1.11.5
 
-WORKDIR /go/src/app
+ENV GO111MODULE on
+WORKDIR /iris
+
+COPY src/. .
 
 RUN go get github.com/kataras/iris
 RUN go get github.com/lib/pq
 RUN go get github.com/valyala/quicktemplate
-RUN go get -u github.com/valyala/quicktemplate/qtc
-COPY src/. .
+RUN go get github.com/valyala/quicktemplate/qtc
+RUN go mod download
 
-RUN qtc
+RUN go generate ./templates
 RUN go build -ldflags="-s -w" -o app .
 
 CMD ./app

+ 0 - 101
frameworks/Go/iris/src/db.go

@@ -1,101 +0,0 @@
-package main
-
-import (
-	"database/sql"
-	"log"
-	"math/rand"
-	"sort"
-)
-
-const (
-	selectQueryStr  = "SELECT id, randomNumber FROM World WHERE id = $1"
-	updateQueryStr  = "UPDATE World SET randomNumber = $1 WHERE id = $2"
-	fortuneQueryStr = "SELECT id, message FROM Fortune"
-)
-
-var (
-	selectStmt  *sql.Stmt
-	updateStmt  *sql.Stmt
-	fortuneStmt *sql.Stmt
-)
-
-func getOneRandomWorld() World {
-	var w World
-	var queryID int
-	queryID = rand.Intn(10000) + 1
-	if err := selectStmt.QueryRow(queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
-		log.Printf("Error scanning world row with ID %d: %s\n", queryID, err)
-	}
-	return w
-}
-
-func updateRandomWorlds(db *sql.DB, queries int) []World {
-	selectedWorlds := make([]World, queries)
-	for i := 0; i < queries; i++ {
-		selectedWorlds[i] = getOneRandomWorld()
-	}
-
-	if len(selectedWorlds) > 0 {
-		// against deadlocks
-		sort.Slice(selectedWorlds, func(i, j int) bool {
-			return selectedWorlds[i].ID < selectedWorlds[j].ID
-		})
-
-		tx, err := db.Begin()
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		for _, selectedWorld := range selectedWorlds {
-			selectedWorld.RandomNumber = rand.Intn(10000) + 1
-			if _, err := tx.Stmt(updateStmt).Exec(selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
-				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
-				tx.Rollback()
-			}
-		}
-
-		if err := tx.Commit(); err != nil {
-			log.Fatal(err)
-		}
-	}
-
-	return selectedWorlds
-}
-
-func mustPrepare(db *sql.DB, query string) (*sql.Stmt, error) {
-	stmt, err := db.Prepare(query)
-	if err != nil {
-		log.Printf("Error when preparing statement %q: %s\n", query, err)
-		return nil, err
-	}
-	return stmt, nil
-}
-
-func initDatabase(dbConnectionString string, maxConnectionsInPool int) (*sql.DB, error) {
-	var err error
-	var db *sql.DB
-	db, err = sql.Open("postgres", dbConnectionString)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = db.Ping(); err != nil {
-		return nil, err
-	}
-
-	maxConnectionsInPool = maxConnectionsInPool * 4
-	db.SetMaxOpenConns(maxConnectionsInPool)
-	db.SetMaxIdleConns(maxConnectionsInPool)
-
-	if selectStmt, err = mustPrepare(db, selectQueryStr); err != nil {
-		return nil, err
-	}
-	if fortuneStmt, err = mustPrepare(db, fortuneQueryStr); err != nil {
-		return nil, err
-	}
-	if updateStmt, err = mustPrepare(db, updateQueryStr); err != nil {
-		return nil, err
-	}
-
-	return db, nil
-}

+ 61 - 0
frameworks/Go/iris/src/go.mod

@@ -0,0 +1,61 @@
+module iris/src
+
+require (
+	github.com/BurntSushi/toml v0.3.1 // indirect
+	github.com/Joker/jade v1.0.0 // indirect
+	github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 // indirect
+	github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f // indirect
+	github.com/aymerick/raymond v2.0.2+incompatible // indirect
+	github.com/cockroachdb/apd v1.1.0 // indirect
+	github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect
+	github.com/fatih/structs v1.1.0 // indirect
+	github.com/flosch/pongo2 v0.0.0-20181225140029-79872a7b2769 // indirect
+	github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d // indirect
+	github.com/google/go-querystring v1.0.0 // indirect
+	github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
+	github.com/imkira/go-interpol v1.1.0 // indirect
+	github.com/iris-contrib/blackfriday v2.0.0+incompatible // indirect
+	github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1 // indirect
+	github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect
+	github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce // indirect
+	github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
+	github.com/jackc/pgx v3.3.0+incompatible
+	github.com/json-iterator/go v1.1.5 // indirect
+	github.com/jtolds/gls v4.2.1+incompatible // indirect
+	github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
+	github.com/kataras/golog v0.0.0-20180321173939-03be10146386 // indirect
+	github.com/kataras/iris v11.1.1+incompatible
+	github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect
+	github.com/klauspost/compress v1.4.1 // indirect
+	github.com/klauspost/cpuid v1.2.0 // indirect
+	github.com/lib/pq v1.0.0
+	github.com/mattn/go-colorable v0.1.0 // indirect
+	github.com/mattn/go-isatty v0.0.4 // indirect
+	github.com/microcosm-cc/bluemonday v1.0.2 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.1 // indirect
+	github.com/moul/http2curl v1.0.0 // indirect
+	github.com/onsi/ginkgo v1.7.0 // indirect
+	github.com/onsi/gomega v1.4.3 // indirect
+	github.com/pkg/errors v0.8.1 // indirect
+	github.com/ryanuber/columnize v2.1.0+incompatible // indirect
+	github.com/satori/go.uuid v1.2.0 // indirect
+	github.com/sergi/go-diff v1.0.0 // indirect
+	github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
+	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+	github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect
+	github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
+	github.com/stretchr/testify v1.3.0 // indirect
+	github.com/valyala/fasthttp v1.2.0 // indirect
+	github.com/valyala/quicktemplate v1.0.2
+	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+	github.com/xeipuuv/gojsonschema v1.1.0 // indirect
+	github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
+	github.com/yudai/gojsondiff v1.0.0 // indirect
+	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
+	github.com/yudai/pp v2.0.1+incompatible // indirect
+	golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 // indirect
+	golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect
+	golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect
+)

+ 171 - 0
frameworks/Go/iris/src/go.sum

@@ -0,0 +1,171 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a h1:PiDAizhfJbwZMISZ1Itx1ZTFeOFCml89Ofmz3V8rhoU=
+github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE=
+github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc=
+github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8=
+github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
+github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
+github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmDlfhUrweEy1R1Fj1gu5iIM=
+github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
+github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
+github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
+github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
+github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
+github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/flosch/pongo2 v0.0.0-20181225140029-79872a7b2769 h1:XToLChWPMXLomJ2InnkrmUkddaXfevrmomMTFL+MaKU=
+github.com/flosch/pongo2 v0.0.0-20181225140029-79872a7b2769/go.mod h1:tbAXHifHQWNSpWbiJHpJTZH5fi3XHhDMdP//vuz9WS4=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d h1:oYXrtNhqNKL1dVtKdv8XUq5zqdGVFNQ0/4tvccXZOLM=
+github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA=
+github.com/go-check/check v1.0.0-20180628173108-788fd7840127 h1:3dbHpVjNKf7Myfit4Xmw4BA0JbCt47OJPhMQ5w8O3E8=
+github.com/go-check/check v1.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
+github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
+github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4=
+github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
+github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1 h1:7GsNnSLoVceNylMpwcfy5aFNz/S5/TV25crb34I5PEo=
+github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w=
+github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE=
+github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
+github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce h1:q8Ka/exfHNgK7izJE+aUOZd7KZXJ7oQbnJWiZakEiMo=
+github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce/go.mod h1:VER17o2JZqquOx41avolD/wMGQSFEFBKWmhag9/RQRY=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
+github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
+github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok=
+github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
+github.com/juju/loggo v0.0.0-20180524022052-584905176618 h1:MK144iBQF9hTSwBW/9eJm034bVoG30IshVm688T2hi8=
+github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
+github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs=
+github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/kataras/golog v0.0.0-20180321173939-03be10146386 h1:VT6AeCHO/mc+VedKBMhoqb5eAK8B1i9F6nZl7EGlHvA=
+github.com/kataras/golog v0.0.0-20180321173939-03be10146386/go.mod h1:PcaEvfvhGsqwXZ6S3CgCbmjcp+4UDUh2MIfF2ZEul8M=
+github.com/kataras/iris v11.1.1+incompatible h1:c2iRKvKLpTYMXKdVB8YP/+A67NtZFt9kFFy+ZwBhWD0=
+github.com/kataras/iris v11.1.1+incompatible/go.mod h1:ki9XPua5SyAJbIxDdsssxevgGrbpBmmvoQmo/A0IodY=
+github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d h1:V5Rs9ztEWdp58oayPq/ulmlqJJZeJP6pP79uP3qjcao=
+github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
+github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8=
+github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM=
+github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
+github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
+github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
+github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
+github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
+github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v0.0.0-20180905170723-c6fd90e432cc/go.mod h1:+g/po7GqyG5E+1CNgquiIxJnsXEi5vwFn5weFujbO78=
+github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
+github.com/valyala/quicktemplate v1.0.2 h1:ZeVRKan1W/25u5f9ilDo5HWtdxD+QxR10WrZwxqLM54=
+github.com/valyala/quicktemplate v1.0.2/go.mod h1:KZAB+RlYlfNtBUGQMzIrnE8uuNgD2SbUn5CpZyod0sk=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg=
+github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
+github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
+github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
+github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
+github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 78 - 64
frameworks/Go/iris/src/handlers.go

@@ -1,111 +1,125 @@
 package main
 
 import (
-	"app/templates"
-	"database/sql"
 	"log"
 	"sort"
 	"strconv"
 
+	"iris/src/templates"
+
+	"iris/src/storage"
+
 	"github.com/kataras/iris/context"
 )
 
+// Test 1: JSON serialization
 func jsonHandler(ctx context.Context) {
 	ctx.Header("Server", "Iris")
 	ctx.JSON(context.Map{"message": "Hello, World!"})
 }
 
-func plaintextHandler(ctx context.Context) {
-	ctx.Header("Server", "Iris")
-	ctx.Text("Hello, World!")
-}
+// Test 2: Single database query
+func dbHandler(db storage.DB) func(ctx context.Context) {
+	return func(ctx context.Context) {
+		world, err := db.GetOneRandomWorld()
+		if err != nil {
+			log.Println(err)
+			return
+		}
 
-func dbHandler(ctx context.Context) {
-	ctx.Header("Server", "Iris")
-	ctx.JSON(getOneRandomWorld())
+		ctx.Header("Server", "Iris")
+		ctx.JSON(world)
+	}
 }
 
-func queriesHandler(ctx context.Context) {
+func queriesParams(ctx context.Context) int {
 	q, err := strconv.Atoi(ctx.URLParam("queries"))
 	if err != nil || q < 1 {
 		q = 1
 	} else if q > 500 {
 		q = 500
 	}
+	return q
+}
 
-	results := make([]World, q)
+// Test 3: Multiple database queries
+func queriesHandler(db storage.DB) func(ctx context.Context) {
+	return func(ctx context.Context) {
+		var err error
+		q := queriesParams(ctx)
+		results := make([]storage.World, q)
+
+		for i := 0; i < q; i++ {
+			results[i], err = db.GetOneRandomWorld()
+			if err != nil {
+				log.Println(err)
+			}
+		}
 
-	for i := 0; i < q; i++ {
-		results[i] = getOneRandomWorld()
+		ctx.Header("Server", "Iris")
+		ctx.JSON(results)
 	}
-
-	ctx.Header("Server", "Iris")
-	ctx.JSON(results)
 }
 
-func updateHandler(db *sql.DB) func(ctx context.Context) {
+// Test 4: Fortunes
+func fortuneHandler(db storage.DB) func(ctx context.Context) {
 	return func(ctx context.Context) {
-		q, err := strconv.Atoi(ctx.URLParam("queries"))
-		if err != nil || q < 1 {
-			q = 1
-		} else if q > 500 {
-			q = 500
+		fortunes, err := db.GetFortunes()
+		if err != nil {
+			log.Println(err)
+			return
 		}
+		fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
+
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
 
 		ctx.Header("Server", "Iris")
-		ctx.JSON(updateRandomWorlds(db, q))
+		ctx.View("fortunes.html", struct {
+			Fortunes []templates.Fortune
+		}{fortunes})
 	}
 }
 
-func fortuneHandler(ctx context.Context) {
-	rows, err := fortuneStmt.Query()
-	if err != nil {
-		log.Fatalf("Can't query fortunes: %s\n", err)
-	}
-
-	fortunes := make([]Fortune, 0, 16)
-	var fortune Fortune
-	for rows.Next() {
-		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
-			log.Fatalf("Can't scan fortune: %s\n", err)
+// Test 4: Fortunes
+func fortuneQuickHandler(db storage.DB) func(ctx context.Context) {
+	return func(ctx context.Context) {
+		fortunes, err := db.GetFortunes()
+		if err != nil {
+			log.Println(err)
+			return
 		}
-		fortunes = append(fortunes, fortune)
-	}
-	rows.Close()
-	fortunes = append(fortunes, Fortune{Message: "Additional fortune added at request time."})
+		fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
 
-	sort.Slice(fortunes, func(i, j int) bool {
-		return fortunes[i].Message < fortunes[j].Message
-	})
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
 
-	ctx.Header("Server", "Iris")
-	ctx.View("fortunes.html", struct {
-		Fortunes []Fortune
-	}{fortunes})
+		ctx.Header("Server", "Iris")
+		ctx.ContentType("text/html; charset=utf-8")
+		templates.WriteFortunePage(ctx.ResponseWriter(), fortunes)
+	}
 }
 
-func fortuneQuickHandler(ctx context.Context) {
-	rows, err := fortuneStmt.Query()
-	if err != nil {
-		log.Fatalf("Can't query fortunes: %s\n", err)
-	}
+// Test 5: Database updates
+func updateHandler(db storage.DB) func(ctx context.Context) {
+	return func(ctx context.Context) {
+		q := queriesParams(ctx)
 
-	fortunes := make([]templates.Fortune, 0, 16)
-	var fortune templates.Fortune
-	for rows.Next() {
-		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
-			log.Fatalf("Can't scan fortune: %s\n", err)
+		worlds, err := db.UpdateRandomWorlds(q)
+		if err != nil {
+			log.Println(err)
+			return
 		}
-		fortunes = append(fortunes, fortune)
-	}
-	rows.Close()
-	fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
 
-	sort.Slice(fortunes, func(i, j int) bool {
-		return fortunes[i].Message < fortunes[j].Message
-	})
+		ctx.Header("Server", "Iris")
+		ctx.JSON(worlds)
+	}
+}
 
+// Test 6: Plaintext
+func plaintextHandler(ctx context.Context) {
 	ctx.Header("Server", "Iris")
-	ctx.ContentType("text/html; charset=utf-8")
-	templates.WriteFortunePage(ctx.ResponseWriter(), fortunes)
+	ctx.Text("Hello, World!")
 }

+ 18 - 15
frameworks/Go/iris/src/main.go

@@ -8,10 +8,9 @@ import (
 	"runtime"
 	"time"
 
-	"github.com/kataras/iris"
-
-	_ "github.com/lib/pq"
+	"iris/src/storage"
 
+	"github.com/kataras/iris"
 	"github.com/kataras/iris/middleware/recover"
 )
 
@@ -20,6 +19,7 @@ func main() {
 	bindHost := flag.String("bind", ":8080", "set bind host")
 	prefork := flag.Bool("prefork", false, "use prefork")
 	child := flag.Bool("child", false, "is child proc")
+	dbDriver := flag.String("db", "none", "db connection driver [values: pq || pgx || none]")
 	dbConnectionString := flag.String("db_connection_string",
 		"host=tfb-database user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world sslmode=disable",
 		"Set bind host")
@@ -29,30 +29,33 @@ func main() {
 	var listener net.Listener
 	if *prefork {
 		listener = doPrefork(*child, *bindHost)
+	} else {
+		runtime.GOMAXPROCS(runtime.NumCPU())
 	}
 
 	// init iris app
 	app := iris.New()
 	app.Use(recover.New())
-	app.RegisterView(iris.HTML("./templates", ".html"))
 
-	// init database
-	db, err := initDatabase(
-		*dbConnectionString,
-		runtime.NumCPU())
+	// init database with appropriate driver
+	db, err := storage.InitDB(*dbDriver, *dbConnectionString)
 	if err != nil {
-		log.Fatalf("Error opening database: %s", err)
+		log.Fatal(err)
 	}
-	defer db.Close()
 
 	// add handlers
 	app.Handle("GET", "/json", jsonHandler)
 	app.Handle("GET", "/plaintext", plaintextHandler)
-	app.Handle("GET", "/db", dbHandler)
-	app.Handle("GET", "/queries", queriesHandler)
-	app.Handle("GET", "/update", updateHandler(db))
-	app.Handle("GET", "/fortune", fortuneHandler)
-	app.Handle("GET", "/fortune-quick", fortuneQuickHandler)
+	if db != nil {
+		defer db.Close()
+		app.Handle("GET", "/db", dbHandler(db))
+		app.Handle("GET", "/queries", queriesHandler(db))
+		app.Handle("GET", "/update", updateHandler(db))
+
+		app.RegisterView(iris.HTML("./templates", ".html"))
+		app.Handle("GET", "/fortune", fortuneHandler(db))
+		app.Handle("GET", "/fortune-quick", fortuneQuickHandler(db))
+	}
 
 	iris.RegisterOnInterrupt(func() {
 		timeout := 10 * time.Second

+ 4 - 2
frameworks/Go/iris/src/prefork.go

@@ -23,9 +23,10 @@ func doPrefork(child bool, toBind string) net.Listener {
 		if err != nil {
 			log.Fatal(err)
 		}
-		children := make([]*exec.Cmd, runtime.NumCPU()/2)
+		children := make([]*exec.Cmd, runtime.NumCPU())
 		for i := range children {
-			children[i] = exec.Command(os.Args[0], "-prefork", "-child")
+			log.Println(append(os.Args[1:], "-child"))
+			children[i] = exec.Command(os.Args[0], append(os.Args[1:], "-child")...)
 			children[i].Stdout = os.Stdout
 			children[i].Stderr = os.Stderr
 			children[i].ExtraFiles = []*os.File{fl}
@@ -45,6 +46,7 @@ func doPrefork(child bool, toBind string) net.Listener {
 		if err != nil {
 			log.Fatal(err)
 		}
+		runtime.GOMAXPROCS(1)
 	}
 	return listener
 }

+ 53 - 0
frameworks/Go/iris/src/storage/db.go

@@ -0,0 +1,53 @@
+package storage
+
+import (
+	"errors"
+	"fmt"
+	"iris/src/templates"
+	"runtime"
+)
+
+const (
+	selectQueryStr  = "SELECT id, randomNumber FROM World WHERE id = $1"
+	updateQueryStr  = "UPDATE World SET randomNumber = $1 WHERE id = $2"
+	fortuneQueryStr = "SELECT id, message FROM Fortune"
+)
+
+var (
+	worldsCount = 10000
+)
+
+// DB is interface for
+type DB interface {
+	GetOneRandomWorld() (World, error)
+	UpdateRandomWorlds(queries int) ([]World, error)
+	GetFortunes() ([]templates.Fortune, error)
+	Close()
+}
+
+// InitDB with appropriate driver
+func InitDB(dbDriver, dbConnectionString string) (DB, error) {
+	var err error
+	var db DB
+	if dbDriver == "pq" {
+		db, err = NewPqDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening pq database: %s", err)
+		}
+	} else if dbDriver == "pgx" {
+		db, err = NewPgxDB(
+			dbConnectionString,
+			runtime.NumCPU())
+		if err != nil {
+			return nil, fmt.Errorf("Error opening pgx database: %s", err)
+		}
+	} else if dbDriver == "none" {
+		db = nil
+	} else {
+		return nil, errors.New("Can't recognize DB driver type")
+	}
+
+	return db, nil
+}

+ 148 - 0
frameworks/Go/iris/src/storage/pgx.go

@@ -0,0 +1,148 @@
+package storage
+
+import (
+	"fmt"
+	"iris/src/templates"
+	"log"
+	"math/rand"
+	"sort"
+
+	"github.com/jackc/pgx"
+)
+
+// PGX struct
+type PGX struct {
+	db *pgx.ConnPool
+	// prepare statements
+	selectStmt  *pgx.PreparedStatement
+	updateStmt  *pgx.PreparedStatement
+	fortuneStmt *pgx.PreparedStatement
+}
+
+// Connect create connection and ping db
+func (psql *PGX) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var parsedConfig pgx.ConnConfig
+	var err error
+	parsedConfig, err = pgx.ParseConnectionString(dbConnectionString)
+	if err != nil {
+		return err
+	}
+
+	var config pgx.ConnPoolConfig
+	config.User = parsedConfig.User
+	config.Host = parsedConfig.Host
+	config.Password = parsedConfig.Password
+	config.Database = parsedConfig.Database
+
+	config.MaxConnections = maxConnectionsInPool
+
+	config.AfterConnect = func(conn *pgx.Conn) error {
+		var err error
+		if psql.selectStmt, err = mustPrepare(conn, "selectStmt", selectQueryStr); err != nil {
+			return err
+		}
+		if psql.updateStmt, err = mustPrepare(conn, "updateStmt", updateQueryStr); err != nil {
+			return err
+		}
+		if psql.fortuneStmt, err = mustPrepare(conn, "fortuneStmt", fortuneQueryStr); err != nil {
+			return err
+		}
+		return nil
+	}
+
+	psql.db, err = pgx.NewConnPool(config)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Close connect to db
+func (psql *PGX) Close() {
+	psql.db.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (psql PGX) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err := psql.db.QueryRow("selectStmt", queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
+		err = fmt.Errorf("error scanning world row with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (psql PGX) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = psql.GetOneRandomWorld()
+	}
+
+	if len(selectedWorlds) > 0 {
+		// against deadlocks
+		sort.Slice(selectedWorlds, func(i, j int) bool {
+			return selectedWorlds[i].ID < selectedWorlds[j].ID
+		})
+
+		tx, err := psql.db.Begin()
+		if err != nil {
+			return selectedWorlds, err
+		}
+
+		for _, selectedWorld := range selectedWorlds {
+			selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+			if _, err := tx.Exec("updateStmt", selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
+				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
+				tx.Rollback()
+			}
+		}
+
+		if err := tx.Commit(); err != nil {
+			tx.Rollback()
+			return selectedWorlds, err
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes selects all fortunes from table
+func (psql PGX) GetFortunes() ([]templates.Fortune, error) {
+	rows, err := psql.db.Query("fortuneStmt")
+	defer rows.Close()
+	if err != nil {
+		return nil, fmt.Errorf("can't query fortunes: %s", err)
+	}
+
+	fortunes := make([]templates.Fortune, 0, 16)
+	var fortune templates.Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Printf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+
+	return fortunes, nil
+}
+
+func mustPrepare(db *pgx.Conn, name, query string) (*pgx.PreparedStatement, error) {
+	stmt, err := db.Prepare(name, query)
+	if err != nil {
+		log.Printf("Error when preparing statement %q: %s\n", query, err)
+		return nil, err
+	}
+	return stmt, nil
+}
+
+// NewPgxDB creates new connection to postgres db with pgx driver
+func NewPgxDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var psql PGX
+	if err := psql.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &psql, nil
+}

+ 140 - 0
frameworks/Go/iris/src/storage/pq.go

@@ -0,0 +1,140 @@
+package storage
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+	"math/rand"
+	"sort"
+
+	"iris/src/templates"
+
+	_ "github.com/lib/pq" // postgresql import
+)
+
+// PQ struct
+type PQ struct {
+	db *sql.DB
+	// prepare statements
+	selectStmt  *sql.Stmt
+	updateStmt  *sql.Stmt
+	fortuneStmt *sql.Stmt
+}
+
+// Connect create connection and ping db
+func (psql *PQ) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var err error
+	psql.db, err = sql.Open("postgres", dbConnectionString)
+	if err != nil {
+		return err
+	}
+
+	err = psql.db.Ping()
+	if err != nil {
+		return err
+	}
+
+	psql.db.SetMaxOpenConns(maxConnectionsInPool)
+	psql.db.SetMaxIdleConns(maxConnectionsInPool)
+
+	if psql.selectStmt, err = psql.mustPrepare(selectQueryStr); err != nil {
+		return err
+	}
+	if psql.fortuneStmt, err = psql.mustPrepare(fortuneQueryStr); err != nil {
+		return err
+	}
+	if psql.updateStmt, err = psql.mustPrepare(updateQueryStr); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Close connect to db
+func (psql *PQ) Close() {
+	psql.db.Close()
+}
+
+// GetOneRandomWorld return one random World struct
+func (psql PQ) GetOneRandomWorld() (World, error) {
+	var w World
+	var err error
+	queryID := rand.Intn(worldsCount) + 1
+	if err := psql.selectStmt.QueryRow(queryID).Scan(&w.ID, &w.RandomNumber); err != nil {
+		err = fmt.Errorf("error scanning world row with ID %d: %s", queryID, err)
+	}
+	return w, err
+}
+
+// UpdateRandomWorlds updates some number of worlds entries, passed as arg
+func (psql PQ) UpdateRandomWorlds(queries int) ([]World, error) {
+	selectedWorlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		selectedWorlds[i], _ = psql.GetOneRandomWorld()
+	}
+
+	if len(selectedWorlds) > 0 {
+		// against deadlocks
+		sort.Slice(selectedWorlds, func(i, j int) bool {
+			return selectedWorlds[i].ID < selectedWorlds[j].ID
+		})
+
+		tx, err := psql.db.Begin()
+		if err != nil {
+			return selectedWorlds, err
+		}
+
+		for _, selectedWorld := range selectedWorlds {
+			selectedWorld.RandomNumber = rand.Intn(worldsCount) + 1
+			if _, err := tx.Stmt(psql.updateStmt).Exec(selectedWorld.RandomNumber, selectedWorld.ID); err != nil {
+				log.Printf("Can't update row ID %d with number %d: %s", selectedWorld.ID, selectedWorld.RandomNumber, err)
+				tx.Rollback()
+			}
+		}
+
+		if err := tx.Commit(); err != nil {
+			tx.Rollback()
+			return selectedWorlds, err
+		}
+	}
+
+	return selectedWorlds, nil
+}
+
+// GetFortunes selects all fortunes from table
+func (psql PQ) GetFortunes() ([]templates.Fortune, error) {
+	rows, err := psql.fortuneStmt.Query()
+	defer rows.Close()
+	if err != nil {
+		return nil, fmt.Errorf("can't query fortunes: %s", err)
+	}
+
+	fortunes := make([]templates.Fortune, 0, 16)
+	var fortune templates.Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Printf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+
+	return fortunes, nil
+}
+
+func (psql PQ) mustPrepare(query string) (*sql.Stmt, error) {
+	stmt, err := psql.db.Prepare(query)
+	if err != nil {
+		log.Printf("Error when preparing statement %q: %s\n", query, err)
+		return nil, err
+	}
+	return stmt, nil
+}
+
+// NewPqDB creates new connection to postgres db with pq driver
+func NewPqDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	var psql PQ
+	if err := psql.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+	return &psql, nil
+}

+ 1 - 1
frameworks/Go/iris/src/world.go → frameworks/Go/iris/src/storage/world.go

@@ -1,4 +1,4 @@
-package main
+package storage
 
 // World struct
 type World struct {

+ 3 - 1
frameworks/Go/iris/src/fortune.go → frameworks/Go/iris/src/templates/fortune.go

@@ -1,4 +1,6 @@
-package main
+package templates
+
+//go:generate qtc
 
 // Fortune struct
 type Fortune struct {

+ 0 - 7
frameworks/Go/iris/src/templates/fortunes.qtpl

@@ -1,10 +1,3 @@
-{% code
-type Fortune struct {
-	ID int32
-	Message string
-}
-%}
-
 {% func FortunePage(rows []Fortune) %}<!DOCTYPE html>
 <html>
 <head>

+ 81 - 0
frameworks/Go/iris/src/templates/fortunes.qtpl.go

@@ -0,0 +1,81 @@
+// This file is automatically generated by qtc from "fortunes.qtpl".
+// See https://github.com/valyala/quicktemplate for details.
+
+//line fortunes.qtpl:1
+package templates
+
+//line fortunes.qtpl:1
+import (
+	qtio422016 "io"
+
+	qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line fortunes.qtpl:1
+var (
+	_ = qtio422016.Copy
+	_ = qt422016.AcquireByteBuffer
+)
+
+//line fortunes.qtpl:1
+func StreamFortunePage(qw422016 *qt422016.Writer, rows []Fortune) {
+	//line fortunes.qtpl:1
+	qw422016.N().S(`<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+`)
+	//line fortunes.qtpl:9
+	for _, r := range rows {
+		//line fortunes.qtpl:9
+		qw422016.N().S(`
+<tr><td>`)
+		//line fortunes.qtpl:10
+		qw422016.N().D(int(r.ID))
+		//line fortunes.qtpl:10
+		qw422016.N().S(`</td><td>`)
+		//line fortunes.qtpl:10
+		qw422016.E().S(r.Message)
+		//line fortunes.qtpl:10
+		qw422016.N().S(`</td></tr>
+`)
+		//line fortunes.qtpl:11
+	}
+	//line fortunes.qtpl:11
+	qw422016.N().S(`
+</table>
+</body>
+</html>
+`)
+//line fortunes.qtpl:15
+}
+
+//line fortunes.qtpl:15
+func WriteFortunePage(qq422016 qtio422016.Writer, rows []Fortune) {
+	//line fortunes.qtpl:15
+	qw422016 := qt422016.AcquireWriter(qq422016)
+	//line fortunes.qtpl:15
+	StreamFortunePage(qw422016, rows)
+	//line fortunes.qtpl:15
+	qt422016.ReleaseWriter(qw422016)
+//line fortunes.qtpl:15
+}
+
+//line fortunes.qtpl:15
+func FortunePage(rows []Fortune) string {
+	//line fortunes.qtpl:15
+	qb422016 := qt422016.AcquireByteBuffer()
+	//line fortunes.qtpl:15
+	WriteFortunePage(qb422016, rows)
+	//line fortunes.qtpl:15
+	qs422016 := string(qb422016.B)
+	//line fortunes.qtpl:15
+	qt422016.ReleaseByteBuffer(qb422016)
+	//line fortunes.qtpl:15
+	return qs422016
+//line fortunes.qtpl:15
+}