Browse Source

[Go/fasthttp] Upgrade to v1.12.0 and add test cases (#5653)

* Upgrade fasthttp to v1.12.0 and dependencies

* Refactor and add test cases

* Fix

* Fix

* Remove gojay test cases and fixes

* Fix typo

* Fix
Sergio Andrés Virviescas Santana 5 years ago
parent
commit
4502295f8e
34 changed files with 1467 additions and 853 deletions
  1. 5 4
      frameworks/Go/fasthttp/README.md
  2. 212 90
      frameworks/Go/fasthttp/benchmark_config.json
  3. 3 4
      frameworks/Go/fasthttp/fasthttp-easyjson-prefork.dockerfile
  4. 3 4
      frameworks/Go/fasthttp/fasthttp-easyjson.dockerfile
  5. 15 0
      frameworks/Go/fasthttp/fasthttp-mongo-prefork.dockerfile
  6. 15 0
      frameworks/Go/fasthttp/fasthttp-mongo.dockerfile
  7. 15 0
      frameworks/Go/fasthttp/fasthttp-prefork-quicktemplate.dockerfile
  8. 3 4
      frameworks/Go/fasthttp/fasthttp-prefork.dockerfile
  9. 15 0
      frameworks/Go/fasthttp/fasthttp-quicktemplate.dockerfile
  10. 15 0
      frameworks/Go/fasthttp/fasthttp-sjson-prefork.dockerfile
  11. 15 0
      frameworks/Go/fasthttp/fasthttp-sjson.dockerfile
  12. 3 4
      frameworks/Go/fasthttp/fasthttp.dockerfile
  13. 0 90
      frameworks/Go/fasthttp/src/common/common.go
  14. 0 49
      frameworks/Go/fasthttp/src/common/prefork.go
  15. 6 4
      frameworks/Go/fasthttp/src/go.mod
  16. 102 20
      frameworks/Go/fasthttp/src/go.sum
  17. 137 0
      frameworks/Go/fasthttp/src/handlers/handlers.go
  18. 74 0
      frameworks/Go/fasthttp/src/handlers/handlers_easyjson.go
  19. 74 0
      frameworks/Go/fasthttp/src/handlers/handlers_sjson.go
  20. 42 0
      frameworks/Go/fasthttp/src/handlers/message.go
  21. 85 0
      frameworks/Go/fasthttp/src/handlers/message_easyjson.go
  22. 122 0
      frameworks/Go/fasthttp/src/main.go
  23. 0 188
      frameworks/Go/fasthttp/src/server-mysql/server.go
  24. 0 191
      frameworks/Go/fasthttp/src/server-postgresql/server.go
  25. 43 0
      frameworks/Go/fasthttp/src/storage/db.go
  26. 108 0
      frameworks/Go/fasthttp/src/storage/mongo.go
  27. 86 0
      frameworks/Go/fasthttp/src/storage/pgx.go
  28. 85 0
      frameworks/Go/fasthttp/src/storage/world.go
  29. 20 103
      frameworks/Go/fasthttp/src/storage/world_easyjson.go
  30. 0 3
      frameworks/Go/fasthttp/src/templates/auxiliary.go
  31. 82 0
      frameworks/Go/fasthttp/src/templates/fortune.go
  32. 0 87
      frameworks/Go/fasthttp/src/templates/fortune.qtpl.go
  33. 1 8
      frameworks/Go/fasthttp/src/templates/fortunes.qtpl
  34. 81 0
      frameworks/Go/fasthttp/src/templates/fortunes.qtpl.go

+ 5 - 4
frameworks/Go/fasthttp/README.md

@@ -2,13 +2,14 @@
 
 This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
 
-"Fasthttp is a fast http package for Go."
+"Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http"
 
 ## Test URLs
 
+    http://localhost:8080/plaintext
     http://localhost:8080/json
     http://localhost:8080/db
     http://localhost:8080/queries?queries=[1-500]
-    http://localhost:8080/fortunes
-    http://localhost:8080/updates?queries=[1-500]
-    http://localhost:8080/plaintext
+    http://localhost:8080/fortune
+    http://localhost:8080/fortune-quick
+    http://localhost:8080/update?queries=[1-500]

+ 212 - 90
frameworks/Go/fasthttp/benchmark_config.json

@@ -1,93 +1,215 @@
 {
   "framework": "fasthttp",
-  "tests": [{
-    "default": {
-      "json_url": "/json",
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
-      "plaintext_url": "/plaintext",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MySQL",
-      "framework": "None",
-      "language": "Go",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "FastHTTP",
-      "notes": "",
-      "versus": "go"
-    },
-    "prefork": {
-      "json_url": "/json",
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
-      "plaintext_url": "/plaintext",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MySQL",
-      "framework": "None",
-      "language": "Go",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "FastHTTP",
-      "notes": "",
-      "versus": "go"
-    },
-    "postgresql": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "Postgres",
-      "framework": "None",
-      "language": "Go",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "FastHTTP",
-      "notes": "",
-      "versus": "go"
-    },
-    "postgresql-prefork": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortune",
-      "update_url": "/update?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "Postgres",
-      "framework": "None",
-      "language": "Go",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "FastHTTP",
-      "notes": "",
-      "versus": "go"
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortune",
+        "update_url": "/update?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "prefork": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortune",
+        "update_url": "/update?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "easyjson": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "easyjson-prefork": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "sjson": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "sjson-prefork": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "quicktemplate": {
+        "fortune_url": "/fortune-quick",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "prefork-quicktemplate": {
+        "fortune_url": "/fortune-quick",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "Postgres",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "mongo": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortune",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "MongoDB",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      },
+      "mongo-prefork": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortune",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "MongoDB",
+        "framework": "None",
+        "language": "Go",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fasthttp",
+        "notes": "",
+        "versus": "go"
+      }
     }
-  }]
-}
+  ]
+}

+ 3 - 4
frameworks/Go/fasthttp/fasthttp-postgresql.dockerfile → frameworks/Go/fasthttp/fasthttp-easyjson-prefork.dockerfile

@@ -9,8 +9,7 @@ RUN go get -u github.com/mailru/easyjson/...
 RUN go mod download
 
 RUN go generate ./templates
-# RUN easyjson -pkg
-# RUN easyjson -all src/common/common.go
-RUN go build -o app-pg -gcflags='-l=4' -ldflags="-s -w" ./server-postgresql
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app-pg
+CMD ./app -db pgx -json_encoder easyjson -prefork

+ 3 - 4
frameworks/Go/fasthttp/fasthttp-postgresql-prefork.dockerfile → frameworks/Go/fasthttp/fasthttp-easyjson.dockerfile

@@ -9,8 +9,7 @@ RUN go get -u github.com/mailru/easyjson/...
 RUN go mod download
 
 RUN go generate ./templates
-# RUN easyjson -pkg
-# RUN easyjson -all src/common/common.go
-RUN go build -o app-pg -gcflags='-l=4' -ldflags="-s -w" ./server-postgresql
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app-pg -prefork
+CMD ./app -db pgx -json_encoder easyjson

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-mongo-prefork.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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 mongo -db_connection_string "mongodb://tfb-database" -prefork

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-mongo.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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 mongo -db_connection_string "mongodb://tfb-database"

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-prefork-quicktemplate.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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 -prefork -db pgx

