Browse Source

renamed generic fasthttp to fasthttp-mysql, added a postgresql-based version at fasthttp-postgresql

silviucm 9 years ago
parent
commit
8d777366e9

+ 0 - 0
frameworks/Go/fasthttp/README.md → frameworks/Go/fasthttp-mysql/README.md


+ 1 - 1
frameworks/Go/fasthttp/benchmark_config.json → frameworks/Go/fasthttp-mysql/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "fasthttp",
+  "framework": "fasthttp-mysql",
   "tests": [{
     "default": {
       "setup_file": "setup",

+ 0 - 0
frameworks/Go/fasthttp/setup.bat → frameworks/Go/fasthttp-mysql/setup.bat


+ 0 - 0
frameworks/Go/fasthttp/setup.sh → frameworks/Go/fasthttp-mysql/setup.sh


+ 0 - 0
frameworks/Go/fasthttp/setup_prefork.sh → frameworks/Go/fasthttp-mysql/setup_prefork.sh


+ 0 - 0
frameworks/Go/fasthttp/source_code → frameworks/Go/fasthttp-mysql/source_code


+ 0 - 0
frameworks/Go/fasthttp/src/hello/hello.go → frameworks/Go/fasthttp-mysql/src/hello/hello.go


+ 0 - 0
frameworks/Go/fasthttp/templates/fortune.html → frameworks/Go/fasthttp-mysql/templates/fortune.html


+ 0 - 0
frameworks/Go/fasthttp/templates/layout.html → frameworks/Go/fasthttp-mysql/templates/layout.html


+ 16 - 0
frameworks/Go/fasthttp-postgresql/README.md

@@ -0,0 +1,16 @@
+# [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.
+
+"Fasthttp is a fast http package for Go."
+
+# This variant uses Postgres via Jack Christensen's pgx library
+
+## Test URLs
+
+    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

+ 51 - 0
frameworks/Go/fasthttp-postgresql/benchmark_config.json

@@ -0,0 +1,51 @@
+{
+  "framework": "fasthttp-postgresql",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "fasthttp",
+      "language": "Go",
+      "orm": "Raw",
+      "platform": "Go",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "fasthttp",
+      "notes": "",
+      "versus": "go"
+    },
+    "prefork": {
+      "setup_file": "setup_prefork",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "fasthttp",
+      "language": "Go",
+      "orm": "Raw",
+      "platform": "Go",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "fasthttp-prefork",
+      "notes": "",
+      "versus": "go"
+    }
+  }]
+}

+ 2 - 0
frameworks/Go/fasthttp-postgresql/setup.bat

@@ -0,0 +1,2 @@
+set GOPATH=C:\FrameworkBenchmarks\Go\fasthttp
+go run src\hello\hello.go

+ 10 - 0
frameworks/Go/fasthttp-postgresql/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go
+
+fw_depends go
+
+go get -u github.com/jackc/pgx
+go get -u github.com/valyala/fasthttp
+
+go run src/hello/hello.go &

+ 10 - 0
frameworks/Go/fasthttp-postgresql/setup_prefork.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/hello/hello.go
+
+fw_depends go
+
+go get -u github.com/jackc/pgx
+go get -u github.com/valyala/fasthttp
+
+go run src/hello/hello.go -prefork &

+ 6 - 0
frameworks/Go/fasthttp-postgresql/source_code

@@ -0,0 +1,6 @@
+./go/src/
+./go/src/hello
+./go/src/hello/hello.go
+./go/templates/
+./go/templates/fortune.html
+./go/templates/layout.html

+ 314 - 0
frameworks/Go/fasthttp-postgresql/src/hello/hello.go

