Browse Source

Add option to run chi in pre fork mode (#3572)

David Cuadrado 7 years ago
parent
commit
e0c8c4fdeb

+ 23 - 0
frameworks/Go/chi/benchmark_config.json

@@ -23,6 +23,29 @@
       "display_name": "Chi",
       "notes": "",
       "versus": "go"
+    },
+    "prefork": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "Chi",
+      "language": "Go",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Chi-prefork",
+      "notes": "",
+      "versus": "go"
     }
   }]
 }

+ 14 - 0
frameworks/Go/chi/chi-prefork.dockerfile

@@ -0,0 +1,14 @@
+FROM golang:1.10.1
+
+ADD ./ /chi
+WORKDIR /chi
+
+RUN mkdir bin
+ENV GOPATH /chi
+ENV PATH ${GOPATH}/bin:${PATH}
+
+RUN go get github.com/mailru/easyjson/...
+RUN go get github.com/jackc/pgx
+RUN go get github.com/go-chi/chi
+
+CMD go run src/chi/*.go -prefork

+ 47 - 0
frameworks/Go/chi/src/chi/db.go

@@ -0,0 +1,47 @@
+package main
+
+import (
+	"flag"
+	"log"
+
+	"github.com/jackc/pgx"
+)
+
+var (
+	defaultDB *pgx.ConnPool
+)
+
+func initDBConnection(pgURL string) {
+	config, err := pgx.ParseConnectionString(pgURL)
+	if err != nil {
+		flag.PrintDefaults()
+		log.Fatalf("Error parsing db URL: %v", err)
+	}
+
+	connPoolConfig := pgx.ConnPoolConfig{
+		ConnConfig:     config,
+		MaxConnections: maxConnectionCount,
+	}
+
+	defaultDB, err = pgx.NewConnPool(connPoolConfig)
+	if err != nil {
+		flag.PrintDefaults()
+		log.Fatalf("Error opening database: %v", err)
+	}
+
+	_, err = defaultDB.Prepare("worldSelect", worldSelect)
+	if err != nil {
+		flag.PrintDefaults()
+		log.Fatal(err)
+	}
+	_, err = defaultDB.Prepare("fortuneSelect", fortuneSelect)
+	if err != nil {
+		flag.PrintDefaults()
+		log.Fatal(err)
+	}
+	_, err = defaultDB.Prepare("worldUpdate", worldUpdate)
+	if err != nil {
+		flag.PrintDefaults()
+		log.Fatal(err)
+	}
+}

+ 60 - 0
frameworks/Go/chi/src/chi/prefork.go

@@ -0,0 +1,60 @@
+package main
+
+import (
+	"log"
+	"net"
+	"os"
+	"os/exec"
+	"runtime"
+)
+
+func doPrefork(isChild bool, bind string) (listener net.Listener) {
+	var err error
+	var fl *os.File
+	var tcplistener *net.TCPListener
+	if !isChild {
+		var addr *net.TCPAddr
+		addr, err = net.ResolveTCPAddr("tcp", bind)
+		if err != nil {
+			log.Fatal(err)
+		}
+		tcplistener, err = net.ListenTCP("tcp", addr)
+		if err != nil {
+			log.Fatal(err)
+		}
+		fl, err = tcplistener.File()
+		if err != nil {
+			log.Fatal(err)
+		}
+		children := make([]*exec.Cmd, runtime.NumCPU()/2)
+		for i := range children {
+			args := make([]string, len(os.Args)-1)
+			copy(args, os.Args[1:])
+			args = append(args, "-child")
+
+			children[i] = exec.Command(os.Args[0], args...)
+			children[i].Stdout = os.Stdout
+			children[i].Stderr = os.Stderr
+			children[i].ExtraFiles = []*os.File{fl}
+			err = children[i].Start()
+			if err != nil {
+				log.Fatal(err)
+			}
+		}
+		for _, ch := range children {
+			err := ch.Wait()
+			if err != nil {
+				log.Print(err)
+			}
+		}
+		os.Exit(0)
+	} else {
+		fl = os.NewFile(3, "")
+		listener, err = net.FileListener(fl)
+		if err != nil {
+			log.Fatal(err)
+		}
+		runtime.GOMAXPROCS(2)
+	}
+	return listener
+}

+ 38 - 46
frameworks/Go/chi/src/chi/server.go

@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"log"
 	"math/rand"
+	"net"
 	"net/http"
 	"sort"
 	"strconv"
@@ -42,11 +43,12 @@ const (
 var (
 	connectionString = "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world?sslmode=disable"
 	bindHost         = ":8080"
-	debug            = false
+	debugFlag        = false
+	preforkFlag      = false
+	childFlag        = false
 
 	// Database
 	helloWorldMessage = &Message{helloWorldString}
-	db                *pgx.ConnPool
 )
 
 // Message is a JSON struct to render a message
@@ -61,7 +63,7 @@ type World struct {
 }
 
 func randomRow() *pgx.Row {
-	return db.QueryRow("worldSelect", rand.Intn(worldRowCount)+1)
+	return defaultDB.QueryRow("worldSelect", rand.Intn(worldRowCount)+1)
 }
 
 // Fortune renders a fortune in JSON
@@ -139,7 +141,7 @@ func multipleQueries(w http.ResponseWriter, r *http.Request) {
 
 // Test 4: Fortunes
 func fortunes(w http.ResponseWriter, r *http.Request) {
-	rows, err := db.Query("fortuneSelect")
+	rows, err := defaultDB.Query("fortuneSelect")
 	if err != nil {
 		log.Printf("Error preparing statement: %v", err)
 	}
@@ -180,7 +182,7 @@ func dbupdate(w http.ResponseWriter, r *http.Request) {
 			log.Printf("Error scanning world row: %s", err.Error())
 		}
 		worlds[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
-		if _, err := db.Exec("worldUpdate", w.RandomNumber, w.ID); err != nil {
+		if _, err := defaultDB.Exec("worldUpdate", w.RandomNumber, w.ID); err != nil {
 			log.Printf("Error updating world row: %s", err.Error())
 		}
 	}
@@ -198,48 +200,13 @@ func plaintext(w http.ResponseWriter, r *http.Request) {
 func init() {
 	flag.StringVar(&bindHost, "bind", bindHost, "Set bind host")
 	flag.StringVar(&connectionString, "db", connectionString, "Set database URL")
-	flag.BoolVar(&debug, "debug", false, "Enable debug mode")
+	flag.BoolVar(&debugFlag, "debug", false, "Enable debug mode")
+	flag.BoolVar(&preforkFlag, "prefork", false, "Enable prefork mode")
+	flag.BoolVar(&childFlag, "child", false, "Enable child mode")
 	flag.Parse()
 }
 
-func main() {
-	if !debug {
-		log.SetOutput(ioutil.Discard)
-	}
-
-	config, err := pgx.ParseConnectionString(connectionString)
-	if err != nil {
-		flag.PrintDefaults()
-		log.Fatalf("Error parsing db URL: %v", err)
-	}
-
-	connPoolConfig := pgx.ConnPoolConfig{
-		ConnConfig:     config,
-		MaxConnections: maxConnectionCount,
-	}
-
-	db, err = pgx.NewConnPool(connPoolConfig)
-	if err != nil {
-		flag.PrintDefaults()
-		log.Fatalf("Error opening database: %v", err)
-	}
-
-	_, err = db.Prepare("worldSelect", worldSelect)
-	if err != nil {
-		flag.PrintDefaults()
-		log.Fatal(err)
-	}
-	_, err = db.Prepare("fortuneSelect", fortuneSelect)
-	if err != nil {
-		flag.PrintDefaults()
-		log.Fatal(err)
-	}
-	_, err = db.Prepare("worldUpdate", worldUpdate)
-	if err != nil {
-		flag.PrintDefaults()
-		log.Fatal(err)
-	}
-
+func initRouter() http.Handler {
 	r := chi.NewRouter()
 
 	r.Get("/json", serializeJSON)
@@ -249,8 +216,33 @@ func main() {
 	r.Get("/plaintext", plaintext)
 	r.Get("/updates", dbupdate)
 
-	err = http.ListenAndServe(bindHost, r)
-	if err != nil {
+	return r
+}
+
+func startListening(listener net.Listener) error {
+	var err error
+	if !preforkFlag {
+		err = http.ListenAndServe(bindHost, initRouter())
+	} else {
+		err = http.Serve(listener, initRouter())
+	}
+
+	return err
+}
+
+func main() {
+	var listener net.Listener
+	if preforkFlag {
+		listener = doPrefork(childFlag, bindHost)
+	}
+
+	initDBConnection(connectionString)
+
+	if !debugFlag {
+		log.SetOutput(ioutil.Discard)
+	}
+
+	if err := startListening(listener); err != nil {
 		log.Fatal(err)
 	}
 }