+ 3 - 4
frameworks/Go/fasthttp/fasthttp-prefork.dockerfile

@@ -9,8 +9,7 @@ RUN go get -u github.com/mailru/easyjson/...
 RUN go mod download
 
 RUN go generate ./templates
-# RUN easyjson -pkg
-# RUN easyjson -all src/common/common.go
-RUN go build -o app -gcflags='-l=4' -ldflags="-s -w" ./server-mysql
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app -prefork
+CMD ./app -prefork -db pgx

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-quicktemplate.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-sjson-prefork.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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 -json_encoder sjson -prefork

+ 15 - 0
frameworks/Go/fasthttp/fasthttp-sjson.dockerfile

@@ -0,0 +1,15 @@
+FROM golang:1.14
+
+WORKDIR /fasthttp
+
+COPY ./src /fasthttp
+
+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 -json_encoder sjson

+ 3 - 4
frameworks/Go/fasthttp/fasthttp.dockerfile

@@ -9,8 +9,7 @@ RUN go get -u github.com/mailru/easyjson/...
 RUN go mod download
 
 RUN go generate ./templates
-# RUN easyjson -pkg
-# RUN easyjson -all src/common/common.go
-RUN go build -o app -gcflags='-l=4' -ldflags="-s -w" ./server-mysql
+RUN easyjson -pkg
+RUN go build -ldflags="-s -w" -o app .
 
-CMD ./app
+CMD ./app -db pgx

+ 0 - 90
frameworks/Go/fasthttp/src/common/common.go

@@ -1,90 +0,0 @@
-package common
-
-import (
-	"encoding/json"
-	"log"
-	"math/rand"
-	"net"
-	"runtime"
-	"sort"
-
-	"fasthttp/src/templates"
-
-	"github.com/valyala/fasthttp"
-)
-
-const worldRowCount = 10000
-
-type JSONResponse struct {
-	Message string `json:"message"`
-}
-
-type World struct {
-	Id           int32 `json:"id"`
-	RandomNumber int32 `json:"randomNumber"`
-}
-
-type Worlds []World
-
-func JSONHandler(ctx *fasthttp.RequestCtx) {
-	r := JSONResponse{
-		Message: "Hello, World!",
-	}
-	rb, err := r.MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(rb)
-}
-
-func PlaintextHandler(ctx *fasthttp.RequestCtx) {
-	ctx.SetContentType("text/plain")
-	ctx.WriteString("Hello, World!")
-}
-
-func JSONMarshal(ctx *fasthttp.RequestCtx, v interface{}) {
-	ctx.SetContentType("application/json")
-	if err := json.NewEncoder(ctx).Encode(v); err != nil {
-		log.Fatalf("error in json.Encoder.Encode: %s", err)
-	}
-}
-
-func RandomWorldNum() int {
-	return rand.Intn(worldRowCount) + 1
-}
-
-func GetQueriesCount(ctx *fasthttp.RequestCtx) int {
-	n := ctx.QueryArgs().GetUintOrZero("queries")
-	if n < 1 {
-		n = 1
-	} else if n > 500 {
-		n = 500
-	}
-	return n
-}
-
-func GetListener(listenAddr string) net.Listener {
-	ln, err := net.Listen("tcp4", listenAddr)
-	if err != nil {
-		log.Fatal(err)
-	}
-	return ln
-}
-
-func NumCPU() int {
-	n := runtime.NumCPU()
-	if n == 0 {
-		n = 8
-	}
-	return n
-}
-
-func SortFortunesByMessage(fortunes []templates.Fortune) {
-	sort.Slice(fortunes, func(i, j int) bool { return fortunes[i].Message < fortunes[j].Message })
-}
-
-func SortWorldsByID(worlds []World) {
-	sort.Slice(worlds, func(i, j int) bool { return worlds[i].Id < worlds[j].Id })
-}

+ 0 - 49
frameworks/Go/fasthttp/src/common/prefork.go

@@ -1,49 +0,0 @@
-package common
-
-import (
-	"log"
-	"net"
-	"os"
-	"os/exec"
-)
-
-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, 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}
-			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)
-		}
-	}
-	return listener
-}

+ 6 - 4
frameworks/Go/fasthttp/src/go.mod

@@ -1,11 +1,13 @@
 module fasthttp/src
 
-go 1.13
+go 1.14
 
 require (
-	github.com/go-sql-driver/mysql v1.5.0
-	github.com/jackc/pgx/v4 v4.3.0
+	github.com/jackc/pgx/v4 v4.6.0
 	github.com/mailru/easyjson v0.7.1
-	github.com/valyala/fasthttp v1.9.0
+	github.com/savsgio/gotils v0.0.0-20200413113635-8c468ce75cca
+	github.com/tidwall/sjson v1.1.1
+	github.com/valyala/fasthttp v1.12.0
 	github.com/valyala/quicktemplate v1.4.1
+	go.mongodb.org/mongo-driver v1.3.2
 )

+ 102 - 20
frameworks/Go/fasthttp/src/go.sum

@@ -1,22 +1,51 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
-github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
 github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
-github.com/jackc/chunkreader/v2 v2.0.0 h1:DUwgMQuuPnS0rhMXenUtZpqZqrR/30NWY+qQvTpSvEs=
 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
+github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
 github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
 github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
 github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
-github.com/jackc/pgconn v1.3.0 h1:RazbQ8yiQQ/4xfndPzk8zx7Gu1jIxgaLFuHSvafAad8=
-github.com/jackc/pgconn v1.3.0/go.mod h1:2Ze5IP7prCiM28C4nc5LUoRaSyMDYZFE32L4gMJVtcU=
+github.com/jackc/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
 github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
 github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -30,28 +59,35 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
 github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
 github.com/jackc/pgproto3/v2 v2.0.1 h1:Rdjp4NFjwHnEslx2b66FfCI2S0LhO4itac3hXz6WX9M=
 github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
 github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
 github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
 github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
-github.com/jackc/pgtype v1.1.0 h1:aZwrtaSe314VgSGmKvggULa2TavoD1jWVsxj9Zdltek=
-github.com/jackc/pgtype v1.1.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0=
+github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
+github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
+github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
 github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
-github.com/jackc/pgx/v4 v4.3.0 h1:gdMglCbebclFvt/q+7oLH41su8KxtpJxj+vls51OUj0=
-github.com/jackc/pgx/v4 v4.3.0/go.mod h1:BiIGdCptiC/hXZI8EkeixUG0xzTPn9J6S2YSXEBFidE=
+github.com/jackc/pgx/v4 v4.6.0 h1:Fh0O9GdlG4gYpjpwOqjdEodJUQM9jzN3Hdv7PN0xmm0=
+github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
 github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.0.0 h1:rbjAshlgKscNa7j0jAM0uNQflis5o2XUogPMVAwtcsM=
-github.com/jackc/puddle v1.0.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0 h1:musOWczZC/rSbqut475Vfcczg7jJsdUQf0D6oKPLgNU=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.10.4 h1:jFzIFaf586tquEB5EhzQG0HwGNSlgAJpG53G6Ss11wc=
+github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -63,66 +99,111 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
 github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/savsgio/gotils v0.0.0-20200413113635-8c468ce75cca h1:Qe7Mtuhjkk38HVpRtvWdziZJcwG3Qup1mfyvyOrcnyM=