@@ -0,0 +1,314 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"html/template"
+	"log"
+	"math/rand"
+	"net"
+	"os"
+	"os/exec"
+	"runtime"
+	"sort"
+
+	"github.com/jackc/pgx"
+	"github.com/valyala/fasthttp"
+	"github.com/valyala/fasthttp/reuseport"
+)
+
+type Message struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           int32 `json:"id"`
+	RandomNumber int32 `json:"randomNumber"`
+}
+
+type Fortune struct {
+	Id      int32  `json:"id"`
+	Message string `json:"message"`
+}
+
+const (
+	worldRowCount      = 10000
+	maxConnectionCount = 256
+)
+
+var (
+	worldSelectStmt   *pgx.PreparedStatement
+	worldUpdateStmt   *pgx.PreparedStatement
+	fortuneSelectStmt *pgx.PreparedStatement
+)
+
+const helloWorldString = "Hello, World!"
+
+const largerJson = `{status:"0",message:"ok",data:[{item:"car"},{item:"house"},{item:"airplane"},{item:"train"}]}`
+
+var (
+	tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))
+
+	db *pgx.ConnPool
+
+	helloWorldBytes = []byte(helloWorldString)
+)
+
+var (
+	listenAddr = flag.String("listenAddr", ":8080", "Address to listen to")
+	prefork    = flag.Bool("prefork", false, "use prefork")
+	child      = flag.Bool("child", false, "is child proc")
+)
+
+func main() {
+	flag.Parse()
+
+	// func initDatabase(dbHost string, dbUser string, dbPass string, dbName string, dbPort int) (*pgx.ConnPool, error) {
+
+	var err error
+	if db, err = initDatabase("localhost", "benchmarkdbuser", "benchmarkdbpass", "hello_world", 5432, maxConnectionCount); err != nil {
+		log.Fatalf("Error opening database: %s", err)
+	}
+
+	s := &fasthttp.Server{
+		Handler: mainHandler,
+		Name:    "fasthttp",
+	}
+	ln := getListener()
+	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(path) {
+	case "/plaintext":
+		plaintextHandler(ctx)
+	case "/json":
+		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)
+	}
+}
+
+// Test 1: JSON serialization
+func jsonHandler(ctx *fasthttp.RequestCtx) {
+	//jsonMarshal(ctx, &Message{helloWorldString})
+	jsonMarshal(ctx, &Message{largerJson})
+}
+
+// Test 2: Single database query
+func dbHandler(ctx *fasthttp.RequestCtx) {
+	var w World
+	fetchRandomWorld(&w)
+	jsonMarshal(ctx, &w)
+}
+
+// Test 3: Multiple database queries
+func queriesHandler(ctx *fasthttp.RequestCtx) {
+	n := getQueriesCount(ctx)
+
+	worlds := make([]World, n)
+	for i := 0; i < n; i++ {
+		fetchRandomWorld(&worlds[i])
+	}
+
+	jsonMarshal(ctx, worlds)
+}
+
+// Test 4: Fortunes
+func fortuneHandler(ctx *fasthttp.RequestCtx) {
+	rows, err := db.Query("fortuneSelectStmt")
+	if err != nil {
+		log.Fatalf("Error selecting db data: %v", err)
+	}
+
+	fortunes := make([]Fortune, 0, 16)
+	for rows.Next() {
+		var f Fortune
+		if err := rows.Scan(&f.Id, &f.Message); err != nil {
+			log.Fatalf("Error scanning fortune row: %s", err)
+		}
+		fortunes = append(fortunes, f)
+	}
+	rows.Close()
+	fortunes = append(fortunes, Fortune{Message: "Additional fortune added at request time."})
+
+	sort.Sort(FortunesByMessage(fortunes))
+
+	ctx.SetContentType("text/html")
+	if err := tmpl.Execute(ctx, fortunes); err != nil {
+		log.Fatalf("Error executing fortune: %s", err)
+	}
+}
+
+// Test 5: Database updates
+func updateHandler(ctx *fasthttp.RequestCtx) {
+	n := getQueriesCount(ctx)
+
+	worlds := make([]World, n)
+	for i := 0; i < n; i++ {
+		w := &worlds[i]
+		fetchRandomWorld(w)
+		w.RandomNumber = int32(randomWorldNum())
+	}
+
+	// sorting is required for insert deadlock prevention.
+	sort.Sort(WorldsByID(worlds))
+	txn, err := db.Begin()
+	if err != nil {
+		log.Fatalf("Error starting transaction: %s", err)
+	}
+
+	for i := 0; i < n; i++ {
+		w := &worlds[i]
+		if _, err := txn.Exec("worldUpdateStmt", w.RandomNumber, w.Id); err != nil {
+			log.Fatalf("Error updating world row %d: %s", i, err)
+		}
+	}
+	if err = txn.Commit(); err != nil {
+		log.Fatalf("Error when commiting world rows: %s", err)
+	}
+
+	jsonMarshal(ctx, worlds)
+}
+
+// Test 6: Plaintext
+func plaintextHandler(ctx *fasthttp.RequestCtx) {
+	ctx.Success("text/plain", helloWorldBytes)
+}
+
+func jsonMarshal(ctx *fasthttp.RequestCtx, v interface{}) {
+	ctx.SetContentType("application/json")
+	if err := json.NewEncoder(ctx).Encode(v); err != nil {
+		log.Fatalf("error in json.Encoder.Encode: %s", err)
+	}
+}
+
+func fetchRandomWorld(w *World) {
+	n := randomWorldNum()
+
+	if err := db.QueryRow("worldSelectStmt", n).Scan(&w.Id, &w.RandomNumber); err != nil {
+		log.Fatalf("Error scanning world row: %s", err)
+	}
+}
+
+func randomWorldNum() int {
+	return rand.Intn(worldRowCount) + 1
+}
+
+func getQueriesCount(ctx *fasthttp.RequestCtx) int {
+	n := ctx.QueryArgs().GetUintOrZero("queries")
+	if n < 1 {
+		n = 1
+	} else if n > 500 {
+		n = 500
+	}
+	return n
+}
+
+type FortunesByMessage []Fortune
+
+func (s FortunesByMessage) Len() int           { return len(s) }
+func (s FortunesByMessage) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s FortunesByMessage) Less(i, j int) bool { return s[i].Message < s[j].Message }
+
+type WorldsByID []World
+
+func (w WorldsByID) Len() int           { return len(w) }
+func (w WorldsByID) Swap(i, j int)      { w[i], w[j] = w[j], w[i] }
+func (w WorldsByID) Less(i, j int) bool { return w[i].Id < w[j].Id }
+
+func mustPrepare(db *pgx.Conn, name, query string) *pgx.PreparedStatement {
+	stmt, err := db.Prepare(name, query)
+	if err != nil {
+		log.Fatalf("Error when preparing statement %q: %s", query, err)
+	}
+	return stmt
+}
+
+func getListener() net.Listener {
+	if !*prefork {
+		runtime.GOMAXPROCS(runtime.NumCPU())
+		ln, err := net.Listen("tcp4", *listenAddr)
+		if err != nil {
+			log.Fatal(err)
+		}
+		return ln
+	}
+
+	if !*child {
+		children := make([]*exec.Cmd, runtime.NumCPU())
+		for i := range children {
+			children[i] = exec.Command(os.Args[0], "-prefork", "-child")
+			children[i].Stdout = os.Stdout
+			children[i].Stderr = os.Stderr
+			if err := children[i].Start(); err != nil {
+				log.Fatal(err)
+			}
+		}
+		for _, ch := range children {
+			if err := ch.Wait(); err != nil {
+				log.Print(err)
+			}
+		}
+		os.Exit(0)
+		panic("unreachable")
+	}
+
+	runtime.GOMAXPROCS(1)
+	ln, err := reuseport.Listen("tcp4", *listenAddr)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return ln
+}
+
+func initDatabase(dbHost string, dbUser string, dbPass string, dbName string, dbPort uint16, maxConnectionsInPool int) (*pgx.ConnPool, error) {
+
+	var successOrFailure string = "OK"
+
+	var config pgx.ConnPoolConfig
+
+	config.Host = dbHost
+	config.User = dbUser
+	config.Password = dbPass
+	config.Database = dbName
+	config.Port = dbPort
+
+	config.MaxConnections = maxConnectionsInPool
+
+	config.AfterConnect = func(eachConn *pgx.Conn) error {
+
+		worldSelectStmt = mustPrepare(eachConn, "worldSelectStmt", "SELECT id, randomNumber FROM World WHERE id = $1")
+		worldUpdateStmt = mustPrepare(eachConn, "worldUpdateStmt", "UPDATE World SET randomNumber = $1 WHERE id = $2")
+		fortuneSelectStmt = mustPrepare(eachConn, "fortuneSelectStmt", "SELECT id, message FROM Fortune")
+
+		return nil
+	}
+
+	fmt.Println("--------------------------------------------------------------------------------------------")
+
+	connPool, err := pgx.NewConnPool(config)
+	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)
+	}
+
+	fmt.Println("--------------------------------------------------------------------------------------------")
+
+	return connPool, err
+
+}

+ 14 - 0
frameworks/Go/fasthttp-postgresql/templates/fortune.html

@@ -0,0 +1,14 @@
+{{define "content"}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+{{end}}

+ 9 - 0
frameworks/Go/fasthttp-postgresql/templates/layout.html

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+{{template "content" .}}
+</body>
+</html>