Browse Source

Merge pull request #342 from robfig/master

Revel: Add tests for micro and full ORM
michaelhixson 12 years ago
parent
commit
a12c7ac3f2

+ 14 - 0
revel-jet/benchmark_config

@@ -0,0 +1,14 @@
+{
+  "framework": "revel-jet",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "port": 8080,
+      "sort": 142
+    }
+  }]
+}

+ 34 - 0
revel-jet/setup.py

@@ -0,0 +1,34 @@
+import subprocess
+import sys
+import os
+import setup_util
+import time
+
+CWD = 'revel-jet'
+
+def start(args):
+  setup_util.replace_text(CWD + "/src/benchmark/conf/app.conf", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
+  if os.name == 'nt':
+    env = os.environ.copy()
+    env["GOPATH"] = "C:\\FrameworkBenchmarks\\" + CWD
+    subprocess.call("go get -u github.com/robfig/revel/revel github.com/eaigner/jet", shell=True, cwd=CWD, env=env)
+    subprocess.call(r"go build -o bin\revel.exe github.com/robfig/revel/revel", shell=True, cwd=CWD, env=env)
+    subprocess.Popen(r"bin\revel.exe run benchmark prod".rsplit(" "), shell=True, cwd=CWD, env=env)
+    return 0
+  subprocess.call("go get -u github.com/robfig/revel/revel github.com/eaigner/jet", shell=True, cwd=CWD)
+  subprocess.call("go build -o bin/revel github.com/robfig/revel/revel", shell=True, cwd=CWD)
+  subprocess.Popen("bin/revel run benchmark prod".rsplit(" "), cwd=CWD)
+  return 0
+
+def stop():
+  if os.name == 'nt':
+    subprocess.call("taskkill /f /im benchmark.exe > NUL", shell=True)
+    subprocess.call("taskkill /f /im revel.exe > NUL", shell=True)
+    return 0
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'revel' in line and 'run-tests' not in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 9)
+  return 0

+ 125 - 0
revel-jet/src/benchmark/app/controllers/app.go

@@ -0,0 +1,125 @@
+package controllers
+
+import (
+	"benchmark/app/db"
+	"github.com/robfig/revel"
+	"math/rand"
+	"runtime"
+	"sort"
+)
+
+type MessageStruct struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           uint16 `json:"id"`
+	RandomNumber uint16 `json:"randomNumber"`
+}
+
+type Fortune struct {
+	Id      uint16 `json:"id"`
+	Message string `json:"message"`
+}
+
+const (
+	WorldSelect        = `SELECT id, randomNumber FROM World WHERE id = ?`
+	WorldUpdate        = `UPDATE World SET randomNumber = ? WHERE id = ?`
+	FortuneSelect      = `SELECT id, message FROM Fortune`
+	WorldRowCount      = 10000
+	MaxConnectionCount = 260
+)
+
+func init() {
+	revel.Filters = []revel.Filter{
+		revel.RouterFilter,
+		revel.ParamsFilter,
+		revel.ActionInvoker,
+	}
+	revel.OnAppStart(func() {
+		runtime.GOMAXPROCS(runtime.NumCPU())
+		db.Init()
+		db.Jet.SetMaxIdleConns(MaxConnectionCount)
+	})
+}
+
+type App struct {
+	*revel.Controller
+}
+
+func (c App) Json() revel.Result {
+	c.Response.ContentType = "application/json"
+	return c.RenderJson(MessageStruct{"Hello, world"})
+}
+
+func (c App) Plaintext() revel.Result {
+	return c.RenderText("Hello, World!")
+}
+
+func (c App) Db(queries int) revel.Result {
+	if queries <= 1 {
+		var w World
+		err := db.Jet.Query(WorldSelect, rand.Intn(WorldRowCount)+1).Rows(&w)
+		if err != nil {
+			revel.ERROR.Fatalf("Db/WorldSelect error: %v", err)
+		}
+		return c.RenderJson(w)
+	}
+
+	ww := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		err := db.Jet.Query(WorldSelect, rand.Intn(WorldRowCount)+1).Rows(&ww[i])
+		if err != nil {
+			revel.ERROR.Fatalf("Db/WorldSelect2 error: %v", err)
+		}
+	}
+	return c.RenderJson(ww)
+}
+
+func (c App) Update(queries int) revel.Result {
+	if queries <= 1 {
+		var w World
+		err := db.Jet.Query(WorldSelect, rand.Intn(WorldRowCount)+1).Rows(&w)
+		if err != nil {
+			revel.ERROR.Fatalf("Update/WorldSelect error: %v", err)
+		}
+		w.RandomNumber = uint16(rand.Intn(WorldRowCount) + 1)
+		if err = db.Jet.Query(WorldUpdate, w.RandomNumber, w.Id).Run(); err != nil {
+			revel.ERROR.Fatalf("Update/WorldUpdate error: %v", err)
+		}
+		return c.RenderJson(&w)
+	}
+
+	ww := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		err := db.Jet.Query(WorldSelect, rand.Intn(WorldRowCount)+1).Rows(&ww[i])
+		if err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+		ww[i].RandomNumber = uint16(rand.Intn(WorldRowCount) + 1)
+		if err = db.Jet.Query(WorldUpdate, ww[i].RandomNumber, ww[i].Id).Run(); err != nil {
+			revel.ERROR.Fatalf("Update/WorldUpdate2 error: %v", err)
+		}
+	}
+	return c.RenderJson(ww)
+}
+
+func (c App) Fortune() revel.Result {
+	var fortunes Fortunes
+	err := db.Jet.Query(FortuneSelect).Rows(&fortunes)
+	if err != nil {
+		revel.ERROR.Fatalf("Fortune/FortuneSelect error: %v", err)
+	}
+	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
+	sort.Sort(ByMessage{fortunes})
+	return c.Render(fortunes)
+}
+
+type Fortunes []*Fortune
+
+func (s Fortunes) Len() int      { return len(s) }
+func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type ByMessage struct{ Fortunes }
+
+func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }

+ 32 - 0
revel-jet/src/benchmark/app/db/jet.go

@@ -0,0 +1,32 @@
+package db
+
+import (
+	"github.com/eaigner/jet"
+	"github.com/robfig/revel"
+	// "os"
+)
+
+var (
+	Jet    jet.Db
+	Driver string
+	Spec   string
+)
+
+func Init() {
+	// Read configuration.
+	var found bool
+	if Driver, found = revel.Config.String("db.driver"); !found {
+		revel.ERROR.Fatal("No db.driver found.")
+	}
+	if Spec, found = revel.Config.String("db.spec"); !found {
+		revel.ERROR.Fatal("No db.spec found.")
+	}
+
+	// Open a connection.
+	var err error
+	Jet, err = jet.Open(Driver, Spec)
+	if err != nil {
+		revel.ERROR.Fatal(err)
+	}
+	// Jet.SetLogger(jet.NewLogger(os.Stdout))
+}

+ 14 - 0
revel-jet/src/benchmark/app/views/App/Fortune.html

@@ -0,0 +1,14 @@
+{{template "header.html" .}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .fortunes}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+{{template "footer.html" .}}

+ 2 - 0
revel-jet/src/benchmark/app/views/footer.html

@@ -0,0 +1,2 @@
+</body>
+</html>

+ 6 - 0
revel-jet/src/benchmark/app/views/header.html

@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>

+ 28 - 0
revel-jet/src/benchmark/conf/app.conf

@@ -0,0 +1,28 @@
+app.name=benchmark
+http.addr=
+http.port=8080
+
+db.driver = mysql
+db.import = github.com/go-sql-driver/mysql
+db.spec = benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8
+
+results.pretty=false
+results.chunked=false
+
+[dev]
+mode.dev=true
+watch=true
+
+log.trace.output = off
+log.info.output  = off
+log.warn.output  = stderr
+log.error.output = stderr
+
+[prod]
+mode.dev=false
+watch=false
+
+log.trace.output = off
+log.info.output  = off
+log.warn.output  = stderr
+log.error.output = stderr

+ 7 - 0
revel-jet/src/benchmark/conf/routes

@@ -0,0 +1,7 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+GET     /db                                     App.Db
+GET     /fortune                                App.Fortune
+GET     /update                                 App.Update

+ 14 - 0
revel-qbs/benchmark_config

@@ -0,0 +1,14 @@
+{
+  "framework": "revel-qbs",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/update?queries=",
+      "port": 8080,
+      "sort": 141
+    }
+  }]
+}

+ 34 - 0
revel-qbs/setup.py

@@ -0,0 +1,34 @@
+import subprocess
+import sys
+import os
+import setup_util
+import time
+
+CWD = 'revel-qbs'
+
+def start(args):
+  setup_util.replace_text(CWD + "/src/benchmark/conf/app.conf", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
+  if os.name == 'nt':
+    env = os.environ.copy()
+    env["GOPATH"] = "C:\\FrameworkBenchmarks\\" + CWD
+    subprocess.call("go get -u github.com/robfig/revel/revel github.com/coocood/qbs", shell=True, cwd=CWD, env=env)
+    subprocess.call(r"go build -o bin\revel.exe github.com/robfig/revel/revel", shell=True, cwd=CWD, env=env)
+    subprocess.Popen(r"bin\revel.exe run benchmark prod".rsplit(" "), shell=True, cwd=CWD, env=env)
+    return 0
+  subprocess.call("go get -u github.com/robfig/revel/revel github.com/coocood/qbs", shell=True, cwd=CWD)
+  subprocess.call("go build -o bin/revel github.com/robfig/revel/revel", shell=True, cwd=CWD)
+  subprocess.Popen("bin/revel run benchmark prod".rsplit(" "), cwd=CWD)
+  return 0
+
+def stop():
+  if os.name == 'nt':
+    subprocess.call("taskkill /f /im benchmark.exe > NUL", shell=True)
+    subprocess.call("taskkill /f /im revel.exe > NUL", shell=True)
+    return 0
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'revel' in line and 'run-tests' not in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 9)
+  return 0

+ 122 - 0
revel-qbs/src/benchmark/app/controllers/app.go

@@ -0,0 +1,122 @@
+package controllers
+
+import (
+	"benchmark/app/db"
+	"github.com/coocood/qbs"
+	"github.com/robfig/revel"
+	"math/rand"
+	"runtime"
+	"sort"
+)
+
+type MessageStruct struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           uint16 `json:"id" qbs:"pk"`
+	RandomNumber uint16 `json:"randomNumber"`
+}
+
+type Fortune struct {
+	Id      uint16 `json:"id" qbs:"pk"`
+	Message string `json:"message"`
+}
+
+const (
+	WorldRowCount      = 10000
+	MaxConnectionCount = 256
+)
+
+func init() {
+	revel.Filters = []revel.Filter{
+		revel.RouterFilter,
+		revel.ParamsFilter,
+		revel.ActionInvoker,
+	}
+	revel.OnAppStart(func() {
+		runtime.GOMAXPROCS(runtime.NumCPU())
+		db.Init()
+		qbs.ChangePoolSize(MaxConnectionCount)
+	})
+}
+
+type App struct {
+	*revel.Controller
+}
+
+func (c App) Db(queries int) revel.Result {
+	qbs, _ := qbs.GetQbs()
+	defer qbs.Close()
+
+	if queries <= 1 {
+		var w World
+		w.Id = uint16(rand.Intn(WorldRowCount) + 1)
+		err := qbs.Find(&w)
+		if err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+		return c.RenderJson(w)
+	}
+
+	ww := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		ww[i].Id = uint16(rand.Intn(WorldRowCount) + 1)
+		if err := qbs.Find(&ww[i]); err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+	}
+	return c.RenderJson(ww)
+}
+
+func (c App) Update(queries int) revel.Result {
+	qbs, _ := qbs.GetQbs()
+	defer qbs.Close()
+
+	if queries <= 1 {
+		var w World
+		w.Id = uint16(rand.Intn(WorldRowCount) + 1)
+		if err := qbs.Find(&w); err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+		w.RandomNumber = uint16(rand.Intn(WorldRowCount) + 1)
+		if _, err := qbs.Save(&w); err != nil {
+			revel.ERROR.Fatalf("Error updating world row: %v", err)
+		}
+		return c.RenderJson(&w)
+	}
+
+	ww := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		ww[i].Id = uint16(rand.Intn(WorldRowCount) + 1)
+		if err := qbs.Find(&ww[i]); err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+		ww[i].RandomNumber = uint16(rand.Intn(WorldRowCount) + 1)
+		if _, err := qbs.Save(&ww[i]); err != nil {
+			revel.ERROR.Fatalf("Error scanning world row: %v", err)
+		}
+	}
+	return c.RenderJson(ww)
+}
+
+func (c App) Fortune() revel.Result {
+	qbs, _ := qbs.GetQbs()
+	defer qbs.Close()
+
+	var fortunes []*Fortune
+	qbs.FindAll(&fortunes)
+	fortunes = append(fortunes,
+		&Fortune{Message: "Additional fortune added at request time."})
+	sort.Sort(ByMessage{fortunes})
+	return c.Render(fortunes)
+}
+
+type Fortunes []*Fortune
+
+func (s Fortunes) Len() int      { return len(s) }
+func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type ByMessage struct{ Fortunes }
+
+func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }

+ 31 - 0
revel-qbs/src/benchmark/app/db/qbs.go

@@ -0,0 +1,31 @@
+package db
+
+import (
+	"github.com/coocood/qbs"
+	"github.com/robfig/revel"
+)
+
+var (
+	Driver string
+	Spec   string
+)
+
+func Init() {
+	var found bool
+	if Driver, found = revel.Config.String("db.driver"); !found {
+		revel.ERROR.Fatal("No db.driver found.")
+	}
+	if Spec, found = revel.Config.String("db.spec"); !found {
+		revel.ERROR.Fatal("No db.spec found.")
+	}
+
+	// QBS uses snake case by default; override the name convention.
+	qbs.ColumnNameToFieldName = noConvert
+	qbs.FieldNameToColumnName = noConvert
+	qbs.TableNameToStructName = noConvert
+	qbs.StructNameToTableName = noConvert
+
+	qbs.Register(Driver, Spec, "", qbs.NewMysql())
+}
+
+func noConvert(s string) string { return s }

+ 14 - 0
revel-qbs/src/benchmark/app/views/App/Fortune.html

@@ -0,0 +1,14 @@
+{{template "header.html" .}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .fortunes}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+{{template "footer.html" .}}

+ 2 - 0
revel-qbs/src/benchmark/app/views/footer.html

@@ -0,0 +1,2 @@
+</body>
+</html>

+ 6 - 0
revel-qbs/src/benchmark/app/views/header.html

@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>

+ 28 - 0
revel-qbs/src/benchmark/conf/app.conf

@@ -0,0 +1,28 @@
+app.name=benchmark
+http.addr=
+http.port=8080
+
+db.driver = mysql
+db.import = github.com/go-sql-driver/mysql
+db.spec = benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8
+
+results.pretty=false
+results.chunked=false
+
+[dev]
+mode.dev=true
+watch=true
+
+log.trace.output = stderr
+log.info.output  = stderr
+log.warn.output  = stderr
+log.error.output = stderr
+
+[prod]
+mode.dev=false
+watch=false
+
+log.trace.output = off
+log.info.output  = off
+log.warn.output  = off
+log.error.output = %(app.name)s.log

+ 7 - 0
revel-qbs/src/benchmark/conf/routes

@@ -0,0 +1,7 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+GET     /db                                     App.Db
+GET     /fortune                                App.Fortune
+GET     /update                                 App.Update

+ 0 - 1
revel/src/benchmark/app/controllers/app.go

@@ -65,7 +65,6 @@ type App struct {
 }
 
 func (c App) Json() revel.Result {
-	c.Response.ContentType = "application/json"
 	return c.RenderJson(MessageStruct{"Hello, world"})
 }