hello_postgres.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "flag"
  6. "html/template"
  7. "log"
  8. "math/rand"
  9. "net/http"
  10. "sort"
  11. "strconv"
  12. _ "github.com/lib/pq"
  13. )
  14. type Message struct {
  15. Message string `json:"message"`
  16. }
  17. type World struct {
  18. Id uint16 `json:"id"`
  19. RandomNumber uint16 `json:"randomNumber"`
  20. }
  21. type Fortune struct {
  22. Id uint16 `json:"id"`
  23. Message string `json:"message"`
  24. }
  25. const (
  26. // Content
  27. fortuneHTML = `<!DOCTYPE html>
  28. <html>
  29. <head>
  30. <title>Fortunes</title>
  31. </head>
  32. <body>
  33. <table>
  34. <tr>
  35. <th>id</th>
  36. <th>message</th>
  37. </tr>
  38. {{range .}}
  39. <tr>
  40. <td>{{.Id}}</td>
  41. <td>{{.Message}}</td>
  42. </tr>
  43. {{end}}
  44. </table>
  45. </body>
  46. </html>`
  47. // Database
  48. connectionString = "postgres://benchmarkdbuser:benchmarkdbpass@TFB-database/hello_world?sslmode=disable"
  49. worldSelect = "SELECT id, randomNumber FROM World WHERE id = $1"
  50. worldUpdate = "UPDATE World SET randomNumber = $1 WHERE id = $2"
  51. fortuneSelect = "SELECT id, message FROM Fortune"
  52. worldRowCount = 10000
  53. maxConnections = 256
  54. )
  55. var (
  56. // Templates
  57. tmpl = template.Must(template.New("fortune.html").Parse(fortuneHTML))
  58. // Database
  59. db *sql.DB
  60. worldSelectPrepared *sql.Stmt
  61. worldUpdatePrepared *sql.Stmt
  62. fortuneSelectPrepared *sql.Stmt
  63. )
  64. func initDB() {
  65. var err error
  66. db, err = sql.Open("postgres", connectionString)
  67. if err != nil {
  68. log.Fatalf("Error opening database: %v", err)
  69. }
  70. db.SetMaxIdleConns(maxConnections)
  71. db.SetMaxOpenConns(maxConnections)
  72. worldSelectPrepared, err = db.Prepare(worldSelect)
  73. if err != nil {
  74. log.Fatal(err)
  75. }
  76. worldUpdatePrepared, err = db.Prepare(worldUpdate)
  77. if err != nil {
  78. log.Fatal(err)
  79. }
  80. fortuneSelectPrepared, err = db.Prepare(fortuneSelect)
  81. if err != nil {
  82. log.Fatal(err)
  83. }
  84. }
  85. func main() {
  86. flag.Parse()
  87. initDB()
  88. http.HandleFunc("/db", dbHandler)
  89. http.HandleFunc("/queries", queriesHandler)
  90. http.HandleFunc("/fortune", fortuneHandler)
  91. http.HandleFunc("/update", updateHandler)
  92. log.Fatal(http.ListenAndServe(":8080", nil))
  93. }
  94. func getQueriesParam(r *http.Request) int {
  95. n := 1
  96. if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
  97. n, _ = strconv.Atoi(nStr)
  98. }
  99. if n < 1 {
  100. n = 1
  101. } else if n > 500 {
  102. n = 500
  103. }
  104. return n
  105. }
  106. // Test 2: Single database query
  107. func dbHandler(w http.ResponseWriter, r *http.Request) {
  108. var world World
  109. err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
  110. if err != nil {
  111. log.Fatalf("Error scanning world row: %s", err.Error())
  112. }
  113. w.Header().Set("Server", "Go")
  114. w.Header().Set("Content-Type", "application/json")
  115. json.NewEncoder(w).Encode(&world)
  116. }
  117. // Test 3: Multiple database queries
  118. func queriesHandler(w http.ResponseWriter, r *http.Request) {
  119. n := getQueriesParam(r)
  120. world := make([]World, n)
  121. for i := 0; i < n; i++ {
  122. err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
  123. if err != nil {
  124. log.Fatalf("Error scanning world row: %v", err)
  125. }
  126. }
  127. w.Header().Set("Server", "Go")
  128. w.Header().Set("Content-Type", "application/json")
  129. json.NewEncoder(w).Encode(world)
  130. }
  131. // Test 4: Fortunes
  132. func fortuneHandler(w http.ResponseWriter, r *http.Request) {
  133. rows, err := fortuneSelectPrepared.Query()
  134. if err != nil {
  135. log.Fatalf("Error preparing statement: %v", err)
  136. }
  137. defer rows.Close()
  138. fortunes := fetchFortunes(rows)
  139. fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
  140. sort.Sort(ByMessage{fortunes})
  141. w.Header().Set("Server", "Go")
  142. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  143. if err := tmpl.Execute(w, fortunes); err != nil {
  144. http.Error(w, err.Error(), http.StatusInternalServerError)
  145. }
  146. }
  147. func fetchFortunes(rows *sql.Rows) Fortunes {
  148. fortunes := make(Fortunes, 0, 16)
  149. for rows.Next() { // Fetch rows
  150. fortune := Fortune{}
  151. if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
  152. log.Fatalf("Error scanning fortune row: %s", err.Error())
  153. }
  154. fortunes = append(fortunes, &fortune)
  155. }
  156. return fortunes
  157. }
  158. // Test 5: Database updates
  159. func updateHandler(w http.ResponseWriter, r *http.Request) {
  160. n := getQueriesParam(r)
  161. world := make([]World, n)
  162. for i := 0; i < n; i++ {
  163. if err := worldSelectPrepared.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
  164. log.Fatalf("Error scanning world row: %v", err)
  165. }
  166. world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
  167. if _, err := worldUpdatePrepared.Exec(world[i].RandomNumber, world[i].Id); err != nil {
  168. log.Fatalf("Error updating world row: %v", err)
  169. }
  170. }
  171. w.Header().Set("Server", "Go")
  172. w.Header().Set("Content-Type", "application/json")
  173. encoder := json.NewEncoder(w)
  174. encoder.Encode(world)
  175. }
  176. type Fortunes []*Fortune
  177. func (s Fortunes) Len() int { return len(s) }
  178. func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  179. type ByMessage struct{ Fortunes }
  180. func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }