server.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "runtime"
  7. "sort"
  8. "github.com/jackc/pgx"
  9. "github.com/valyala/fasthttp"
  10. "common"
  11. "templates"
  12. )
  13. var (
  14. worldSelectStmt *pgx.PreparedStatement
  15. worldUpdateStmt *pgx.PreparedStatement
  16. fortuneSelectStmt *pgx.PreparedStatement
  17. db *pgx.ConnPool
  18. )
  19. func main() {
  20. flag.Parse()
  21. var err error
  22. maxConnectionCount := runtime.NumCPU() * 2
  23. if db, err = initDatabase("localhost", "benchmarkdbuser", "benchmarkdbpass", "hello_world", 5432, maxConnectionCount); err != nil {
  24. log.Fatalf("Error opening database: %s", err)
  25. }
  26. s := &fasthttp.Server{
  27. Handler: mainHandler,
  28. Name: "go",
  29. }
  30. ln := common.GetListener()
  31. if err = s.Serve(ln); err != nil {
  32. log.Fatalf("Error when serving incoming connections: %s", err)
  33. }
  34. }
  35. func mainHandler(ctx *fasthttp.RequestCtx) {
  36. path := ctx.Path()
  37. switch string(path) {
  38. case "/plaintext":
  39. common.PlaintextHandler(ctx)
  40. case "/json":
  41. common.JSONHandler(ctx)
  42. case "/db":
  43. dbHandler(ctx)
  44. case "/queries":
  45. queriesHandler(ctx)
  46. case "/fortune":
  47. fortuneHandler(ctx)
  48. case "/update":
  49. updateHandler(ctx)
  50. default:
  51. ctx.Error("unexpected path", fasthttp.StatusBadRequest)
  52. }
  53. }
  54. func dbHandler(ctx *fasthttp.RequestCtx) {
  55. var w common.World
  56. fetchRandomWorld(&w)
  57. common.JSONMarshal(ctx, &w)
  58. }
  59. func queriesHandler(ctx *fasthttp.RequestCtx) {
  60. n := common.GetQueriesCount(ctx)
  61. worlds := make([]common.World, n)
  62. for i := 0; i < n; i++ {
  63. fetchRandomWorld(&worlds[i])
  64. }
  65. common.JSONMarshal(ctx, worlds)
  66. }
  67. func fortuneHandler(ctx *fasthttp.RequestCtx) {
  68. rows, err := db.Query("fortuneSelectStmt")
  69. if err != nil {
  70. log.Fatalf("Error selecting db data: %v", err)
  71. }
  72. fortunes := make([]templates.Fortune, 0, 16)
  73. for rows.Next() {
  74. var f templates.Fortune
  75. if err := rows.Scan(&f.ID, &f.Message); err != nil {
  76. log.Fatalf("Error scanning fortune row: %s", err)
  77. }
  78. fortunes = append(fortunes, f)
  79. }
  80. rows.Close()
  81. fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."})
  82. sort.Sort(common.FortunesByMessage(fortunes))
  83. ctx.SetContentType("text/html; charset=utf-8")
  84. templates.WriteFortunePage(ctx, fortunes)
  85. }
  86. func updateHandler(ctx *fasthttp.RequestCtx) {
  87. n := common.GetQueriesCount(ctx)
  88. worlds := make([]common.World, n)
  89. for i := 0; i < n; i++ {
  90. w := &worlds[i]
  91. fetchRandomWorld(w)
  92. w.RandomNumber = int32(common.RandomWorldNum())
  93. }
  94. // sorting is required for insert deadlock prevention.
  95. sort.Sort(common.WorldsByID(worlds))
  96. txn, err := db.Begin()
  97. if err != nil {
  98. log.Fatalf("Error starting transaction: %s", err)
  99. }
  100. for i := 0; i < n; i++ {
  101. w := &worlds[i]
  102. if _, err = txn.Exec("worldUpdateStmt", w.RandomNumber, w.Id); err != nil {
  103. log.Fatalf("Error updating world row %d: %s", i, err)
  104. }
  105. }
  106. if err = txn.Commit(); err != nil {
  107. log.Fatalf("Error when commiting world rows: %s", err)
  108. }
  109. common.JSONMarshal(ctx, worlds)
  110. }
  111. func fetchRandomWorld(w *common.World) {
  112. n := common.RandomWorldNum()
  113. if err := db.QueryRow("worldSelectStmt", n).Scan(&w.Id, &w.RandomNumber); err != nil {
  114. log.Fatalf("Error scanning world row: %s", err)
  115. }
  116. }
  117. func mustPrepare(db *pgx.Conn, name, query string) *pgx.PreparedStatement {
  118. stmt, err := db.Prepare(name, query)
  119. if err != nil {
  120. log.Fatalf("Error when preparing statement %q: %s", query, err)
  121. }
  122. return stmt
  123. }
  124. func initDatabase(dbHost string, dbUser string, dbPass string, dbName string, dbPort uint16, maxConnectionsInPool int) (*pgx.ConnPool, error) {
  125. var successOrFailure string = "OK"
  126. var config pgx.ConnPoolConfig
  127. config.Host = dbHost
  128. config.User = dbUser
  129. config.Password = dbPass
  130. config.Database = dbName
  131. config.Port = dbPort
  132. config.MaxConnections = maxConnectionsInPool
  133. config.AfterConnect = func(conn *pgx.Conn) error {
  134. worldSelectStmt = mustPrepare(conn, "worldSelectStmt", "SELECT id, randomNumber FROM World WHERE id = $1")
  135. worldUpdateStmt = mustPrepare(conn, "worldUpdateStmt", "UPDATE World SET randomNumber = $1 WHERE id = $2")
  136. fortuneSelectStmt = mustPrepare(conn, "fortuneSelectStmt", "SELECT id, message FROM Fortune")
  137. return nil
  138. }
  139. fmt.Println("--------------------------------------------------------------------------------------------")
  140. connPool, err := pgx.NewConnPool(config)
  141. if err != nil {
  142. successOrFailure = "FAILED"
  143. log.Println("Connecting to database ", dbName, " as user ", dbUser, " ", successOrFailure, ": \n ", err)
  144. } else {
  145. log.Println("Connecting to database ", dbName, " as user ", dbUser, ": ", successOrFailure)
  146. log.Println("Fetching one record to test if db connection is valid...")
  147. var w common.World
  148. n := common.RandomWorldNum()
  149. if errPing := connPool.QueryRow("worldSelectStmt", n).Scan(&w.Id, &w.RandomNumber); errPing != nil {
  150. log.Fatalf("Error scanning world row: %s", errPing)
  151. }
  152. log.Println("OK")
  153. }
  154. fmt.Println("--------------------------------------------------------------------------------------------")
  155. return connPool, err
  156. }