Browse Source

updated Go/iris (#4436)

* updated Go/iris

* removed vendor from Go/iris

* fixed golang ver in Go/iris
chunariov 6 years ago
parent
commit
7a3763469d

+ 3 - 2
frameworks/Go/iris/README.md

@@ -9,5 +9,6 @@ This is the go portion of a [benchmarking test suite](https://www.techempower.co
     http://localhost:8080/json
     http://localhost:8080/db
     http://localhost:8080/queries?queries=[1-500]
-    http://localhost:8080/updates?queries=[1-500]
-    http://localhost:8080/plaintext
+    http://localhost:8080/update?queries=[1-500]
+    http://localhost:8080/plaintext
+    http://localhost:8080/fortune

+ 3 - 2
frameworks/Go/iris/benchmark_config.json

@@ -4,12 +4,13 @@
     "default": {
       "json_url": "/json",
       "plaintext_url": "/plaintext",
+      "fortune_url": "/fortune",
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "update_url": "/update?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "Postgres",
       "framework": "None",
       "language": "Go",
@@ -24,4 +25,4 @@
       "versus": "go"
     }
   }]
-}
+}

+ 6 - 9
frameworks/Go/iris/iris.dockerfile

@@ -1,14 +1,11 @@
-FROM golang:1.10.1
+FROM golang:1.11.5
 
-COPY ./ /iris
-WORKDIR /iris
+WORKDIR /go/src/app
 
-RUN mkdir bin
-ENV GOPATH /iris
-ENV PATH ${GOPATH}/bin:${PATH}
-
-RUN rm -rf ./pkg/*
 RUN go get github.com/kataras/iris
 RUN go get github.com/lib/pq
+COPY src/. .
+
+RUN go build -ldflags="-s -w" -o app .
 
-CMD go run main.go
+CMD ./app

+ 0 - 177
frameworks/Go/iris/main.go

@@ -1,177 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"strconv"
-	"math/rand"
-
-	"github.com/kataras/iris"
-
-	"database/sql"
-	_ "github.com/lib/pq"
-
-	"github.com/kataras/iris/context"
-	"github.com/kataras/iris/middleware/logger"
-	"github.com/kataras/iris/middleware/recover"
-)
-
-func main() {
-	app := iris.New()
-	app.Logger().SetLevel("debug")
-	// Optionally, add two built'n handlers
-	// that can recover from any http-relative panics
-	// and log the requests to the terminal.
-	app.Use(recover.New())
-	app.Use(logger.New())
-
-	connStr := "user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world host=tfb-database sslmode=disable"
-
-	db, err := sql.Open("postgres", connStr)
-	if err != nil {
-		panic(err)
-	}
-
-	defer db.Close()
-
-	err = db.Ping()
-	if err != nil {
-		panic(err)
-	}
-
-	fmt.Println("Successfully connected to PG!")
-
-	// Helper methods
-
-	type fetchingFunction func() map[string] int
-
-	var getOneRandomWorld fetchingFunction = func() map[string]int {
-		results := make(map[string]int)
-
-		randInt := rand.Intn(10000) + 1
-		sqlStatement := `SELECT id, randomnumber FROM world WHERE id=$1`
-
-		rows, err := db.Query(sqlStatement, randInt)
-		if err != nil {
-			panic(err)
-		}
-
-		defer rows.Close()
-		for rows.Next() {
-			var id int
-			var randomnumber int
-			err = rows.Scan(&id, &randomnumber)
-			if err != nil {
-				panic(err)
-			}
-			results["id"] = id
-			results["randomnumber"] = randomnumber
-		}
-
-		err = rows.Err()
-		if err != nil {
-			panic(err)
-		}
-
-		return results
-	}
-
-	var updateOneRandomWorld fetchingFunction = func() map[string]int {
-		results := make(map[string]int)
-
-		randInt1 := rand.Intn(10000) + 1
-		randInt2 := rand.Intn(10000) + 1
-
-		sqlStatement := `UPDATE world SET randomnumber=$1 WHERE id=$2 RETURNING id, randomnumber`
-
-		rows, err := db.Query(sqlStatement, randInt1, randInt2)
-		if err != nil {
-			panic(err)
-		}
-
-		defer rows.Close()
-		for rows.Next() {
-			var id int
-			var randomnumber int
-			err = rows.Scan(&id, &randomnumber)
-			if err != nil {
-				panic(err)
-			}
-			results["id"] = id
-			results["randomnumber"] = randomnumber
-		}
-
-		err = rows.Err()
-		if err != nil {
-			panic(err)
-		}
-
-		return results
-	}
-
-	// Routes
-	app.Handle("GET", "/json", func(ctx context.Context) {
-		ctx.Header("Server", "Iris")
-		ctx.JSON(context.Map{"message": "Hello, World!"})
-	})
-
-	app.Handle("GET", "/plaintext", func(ctx context.Context) {
-		ctx.Header("Server", "Iris")
-		ctx.Text("Hello, World!")
-	})
-
-	app.Handle("GET", "/db", func(ctx context.Context) {
-
-		results := getOneRandomWorld()
-
-		ctx.Header("Server", "Iris")
-		ctx.JSON(results)
-	})
-
-	app.Handle("GET", "/queries", func(ctx context.Context) {
-		queryVal := ctx.FormValue("queries")
-		q, err := strconv.Atoi(queryVal)
-		if err != nil {
-			q = 1
-		}
-		if q < 1 {
-			q = 1
-		} else if q > 500 {
-			q = 500
-		}
-		
-		results := make([]map[string]int, q)
-
-		i := 0
-		for i < q {
-			results[i] = getOneRandomWorld()
-			i = i + 1
-		}
-		ctx.Header("Server", "Iris")
-		ctx.JSON(results)
-	})
-
-	app.Handle("GET", "/update", func(ctx context.Context) {
-		queryVal := ctx.FormValue("queries")
-		q, err := strconv.Atoi(queryVal)
-		if err != nil {
-			q = 1
-		}
-		if q < 1 {
-			q = 1
-		} else if q > 500 {
-			q = 500
-		}
-		
-		results := make([]map[string]int, q)
-
-		i := 0
-		for i < q {
-			results[i] = updateOneRandomWorld()
-			i = i + 1
-		}
-		ctx.Header("Server", "Iris")
-		ctx.JSON(results)
-	})
-
-	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
-}

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

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

+ 7 - 0
frameworks/Go/iris/src/fortune.go

@@ -0,0 +1,7 @@
+package main
+
+// Fortune struct
+type Fortune struct {
+	ID      int    `json:"id,omitempty"`
+	Message string `json:"message,omitempty"`
+}

+ 86 - 0
frameworks/Go/iris/src/handlers.go

@@ -0,0 +1,86 @@
+package main
+
+import (
+	"database/sql"
+	"log"
+	"sort"
+	"strconv"
+
+	"github.com/kataras/iris/context"
+)
+
+func jsonHandler(ctx context.Context) {
+	ctx.Header("Server", "Iris")
+	ctx.JSON(context.Map{"message": "Hello, World!"})
+}
+
+func plaintextHandler(ctx context.Context) {
+	ctx.Header("Server", "Iris")
+	ctx.Text("Hello, World!")
+}
+
+func dbHandler(ctx context.Context) {
+	ctx.Header("Server", "Iris")
+	ctx.JSON(getOneRandomWorld())
+}
+
+func queriesHandler(ctx context.Context) {
+	q, err := strconv.Atoi(ctx.URLParam("queries"))
+	if err != nil || q < 1 {
+		q = 1
+	} else if q > 500 {
+		q = 500
+	}
+
+	results := make([]World, q)
+	for i := 0; i < q; i++ {
+		results[i] = getOneRandomWorld()
+	}
+
+	ctx.Header("Server", "Iris")
+	ctx.JSON(results)
+}
+
+func updateHandler(db *sql.DB) func(ctx context.Context) {
+	return func(ctx context.Context) {
+		q, err := strconv.Atoi(ctx.URLParam("queries"))
+		if err != nil || q < 1 {
+			q = 1
+		} else if q > 500 {
+			q = 500
+		}
+
+		ctx.Header("Server", "Iris")
+		ctx.JSON(updateRandomWorlds(db, q))
+	}
+}
+
+func fortuneHandler(ctx context.Context) {
+	var err error
+	var rows *sql.Rows
+	rows, err = fortuneStmt.Query()
+	if err != nil {
+		log.Fatalf("Can't query fortunes: %s\n", err)
+	}
+
+	var fortunes []Fortune
+
+	var fortune Fortune
+	for rows.Next() {
+		if err = rows.Scan(&fortune.ID, &fortune.Message); err != nil {
+			log.Fatalf("Can't scan fortune: %s\n", err)
+		}
+		fortunes = append(fortunes, fortune)
+	}
+	rows.Close()
+	fortunes = append(fortunes, Fortune{Message: "Additional fortune added at request time."})
+
+	sort.Slice(fortunes, func(i, j int) bool {
+		return fortunes[i].Message < fortunes[j].Message
+	})
+
+	ctx.Header("Server", "Iris")
+	ctx.View("fortunes.html", struct {
+		Fortunes []Fortune
+	}{fortunes})
+}

+ 50 - 0
frameworks/Go/iris/src/main.go

@@ -0,0 +1,50 @@
+package main
+
+import (
+	"context"
+	"log"
+	"runtime"
+	"time"
+
+	"github.com/kataras/iris"
+
+	"database/sql"
+
+	_ "github.com/lib/pq"
+
+	"github.com/kataras/iris/middleware/recover"
+)
+
+func main() {
+	app := iris.New()
+	app.Use(recover.New())
+
+	app.RegisterView(iris.HTML("./templates", ".html"))
+
+	var err error
+	var db *sql.DB
+	if db, err = initDatabase("tfb-database",
+		"benchmarkdbuser",
+		"benchmarkdbpass",
+		"hello_world",
+		runtime.NumCPU()); err != nil {
+		log.Fatalf("Error opening database: %s", err)
+	}
+	defer db.Close()
+
+	app.Handle("GET", "/json", jsonHandler)
+	app.Handle("GET", "/plaintext", plaintextHandler)
+	app.Handle("GET", "/db", dbHandler)
+	app.Handle("GET", "/queries", queriesHandler)
+	app.Handle("GET", "/update", updateHandler(db))
+	app.Handle("GET", "/fortune", fortuneHandler)
+
+	iris.RegisterOnInterrupt(func() {
+		timeout := 10 * time.Second
+		ctx, cancel := context.WithTimeout(context.Background(), timeout)
+		defer cancel()
+		app.Shutdown(ctx)
+	})
+
+	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithoutInterruptHandler)
+}

+ 20 - 0
frameworks/Go/iris/src/templates/fortunes.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>Fortunes</title>
+</head>
+
+<body>
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>{{range .Fortunes}}<tr>
+            <td>{{.ID}}</td>
+            <td>{{.Message}}</td>
+        </tr>{{end}}
+    </table>
+</body>
+
+</html>

+ 7 - 0
frameworks/Go/iris/src/world.go

@@ -0,0 +1,7 @@
+package main
+
+// World struct
+type World struct {
+	ID           int `json:"id"`
+	RandomNumber int `json:"randomnumber"`
+}