+github.com/savsgio/gotils v0.0.0-20200413113635-8c468ce75cca/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
+github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
+github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
+github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
+github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U=
+github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs=
 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 v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
-github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
-github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
+github.com/valyala/fasthttp v1.12.0 h1:TsB9qkSeiMXB40ELWWSRMjlsE+8IkqXHcs01y2d9aw0=
+github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E=
 github.com/valyala/quicktemplate v1.4.1 h1:tEtkSN6mTCJlYVT7As5x4wjtkk2hj2thsb0M+AcAVeM=
 github.com/valyala/quicktemplate v1.4.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+go.mongodb.org/mongo-driver v1.3.2 h1:IYppNjEV/C+/3VPbhHVxQ4t04eVW0cLp0/pNdW++6Ug=
+go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -130,5 +211,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+y
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 137 - 0
frameworks/Go/fasthttp/src/handlers/handlers.go

@@ -0,0 +1,137 @@
+package handlers
+
+import (
+	"encoding/json"
+	"sort"
+
+	"fasthttp/src/storage"
+	"fasthttp/src/templates"
+
+	"github.com/valyala/fasthttp"
+)
+
+const helloWorldStr = "Hello, World!"
+
+func queriesParam(ctx *fasthttp.RequestCtx) int {
+	n := ctx.QueryArgs().GetUintOrZero("queries")
+	if n < 1 {
+		n = 1
+	} else if n > 500 {
+		n = 500
+	}
+
+	return n
+}
+
+// JSONHandler . Test 1: JSON serialization
+func JSONHandler(ctx *fasthttp.RequestCtx) {
+	message := AcquireMessage()
+	message.Message = helloWorldStr
+	data, _ := json.Marshal(message)
+
+	ctx.SetContentType("application/json")
+	ctx.Write(data)
+
+	ReleaseMessage(message)
+}
+
+// DBHandler . Test 2: Single database query
+func DBHandler(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		world := storage.AcquireWorld()
+		db.GetOneRandomWorld(world)
+		data, _ := json.Marshal(world)
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorld(world)
+	}
+}
+
+// QueriesHandler . Test 3: Multiple database queries
+func QueriesHandler(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			db.GetOneRandomWorld(&worlds[i])
+		}
+
+		data, _ := json.Marshal(worlds)
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}
+
+// FortuneHandler . Test 4: Fortunes
+func FortuneHandler(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		fortunes, _ := db.GetFortunes()
+		newFortune := templates.AcquireFortune()
+		newFortune.Message = "Additional fortune added at request time."
+		fortunes = append(fortunes, *newFortune)
+
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
+
+		ctx.SetContentType("text/html; charset=utf-8")
+
+		templates.FortuneTemplate.Execute(ctx, fortunes)
+
+		templates.ReleaseFortune(newFortune)
+		templates.ReleaseFortunes(fortunes)
+	}
+}
+
+// FortuneQuickHandler . Test 4: Fortunes
+func FortuneQuickHandler(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		fortunes, _ := db.GetFortunes()
+		newFortune := templates.AcquireFortune()
+		newFortune.Message = "Additional fortune added at request time."
+		fortunes = append(fortunes, *newFortune)
+
+		sort.Slice(fortunes, func(i, j int) bool {
+			return fortunes[i].Message < fortunes[j].Message
+		})
+
+		ctx.SetContentType("text/html; charset=utf-8")
+		templates.WriteFortunePage(ctx, fortunes)
+
+		templates.ReleaseFortune(newFortune)
+		templates.ReleaseFortunes(fortunes)
+	}
+}
+
+// UpdateHandler . Test 5: Database updates
+func UpdateHandler(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			w := &worlds[i]
+			db.GetOneRandomWorld(w)
+			w.RandomNumber = int32(storage.RandomWorldNum())
+		}
+
+		db.UpdateWorlds(worlds)
+		data, _ := json.Marshal(worlds)
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}
+
+// PlaintextHandler . Test 6: Plaintext
+func PlaintextHandler(ctx *fasthttp.RequestCtx) {
+	ctx.WriteString(helloWorldStr)
+}

+ 74 - 0
frameworks/Go/fasthttp/src/handlers/handlers_easyjson.go

@@ -0,0 +1,74 @@
+package handlers
+
+import (
+	"fasthttp/src/storage"
+
+	"github.com/valyala/fasthttp"
+)
+
+// JSONHandlerEasyJSON . Test 1: JSON serialization
+func JSONHandlerEasyJSON(ctx *fasthttp.RequestCtx) {
+	message := AcquireMessage()
+	message.Message = helloWorldStr
+	messageBytes, _ := message.MarshalJSON()
+
+	ctx.SetContentType("application/json")
+	ctx.Write(messageBytes)
+
+	ReleaseMessage(message)
+}
+
+// DBHandlerEasyJSON . Test 2: Single database query
+func DBHandlerEasyJSON(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		world := storage.AcquireWorld()
+		db.GetOneRandomWorld(world)
+		worldBytes, _ := world.MarshalJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(worldBytes)
+
+		storage.ReleaseWorld(world)
+	}
+}
+
+// QueriesHandlerEasyJSON . Test 3: Multiple database queries
+func QueriesHandlerEasyJSON(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			db.GetOneRandomWorld(&worlds[i])
+		}
+
+		worldsBytes, _ := worlds.MarshalJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(worldsBytes)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}
+
+// UpdateHandlerEasyJSON . Test 5: Database updates
+func UpdateHandlerEasyJSON(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			w := &worlds[i]
+			db.GetOneRandomWorld(w)
+			w.RandomNumber = int32(storage.RandomWorldNum())
+		}
+
+		db.UpdateWorlds(worlds)
+		worldsBytes, _ := worlds.MarshalJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(worldsBytes)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}

+ 74 - 0
frameworks/Go/fasthttp/src/handlers/handlers_sjson.go

@@ -0,0 +1,74 @@
+package handlers
+
+import (
+	"fasthttp/src/storage"
+
+	"github.com/valyala/fasthttp"
+)
+
+// JSONHandlerSJson . Test 1: JSON serialization
+func JSONHandlerSJson(ctx *fasthttp.RequestCtx) {
+	message := AcquireMessage()
+	message.Message = helloWorldStr
+	data, _ := message.MarshalSJSON()
+
+	ctx.SetContentType("application/json")
+	ctx.Write(data)
+
+	ReleaseMessage(message)
+}
+
+// DBHandlerSJson . Test 2: Single database query
+func DBHandlerSJson(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		world := storage.AcquireWorld()
+		db.GetOneRandomWorld(world)
+		data, _ := world.MarshalSJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorld(world)
+	}
+}
+
+// QueriesHandlerSJson . Test 3: Multiple database queries
+func QueriesHandlerSJson(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			db.GetOneRandomWorld(&worlds[i])
+		}
+
+		data, _ := worlds.MarshalSJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}
+
+// UpdateHandlerSJson . Test 5: Database updates
+func UpdateHandlerSJson(db storage.DB) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		queries := queriesParam(ctx)
+		worlds := storage.AcquireWorlds()[:queries]
+
+		for i := 0; i < queries; i++ {
+			w := &worlds[i]
+			db.GetOneRandomWorld(w)
+			w.RandomNumber = int32(storage.RandomWorldNum())
+		}
+
+		db.UpdateWorlds(worlds)
+		data, _ := worlds.MarshalSJSON()
+
+		ctx.SetContentType("application/json")
+		ctx.Write(data)
+
+		storage.ReleaseWorlds(worlds)
+	}
+}

+ 42 - 0
frameworks/Go/fasthttp/src/handlers/message.go

@@ -0,0 +1,42 @@
+package handlers
+
+import (
+	"sync"
+
+	"github.com/tidwall/sjson"
+)
+
+// Message struct
+type Message struct {
+	Message string `json:"message"`
+}
+
+var messageJSONStr = []byte(`{"message": ""}`)
+
+// MessagePool ...
+var MessagePool = sync.Pool{
+	New: func() interface{} {
+		return new(Message)
+	},
+}
+
+// AcquireMessage returns new message from pool
+func AcquireMessage() *Message {
+	return MessagePool.Get().(*Message)
+}
+
+// ReleaseMessage resets the message and return it to the pool
+func ReleaseMessage(m *Message) {
+	m.Message = ""
+	MessagePool.Put(m)
+}
+
+// IsNil returns true if the object is nil
+func (m *Message) IsNil() bool {
+	return m == nil
+}
+
+// MarshalSJSON marshals the object as json
+func (m Message) MarshalSJSON() ([]byte, error) {
+	return sjson.SetBytesOptions(messageJSONStr, "message", m.Message, &sjson.Options{Optimistic: true})
+}

+ 85 - 0
frameworks/Go/fasthttp/src/handlers/message_easyjson.go

@@ -0,0 +1,85 @@
+// 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 easyjson4086215fDecodeFasthttpSrcHandlers(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 easyjson4086215fEncodeFasthttpSrcHandlers(out *jwriter.Writer, in Message) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"message\":"
+		out.RawString(prefix[1:])
+		out.String(string(in.Message))
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v Message) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjson4086215fEncodeFasthttpSrcHandlers(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v Message) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjson4086215fEncodeFasthttpSrcHandlers(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *Message) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjson4086215fDecodeFasthttpSrcHandlers(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *Message) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjson4086215fDecodeFasthttpSrcHandlers(l, v)
+}

+ 122 - 0
frameworks/Go/fasthttp/src/main.go

@@ -0,0 +1,122 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"runtime"
+
+	"fasthttp/src/handlers"
+	"fasthttp/src/storage"
+
+	"github.com/savsgio/gotils"
+	"github.com/valyala/fasthttp"
+	fastprefork "github.com/valyala/fasthttp/prefork"
+)
+
+var bindHost, jsonEncoder, dbDriver, dbConnectionString string
+var prefork bool
+
+func init() {
+	// init flags
+	flag.StringVar(&bindHost, "bind", ":8080", "set bind host")
+	flag.BoolVar(&prefork, "prefork", false, "use prefork")
+	flag.StringVar(&jsonEncoder, "json_encoder", "none", "json encoder: none or easyjson or sjson")
+	flag.StringVar(&dbDriver, "db", "none", "db connection driver [values: none or pgx or mongo]")
+	flag.StringVar(&dbConnectionString, "db_connection_string", "", "db connection string")
+
+	flag.Parse()
+}
+
+func numCPU() int {
+	n := runtime.NumCPU()
+	if n == 0 {
+		n = 8
+	}
+
+	return n
+}
+
+func main() {
+	maxConn := numCPU() * 4
+	if fastprefork.IsChild() {
+		maxConn = numCPU()
+	}
+
+	if dbConnectionString == "" {
+		dbConnectionString = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s pool_max_conns=%d", "tfb-database", 5432, "benchmarkdbuser", "benchmarkdbpass", "hello_world", maxConn)
+	}
+
+	// init database with appropriate driver
+	db, err := storage.InitDB(dbDriver, dbConnectionString, maxConn)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if db != nil {
+		defer db.Close()
+	}
+
+	// init json encoders
+	var jsonHandler, dbHandler, queriesHandler, updateHandler fasthttp.RequestHandler
+
+	switch jsonEncoder {
+	case "easyjson":
+		jsonHandler = handlers.JSONHandlerEasyJSON
+		dbHandler = handlers.DBHandlerEasyJSON(db)
+		queriesHandler = handlers.QueriesHandlerEasyJSON(db)
+		updateHandler = handlers.UpdateHandlerEasyJSON(db)
+	case "sjson":
+		jsonHandler = handlers.JSONHandlerSJson
+		dbHandler = handlers.DBHandlerSJson(db)
+		queriesHandler = handlers.QueriesHandlerSJson(db)
+		updateHandler = handlers.UpdateHandlerSJson(db)
+	default:
+		jsonHandler = handlers.JSONHandler
+		dbHandler = handlers.DBHandler(db)
+		queriesHandler = handlers.QueriesHandler(db)
+		updateHandler = handlers.UpdateHandler(db)
+	}
+
+	fortunesHandler := handlers.FortuneHandler(db)
+	fortunesQuickHandler := handlers.FortuneQuickHandler(db)
+
+	handler := func(ctx *fasthttp.RequestCtx) {
+		switch gotils.B2S(ctx.Path()) {
+		case "/plaintext":
+			handlers.PlaintextHandler(ctx)
+		case "/json":
+			jsonHandler(ctx)
+		case "/db":
+			dbHandler(ctx)
+		case "/queries":
+			queriesHandler(ctx)
+		case "/fortune":
+			fortunesHandler(ctx)
+		case "/fortune-quick":
+			fortunesQuickHandler(ctx)
+		case "/update":
+			updateHandler(ctx)
+		default:
+			ctx.Error(fasthttp.StatusMessage(fasthttp.StatusNotFound), fasthttp.StatusNotFound)
+		}
+	}
+
+	server := &fasthttp.Server{
+		Handler: handler,
+		Name:    "go",
+	}
+
+	if prefork {
+		preforkServer := fastprefork.New(server)
+
+		if err := preforkServer.ListenAndServe(bindHost); err != nil {
+			panic(err)
+		}
+
+	} else {
+		if err := server.ListenAndServe(bindHost); err != nil {
+			panic(err)
+		}
+	}
+}

+ 0 - 188
frameworks/Go/fasthttp/src/server-mysql/server.go

@@ -1,188 +0,0 @@
-package main
-
-import (
-	"database/sql"
-	"flag"
-	"log"
-	"net"
-	"runtime"
-	"unsafe"
-
-	_ "github.com/go-sql-driver/mysql"
-	"github.com/valyala/fasthttp"
-
-	"fasthttp/src/common"
-	"fasthttp/src/templates"
-)
-
-const connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(tfb-database:3306)/hello_world"
-
-var (
-	worldSelectStmt   *sql.Stmt
-	worldUpdateStmt   *sql.Stmt
-	fortuneSelectStmt *sql.Stmt
-
-	db *sql.DB
-)
-
-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")
-	flag.Parse()
-
-	var err error
-	if db, err = sql.Open("mysql", connectionString); err != nil {
-		log.Fatalf("Error opening database: %s", err)
-	}
-	if err = db.Ping(); err != nil {
-		log.Fatalf("Cannot connect to db: %s", err)
-	}
-
-	maxConnectionCount := runtime.NumCPU() * 2
-	db.SetMaxIdleConns(maxConnectionCount)
-	db.SetMaxOpenConns(maxConnectionCount)
-
-	worldSelectStmt = mustPrepare(db, "SELECT id, randomNumber FROM World WHERE id = ?")
-	worldUpdateStmt = mustPrepare(db, "UPDATE World SET randomNumber = ? WHERE id = ?")
-	fortuneSelectStmt = mustPrepare(db, "SELECT id, message FROM Fortune")
-
-	s := &fasthttp.Server{
-		Handler: mainHandler,
-		Name:    "go",
-	}
-
-	var ln net.Listener
-	if *prefork {
-		ln = common.DoPrefork(*child, *bindHost)
-	} else {
-		ln = common.GetListener(*bindHost)
-	}
-
-	if err = s.Serve(ln); err != nil {
-		log.Fatalf("Error when serving incoming connections: %s", err)
-	}
-}
-
-func mainHandler(ctx *fasthttp.RequestCtx) {
-	path := ctx.Path()
-	switch *(*string)(unsafe.Pointer(&path)) {
-	case "/plaintext":
-		common.PlaintextHandler(ctx)
-	case "/json":
-		common.JSONHandler(ctx)
-	case "/db":
-		dbHandler(ctx)
-	case "/queries":
-		queriesHandler(ctx)
-	case "/fortune":
-		fortuneHandler(ctx)
-	case "/update":
-		updateHandler(ctx)
-	default:
-		ctx.Error("unexpected path", fasthttp.StatusBadRequest)
-	}
-}
-
-func dbHandler(ctx *fasthttp.RequestCtx) {
-	var w common.World
-	fetchRandomWorld(&w)
-	wb, err := w.MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func queriesHandler(ctx *fasthttp.RequestCtx) {
-	n := common.GetQueriesCount(ctx)
-	worlds := make([]common.World, n)
-	for i := 0; i < n; i++ {
-		fetchRandomWorld(&worlds[i])
-	}
-	wb, err := common.Worlds(worlds).MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func fortuneHandler(ctx *fasthttp.RequestCtx) {
-	rows, err := fortuneSelectStmt.Query()
-	if err != nil {
-		log.Fatalf("Error selecting db data: %v", err)
-	}
-
-	var f templates.Fortune
-	fortunes := make([]templates.Fortune, 0, 16)
-	for rows.Next() {
-		if err := rows.Scan(&f.ID, &f.Message); err != nil {
-			log.Fatalf("Error scanning fortune row: %s", err)
-		}
-		fortunes = append(fortunes, f)
-	}
-	rows.Close()
-	fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
-
-	common.SortFortunesByMessage(fortunes)
-
-	ctx.SetContentType("text/html; charset=utf-8")
-	templates.WriteFortunePage(ctx, fortunes)
-}
-
-func updateHandler(ctx *fasthttp.RequestCtx) {
-	n := common.GetQueriesCount(ctx)
-
-	worlds := make([]common.World, n)
-	for i := 0; i < n; i++ {
-		w := &worlds[i]
-		fetchRandomWorld(w)
-		w.RandomNumber = int32(common.RandomWorldNum())
-	}
-
-	// sorting is required for insert deadlock prevention.
-	common.SortWorldsByID(worlds)
-
-	txn, err := db.Begin()
-	if err != nil {
-		log.Fatalf("Error starting transaction: %s", err)
-	}
-	stmt := txn.Stmt(worldUpdateStmt)
-	for i := 0; i < n; i++ {
-		w := &worlds[i]
-		if _, err := stmt.Exec(w.RandomNumber, w.Id); err != nil {
-			log.Fatalf("Error updating world row %d: %s", i, err)
-		}
-	}
-	if err = txn.Commit(); err != nil {
-		log.Fatalf("Error when commiting world rows: %s", err)
-	}
-
-	wb, err := common.Worlds(worlds).MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func fetchRandomWorld(w *common.World) {
-	n := common.RandomWorldNum()
-	if err := worldSelectStmt.QueryRow(n).Scan(&w.Id, &w.RandomNumber); err != nil {
-		log.Fatalf("Error scanning world row: %s", err)
-	}
-}
-
-func mustPrepare(db *sql.DB, query string) *sql.Stmt {
-	stmt, err := db.Prepare(query)
-	if err != nil {
-		log.Fatalf("Error when preparing statement %q: %s", query, err)
-	}
-	return stmt
-	// ?
-}

+ 0 - 191
frameworks/Go/fasthttp/src/server-postgresql/server.go

@@ -1,191 +0,0 @@
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"log"
-	"net"
-	"unsafe"
-
-	pgx "github.com/jackc/pgx/v4"
-	"github.com/jackc/pgx/v4/pgxpool"
-	"github.com/valyala/fasthttp"
-
-	"fasthttp/src/common"
-	"fasthttp/src/templates"
-)
-
-var (
-	db *pgxpool.Pool
-)
-
-const worldSelectSQL = "SELECT id, randomNumber FROM World WHERE id = $1"
-const worldUpdateSQL = "UPDATE World SET randomNumber = $1 WHERE id = $2"
-const fortuneSelectSQL = "SELECT id, message FROM Fortune"
-
-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")
-	flag.Parse()
-
-	var err error
-	maxConnectionCount := common.NumCPU() * 4
-	if *child {
-		maxConnectionCount = common.NumCPU()
-	}
-	if db, err = initDatabase("tfb-database", "benchmarkdbuser", "benchmarkdbpass", "hello_world", 5432, maxConnectionCount); err != nil {
-		log.Fatalf("Error opening database: %s", err)
-	}
-
-	s := &fasthttp.Server{
-		Handler: mainHandler,
-		Name:    "go",
-	}
-
-	var ln net.Listener
-	if *prefork {
-		ln = common.DoPrefork(*child, *bindHost)
-	} else {
-		ln = common.GetListener(*bindHost)
-	}
-
-	if err = s.Serve(ln); err != nil {
-		log.Fatalf("Error when serving incoming connections: %s", err)
-	}
-}
-
-func mainHandler(ctx *fasthttp.RequestCtx) {
-	path := ctx.Path()
-	switch *(*string)(unsafe.Pointer(&path)) {
-	case "/plaintext":
-		common.PlaintextHandler(ctx)
-	case "/json":
-		common.JSONHandler(ctx)
-	case "/db":
-		dbHandler(ctx)
-	case "/queries":
-		queriesHandler(ctx)
-	case "/fortune":
-		fortuneHandler(ctx)
-	case "/update":
-		updateHandler(ctx)
-	default:
-		ctx.Error("unexpected path", fasthttp.StatusBadRequest)
-	}
-}
-
-func dbHandler(ctx *fasthttp.RequestCtx) {
-	w := fetchRandomWorld()
-	wb, err := w.MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func queriesHandler(ctx *fasthttp.RequestCtx) {
-	n := common.GetQueriesCount(ctx)
-	worlds := make([]common.World, n)
-	for i := 0; i < n; i++ {
-		worlds[i] = fetchRandomWorld()
-	}
-	wb, err := common.Worlds(worlds).MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func fortuneHandler(ctx *fasthttp.RequestCtx) {
-	rows, err := db.Query(context.Background(), fortuneSelectSQL)
-	if err != nil {
-		log.Fatalf("Error selecting db data: %v", err)
-	}
-
-	var f templates.Fortune
-	fortunes := make([]templates.Fortune, 0, 16)
-	for rows.Next() {
-		if err := rows.Scan(&f.ID, &f.Message); err != nil {
-			log.Fatalf("Error scanning fortune row: %s", err)
-		}
-		fortunes = append(fortunes, f)
-	}
-	rows.Close()
-	fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
-
-	common.SortFortunesByMessage(fortunes)
-
-	ctx.SetContentType("text/html; charset=utf-8")
-	templates.WriteFortunePage(ctx, fortunes)
-}
-
-func updateHandler(ctx *fasthttp.RequestCtx) {
-	n := common.GetQueriesCount(ctx)
-
-	worlds := make([]common.World, n)
-	for i := 0; i < n; i++ {
-		worlds[i] = fetchRandomWorld()
-		worlds[i].RandomNumber = int32(common.RandomWorldNum())
-	}
-
-	// sorting is required for insert deadlock prevention.
-	common.SortWorldsByID(worlds)
-
-	batch := pgx.Batch{}
-	for _, w := range worlds {
-		batch.Queue(worldUpdateSQL, w.RandomNumber, w.Id)
-	}
-	if err := db.SendBatch(context.Background(), &batch).Close(); err != nil {
-		log.Fatalf("Error when closing a batch: %s", err)
-	}
-
-	wb, err := common.Worlds(worlds).MarshalJSON()
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	ctx.SetContentType("application/json")
-	ctx.Write(wb)
-}
-
-func fetchRandomWorld() (w common.World) {
-	n := common.RandomWorldNum()
-	if err := db.QueryRow(context.Background(), worldSelectSQL, n).Scan(&w.Id, &w.RandomNumber); err != nil {
-		log.Fatalf("Error scanning world row: %s", err)
-	}
-	return w
-}
-
-func initDatabase(dbHost string, dbUser string, dbPass string, dbName string, dbPort uint16, maxConnectionsInPool int) (*pgxpool.Pool, error) {
-	var successOrFailure string = "OK"
-
-	dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s pool_max_conns=%d", dbHost, dbPort, dbUser, dbPass, dbName, maxConnectionsInPool)
-
-	fmt.Println("--------------------------------------------------------------------------------------------")
-
-	connPool, err := pgxpool.Connect(context.Background(), dsn)
-	if err != nil {
-		successOrFailure = "FAILED"
-		log.Println("Connecting to database ", dbName, " as user ", dbUser, " ", successOrFailure, ": \n ", err)
-	} else {
-		log.Println("Connecting to database ", dbName, " as user ", dbUser, ": ", successOrFailure)
-
-		log.Println("Fetching one record to test if db connection is valid...")
-		var w common.World
-		n := common.RandomWorldNum()
-		if errPing := connPool.QueryRow(context.Background(), worldSelectSQL, n).Scan(&w.Id, &w.RandomNumber); errPing != nil {
-			log.Fatalf("Error scanning world row: %s", errPing)
-		}
-		log.Println("OK")
-	}
-
-	fmt.Println("--------------------------------------------------------------------------------------------")
-
-	return connPool, err
-}

+ 43 - 0
frameworks/Go/fasthttp/src/storage/db.go

@@ -0,0 +1,43 @@
+package storage
+
+import (
+	"errors"
+	"math/rand"
+
+	"fasthttp/src/templates"
+)
+
+const (
+	worldsCount = 10000
+
+	worldSelectSQL   = "SELECT id, randomNumber FROM World WHERE id = $1"
+	worldUpdateSQL   = "UPDATE World SET randomNumber = $1 WHERE id = $2"
+	fortuneSelectSQL = "SELECT id, message FROM Fortune"
+)
+
+// DB is interface for
+type DB interface {
+	// GetOneRandomWorld() (World, error)
+	GetOneRandomWorld(*World) error
+	UpdateWorlds(Worlds) error
+	GetFortunes() (templates.Fortunes, error)
+	Close()
+}
+
+func RandomWorldNum() int {
+	return rand.Intn(worldsCount) + 1
+}
+
+// InitDB with appropriate driver
+func InitDB(dbDriver, dbConnectionString string, maxConnectionCount int) (DB, error) {
+	switch dbDriver {
+	case "pgx":
+		return NewPgxDB(dbConnectionString)
+	case "mongo":
+		return NewMongoDB(dbConnectionString, maxConnectionCount)
+	case "none":
+		return nil, nil
+	}
+
+	return nil, errors.New("can not recognize DB driver type")
+}

+ 108 - 0
frameworks/Go/fasthttp/src/storage/mongo.go

@@ -0,0 +1,108 @@
+package storage
+
+import (
+	"context"
+
+	"fasthttp/src/templates"
+
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"go.mongodb.org/mongo-driver/mongo/readpref"
+)
+
+// Mongo struct
+type Mongo struct {
+	db       *mongo.Client
+	database *mongo.Database
+	// mongodb collections
+	worlds   *mongo.Collection
+	fortunes *mongo.Collection
+}
+
+// NewMongoDB creates new connection to postgres db with official mongo driver
+func NewMongoDB(dbConnectionString string, maxConnectionsInPool int) (DB, error) {
+	m := new(Mongo)
+
+	if err := m.Connect(dbConnectionString, maxConnectionsInPool); err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
+// Connect create connection and ping db
+func (m *Mongo) Connect(dbConnectionString string, maxConnectionsInPool int) error {
+	var err error
+
+	opts := options.Client()
+	opts.SetMaxPoolSize(uint64(maxConnectionsInPool))
+
+	m.db, err = mongo.Connect(context.Background(), opts.ApplyURI(dbConnectionString))
+	if err != nil {
+		return err
+	}
+
+	err = m.db.Ping(context.Background(), readpref.Primary())
+	if err != nil {
+		return err
+	}
+
+	m.database = m.db.Database("hello_world")
+	m.worlds = m.database.Collection("world")
+	m.fortunes = m.database.Collection("fortune")
+
+	return nil
+}
+
+// Close connect to db
+func (m *Mongo) Close() {
+	if err := m.db.Disconnect(context.Background()); err != nil {
+		panic(err)
+	}
+}
+
+// GetOneRandomWorld return one random World struct
+func (m *Mongo) GetOneRandomWorld(w *World) error {
+	id := RandomWorldNum()
+	filter := bson.M{"_id": id}
+
+	return m.worlds.FindOne(context.Background(), filter).Decode(w)
+}
+
+// UpdateWorlds updates some number of worlds entries, passed as arg
+func (m *Mongo) UpdateWorlds(worlds Worlds) error {
+	var operations []mongo.WriteModel
+
+	for _, w := range worlds {
+		operation := mongo.NewUpdateOneModel()
+		operation.SetFilter(bson.M{"_id": w.ID})
+		operation.SetUpdate(bson.M{"$set": bson.M{"randomNumber": w.RandomNumber}})
+		operations = append(operations, operation)
+	}
+
+	_, err := m.worlds.BulkWrite(context.Background(), operations)
+
+	return err
+}
+
+// GetFortunes selects all fortunes from table
+func (m *Mongo) GetFortunes() (templates.Fortunes, error) {
+	cur, err := m.fortunes.Find(context.Background(), bson.M{})
+	if err != nil {
+		return nil, err
+	}
+	defer cur.Close(context.Background())
+
+	fortunes := templates.AcquireFortunes()
+	fortune := templates.AcquireFortune()
+
+	for cur.Next(context.Background()) {
+		cur.Decode(fortune)
+		fortunes = append(fortunes, *fortune)
+	}
+
+	templates.ReleaseFortune(fortune)
+
+	return fortunes, cur.Err()
+}

+ 86 - 0
frameworks/Go/fasthttp/src/storage/pgx.go

@@ -0,0 +1,86 @@
+package storage
+
+import (
+	"context"
+	"sort"
+
+	"fasthttp/src/templates"
+
+	pgx "github.com/jackc/pgx/v4"
+	"github.com/jackc/pgx/v4/pgxpool"
+)
+
+// PGX struct
+type PGX struct {
+	db *pgxpool.Pool
+}
+
+// NewPgxDB creates new connection to postgres db with pgx driver
+func NewPgxDB(dbConnectionString string) (DB, error) {
+	psql := new(PGX)
+	if err := psql.Connect(dbConnectionString); err != nil {
+		return nil, err
+	}
+
+	return psql, nil
+}
+
+// Connect create connection and ping db
+func (psql *PGX) Connect(dbConnectionString string) error {
+	var err error
+
+	if psql.db, err = pgxpool.Connect(context.Background(), dbConnectionString); 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(w *World) error {
+	id := RandomWorldNum()
+
+	return psql.db.QueryRow(context.Background(), worldSelectSQL, id).Scan(&w.ID, &w.RandomNumber)
+}
+
+// UpdateWorlds updates some number of worlds entries, passed as arg
+func (psql *PGX) UpdateWorlds(worlds Worlds) error {
+	// against deadlocks
+	sort.Slice(worlds, func(i, j int) bool {
+		return worlds[i].ID < worlds[j].ID
+	})
+
+	batch := pgx.Batch{}
+
+	for _, w := range worlds {
+		batch.Queue(worldUpdateSQL, w.RandomNumber, w.ID)
+	}
+
+	return psql.db.SendBatch(context.Background(), &batch).Close()
+}
+
+// GetFortunes selects all fortunes from table
+func (psql *PGX) GetFortunes() (templates.Fortunes, error) {
+	rows, err := psql.db.Query(context.Background(), fortuneSelectSQL)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+
+	fortunes := templates.AcquireFortunes()
+	fortune := templates.AcquireFortune()
+
+	for rows.Next() {
+		rows.Scan(&fortune.ID, &fortune.Message)
+		fortunes = append(fortunes, *fortune)
+	}
+
+	templates.ReleaseFortune(fortune)
+
+	return fortunes, nil
+}

+ 85 - 0
frameworks/Go/fasthttp/src/storage/world.go

@@ -0,0 +1,85 @@
+package storage
+
+import (
+	"sync"
+
+	"github.com/tidwall/sjson"
+)
+
+var worldJSONStr = []byte(`{"id": 0, "randomNumber": 0}`)
+
+//easyjson:json
+type World struct {
+	ID           int32 `json:"id"`
+	RandomNumber int32 `json:"randomnumber"`
+}
+
+// WorldPool ...
+var WorldPool = sync.Pool{
+	New: func() interface{} {
+		return new(World)
+	},
+}
+
+// AcquireWorld returns new world from pool
+func AcquireWorld() *World {
+	return WorldPool.Get().(*World)
+}
+
+// ReleaseWorld resets the world and return it to the pool
+func ReleaseWorld(w *World) {
+	w.ID = 0
+	w.RandomNumber = 0
+	WorldPool.Put(w)
+}
+
+// IsNil returns true if the object is nil
+func (w *World) IsNil() bool {
+	return w == nil
+}
+
+// MarshalSJSON marshals the object as json
+func (w World) MarshalSJSON() ([]byte, error) {
+	data, _ := sjson.SetBytesOptions(worldJSONStr, "id", w.ID, &sjson.Options{Optimistic: true})
+
+	return sjson.SetBytesOptions(
+		data, "randomNumber", w.RandomNumber, &sjson.Options{Optimistic: true, ReplaceInPlace: true},
+	)
+}
+
+//easyjson:json
+type Worlds []World
+
+// WorldsPool ...
+var WorldsPool = sync.Pool{
+	New: func() interface{} {
+		return make(Worlds, 0, 512)
+	},
+}
+
+// AcquireWorlds returns new worlds from pool
+func AcquireWorlds() Worlds {
+	return WorldsPool.Get().(Worlds)
+}
+
+// ReleaseWorlds resets the worlds and return it to the pool
+func ReleaseWorlds(w Worlds) {
+	w = w[:0]
+	WorldsPool.Put(w)
+}
+
+// IsNil returns true if the object is nil
+func (ws Worlds) IsNil() bool {
+	return ws == nil
+}
+
+// MarshalSJSON marshals the object as json
+func (ws Worlds) MarshalSJSON() ([]byte, error) {
+	jsonResult := []byte(`[]`)
+
+	for _, w := range ws {
+		jsonResult, _ = sjson.SetBytesOptions(jsonResult, "-1", w, &sjson.Options{Optimistic: true, ReplaceInPlace: true})
+	}
+
+	return jsonResult, nil
+}

+ 20 - 103
frameworks/Go/fasthttp/src/common/common_easyjson.go → frameworks/Go/fasthttp/src/storage/world_easyjson.go

@@ -1,10 +1,9 @@
 // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
 
-package common
+package storage
 
 import (
 	json "encoding/json"
-
 	easyjson "github.com/mailru/easyjson"
 	jlexer "github.com/mailru/easyjson/jlexer"
 	jwriter "github.com/mailru/easyjson/jwriter"
@@ -18,7 +17,7 @@ var (
 	_ easyjson.Marshaler
 )
 
-func easyjson4da0dabeDecodeGoStdSrcStorage(in *jlexer.Lexer, out *Worlds) {
+func easyjson4da0dabeDecodeFasthttpSrcStorage(in *jlexer.Lexer, out *Worlds) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		in.Skip()
@@ -27,7 +26,7 @@ func easyjson4da0dabeDecodeGoStdSrcStorage(in *jlexer.Lexer, out *Worlds) {
 		in.Delim('[')
 		if *out == nil {
 			if !in.IsDelim(']') {
-				*out = make(Worlds, 0, 4)
+				*out = make(Worlds, 0, 8)
 			} else {
 				*out = Worlds{}
 			}
@@ -46,7 +45,7 @@ func easyjson4da0dabeDecodeGoStdSrcStorage(in *jlexer.Lexer, out *Worlds) {
 		in.Consumed()
 	}
 }
-func easyjson4da0dabeEncodeGoStdSrcStorage(out *jwriter.Writer, in Worlds) {
+func easyjson4da0dabeEncodeFasthttpSrcStorage(out *jwriter.Writer, in Worlds) {
 	if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
 		out.RawString("null")
 	} else {
@@ -64,28 +63,27 @@ func easyjson4da0dabeEncodeGoStdSrcStorage(out *jwriter.Writer, in Worlds) {
 // MarshalJSON supports json.Marshaler interface
 func (v Worlds) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjson4da0dabeEncodeGoStdSrcStorage(&w, v)
+	easyjson4da0dabeEncodeFasthttpSrcStorage(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v Worlds) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson4da0dabeEncodeGoStdSrcStorage(w, v)
+	easyjson4da0dabeEncodeFasthttpSrcStorage(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *Worlds) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjson4da0dabeDecodeGoStdSrcStorage(&r, v)
+	easyjson4da0dabeDecodeFasthttpSrcStorage(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *Worlds) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson4da0dabeDecodeGoStdSrcStorage(l, v)
+	easyjson4da0dabeDecodeFasthttpSrcStorage(l, v)
 }
-
-func easyjsonC803d3e7DecodeCommon(in *jlexer.Lexer, out *World) {
+func easyjson4da0dabeDecodeFasthttpSrcStorage1(in *jlexer.Lexer, out *World) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -105,8 +103,8 @@ func easyjsonC803d3e7DecodeCommon(in *jlexer.Lexer, out *World) {
 		}
 		switch key {
 		case "id":
-			out.Id = int32(in.Int32())
-		case "randomNumber":
+			out.ID = int32(in.Int32())
+		case "randomnumber":
 			out.RandomNumber = int32(in.Int32())
 		default:
 			in.SkipRecursive()
@@ -118,28 +116,18 @@ func easyjsonC803d3e7DecodeCommon(in *jlexer.Lexer, out *World) {
 		in.Consumed()
 	}
 }
-func easyjsonC803d3e7EncodeCommon(out *jwriter.Writer, in World) {
+func easyjson4da0dabeEncodeFasthttpSrcStorage1(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.Int32(int32(in.Id))
+		out.RawString(prefix[1:])
+		out.Int32(int32(in.ID))
 	}
 	{
-		const prefix string = ",\"randomNumber\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
+		const prefix string = ",\"randomnumber\":"
+		out.RawString(prefix)
 		out.Int32(int32(in.RandomNumber))
 	}
 	out.RawByte('}')
@@ -148,94 +136,23 @@ func easyjsonC803d3e7EncodeCommon(out *jwriter.Writer, in World) {
 // MarshalJSON supports json.Marshaler interface
 func (v World) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonC803d3e7EncodeCommon(&w, v)
+	easyjson4da0dabeEncodeFasthttpSrcStorage1(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v World) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonC803d3e7EncodeCommon(w, v)
+	easyjson4da0dabeEncodeFasthttpSrcStorage1(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *World) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonC803d3e7DecodeCommon(&r, v)
+	easyjson4da0dabeDecodeFasthttpSrcStorage1(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *World) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonC803d3e7DecodeCommon(l, v)
-}
-func easyjsonC803d3e7DecodeCommon1(in *jlexer.Lexer, out *JSONResponse) {
-	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 easyjsonC803d3e7EncodeCommon1(out *jwriter.Writer, in JSONResponse) {
-	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 JSONResponse) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjsonC803d3e7EncodeCommon1(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v JSONResponse) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonC803d3e7EncodeCommon1(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *JSONResponse) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjsonC803d3e7DecodeCommon1(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *JSONResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonC803d3e7DecodeCommon1(l, v)
+	easyjson4da0dabeDecodeFasthttpSrcStorage1(l, v)
 }

+ 0 - 3
frameworks/Go/fasthttp/src/templates/auxiliary.go

@@ -1,3 +0,0 @@
-package templates
-
-//go:generate qtc

+ 82 - 0
frameworks/Go/fasthttp/src/templates/fortune.go

@@ -0,0 +1,82 @@
+package templates
+
+import (
+	"html/template"
+	"sync"
+)
+
+//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"`
+}
+
+// FortunePool ...
+var FortunePool = &sync.Pool{
+	New: func() interface{} {
+		return new(Fortune)
+	},
+}
+
+// AcquireFortune returns new message from pool
+func AcquireFortune() *Fortune {
+	return FortunePool.Get().(*Fortune)
+}
+
+// ReleaseFortune resets the message and return it to the pool
+func ReleaseFortune(f *Fortune) {
+	f.ID = 0
+	f.Message = ""
+	FortunePool.Put(f)
+}
+
+// Fortunes ...
+type Fortunes []Fortune
+
+// FortunesPool ...
+var FortunesPool = sync.Pool{
+	New: func() interface{} {
+		return make(Fortunes, 0, 16)
+	},
+}
+
+// AcquireFortunes returns new fortunes from pool
+func AcquireFortunes() Fortunes {
+	return FortunesPool.Get().(Fortunes)
+}
+
+// ReleaseFortunes resets the fortunes and return it to the pool
+func ReleaseFortunes(f Fortunes) {
+	f = f[:0]
+	FortunesPool.Put(f)
+}

+ 0 - 87
frameworks/Go/fasthttp/src/templates/fortune.qtpl.go

@@ -1,87 +0,0 @@
-// Code generated by qtc from "fortune.qtpl". DO NOT EDIT.
-// See https://github.com/valyala/quicktemplate for details.
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:1
-package templates
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:1
-import (
-	qtio422016 "io"
-
-	qt422016 "github.com/valyala/quicktemplate"
-)
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:1
-var (
-	_ = qtio422016.Copy
-	_ = qt422016.AcquireByteBuffer
-)
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:2
-type Fortune struct {
-	ID      int32
-	Message string
-}
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:8
-func StreamFortunePage(qw422016 *qt422016.Writer, rows []Fortune) {
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:8
-	qw422016.N().S(`<!DOCTYPE html>
-<html>
-<head>
-<title>Fortunes</title>
-</head>
-<body>
-<table>
-<tr><th>id</th><th>message</th></tr>
-`)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:16
-	for _, r := range rows {
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:16
-		qw422016.N().S(`
-<tr><td>`)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:17
-		qw422016.N().D(int(r.ID))
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:17
-		qw422016.N().S(`</td><td>`)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:17
-		qw422016.E().S(r.Message)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:17
-		qw422016.N().S(`</td></tr>
-`)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:18
-	}
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:18
-	qw422016.N().S(`
-</table>
-</body>
-</html>
-`)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-}
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-func WriteFortunePage(qq422016 qtio422016.Writer, rows []Fortune) {
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	qw422016 := qt422016.AcquireWriter(qq422016)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	StreamFortunePage(qw422016, rows)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	qt422016.ReleaseWriter(qw422016)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-}
-
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-func FortunePage(rows []Fortune) string {
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	qb422016 := qt422016.AcquireByteBuffer()
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	WriteFortunePage(qb422016, rows)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	qs422016 := string(qb422016.B)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	qt422016.ReleaseByteBuffer(qb422016)
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-	return qs422016
-//line frameworks/Go/fasthttp/src/templates/fortune.qtpl:22
-}

+ 1 - 8
frameworks/Go/fasthttp/src/templates/fortune.qtpl → frameworks/Go/fasthttp/src/templates/fortunes.qtpl

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

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

@@ -0,0 +1,81 @@
+// Code generated by qtc from "fortunes.qtpl". DO NOT EDIT.
+// 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 Fortunes) {
+//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 Fortunes) {
+//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 Fortunes) 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
+}