servlet.rkt 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #lang racket/base
  2. (require db
  3. json
  4. threading
  5. web-server/dispatch
  6. web-server/http)
  7. ;; db ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. (define max-db-conns 1024)
  9. (define db-conn-pool
  10. (connection-pool
  11. #:max-connections max-db-conns
  12. #:max-idle-connections max-db-conns
  13. (λ ()
  14. (postgresql-connect #:database "hello_world"
  15. #:user "benchmarkdbuser"
  16. #:password "benchmarkdbpass"
  17. #:server "tfb-database"))))
  18. (define db-conn-pool-sema
  19. (make-semaphore max-db-conns))
  20. (define current-db-conn
  21. (make-parameter #f))
  22. (define (call-with-db-conn f)
  23. (call-with-semaphore db-conn-pool-sema
  24. (lambda ()
  25. (define conn #f)
  26. (dynamic-wind
  27. (lambda ()
  28. (set! conn (connection-pool-lease db-conn-pool)))
  29. (lambda ()
  30. (parameterize ([current-db-conn conn])
  31. (f)))
  32. (lambda ()
  33. (disconnect conn))))))
  34. ;; helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  35. (define (response/json e)
  36. (response/output
  37. #:mime-type #"application/json; charset=utf-8"
  38. (lambda (out)
  39. (write-json e out))))
  40. ;; world ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  41. (struct world (id n)
  42. #:transparent)
  43. (define select-one-world
  44. (virtual-statement
  45. (lambda (_dbsystem)
  46. "SELECT id, randomnumber FROM world WHERE id = $1")))
  47. (define update-one-world
  48. (virtual-statement
  49. (lambda (_dbsystem)
  50. "UPDATE world SET randomnumber = $2 WHERE id = $1")))
  51. (define (random-world-id)
  52. (random 1 10001))
  53. (define (random-world-ids n)
  54. (for/list ([_ (in-range n)])
  55. (random-world-id)))
  56. (define (worlds-ref id)
  57. (for/first ([(id n) (in-query (current-db-conn) select-one-world id)])
  58. (world id n)))
  59. (define (worlds-ref/random n)
  60. (define conn (current-db-conn))
  61. (for*/list ([id (in-list (random-world-ids n))]
  62. [(id n) (in-query conn select-one-world id)])
  63. (world id n)))
  64. (define (worlds-update! rs)
  65. (define conn (current-db-conn))
  66. (for ([r (in-list rs)])
  67. (query-exec conn update-one-world (world-id r) (world-n r))))
  68. (define (world->hash r)
  69. (hash 'id (world-id r)
  70. 'randomNumber (world-n r)))
  71. ;; fortune ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  72. (struct fortune (id message)
  73. #:transparent)
  74. (define select-fortunes
  75. (virtual-statement
  76. (lambda (_dbsystem)
  77. "SELECT id, message FROM fortune")))
  78. (define (all-fortunes)
  79. (define fortunes
  80. (cons
  81. (fortune 0 "Additional fortune added at request time.")
  82. (for/list ([(id message) (in-query (current-db-conn) select-fortunes)])
  83. (fortune id message))))
  84. (sort fortunes string<? #:key fortune-message))
  85. (define (fortune->table-row f)
  86. `(tr
  87. (td ,(number->string (fortune-id f)))
  88. (td ,(fortune-message f))))
  89. ;; web ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  90. (define (parse-queries req)
  91. (or
  92. (and~> (request-bindings/raw req)
  93. (bindings-assq #"queries" _)
  94. (binding:form-value)
  95. (bytes->string/utf-8)
  96. (string->number)
  97. (min 500)
  98. (max 1))
  99. 1))
  100. (define-values (dispatch _url)
  101. (dispatch-rules
  102. [("plaintext")
  103. (lambda (_req)
  104. (response/output
  105. #:mime-type #"text/plain"
  106. (lambda (out)
  107. (display "Hello, World!" out))))]
  108. [("json")
  109. (lambda (_req)
  110. (response/json
  111. (hasheq 'message "Hello, World!")))]
  112. [("db")
  113. (lambda (_req)
  114. (define world-id (random-world-id))
  115. (define world
  116. (call-with-db-conn
  117. (lambda ()
  118. (worlds-ref world-id))))
  119. (response/json
  120. (world->hash world)))]
  121. [("fortunes")
  122. (lambda (_req)
  123. (define fortunes
  124. (call-with-db-conn all-fortunes))
  125. (response/xexpr
  126. #:preamble #"<!DOCTYPE html>"
  127. `(html
  128. (head
  129. (title "Fortunes"))
  130. (body
  131. (table
  132. (tr
  133. (th "id")
  134. (th "message"))
  135. ,@(map fortune->table-row fortunes))))))]
  136. [("queries")
  137. (lambda (req)
  138. (define n (parse-queries req))
  139. (define worlds
  140. (call-with-db-conn
  141. (lambda ()
  142. (worlds-ref/random n))))
  143. (response/json
  144. (map world->hash worlds)))]
  145. [("updates")
  146. (lambda (req)
  147. (define worlds
  148. (call-with-db-conn
  149. (lambda ()
  150. (define n (parse-queries req))
  151. (define worlds (worlds-ref/random n))
  152. (define worlds*
  153. (for/list ([r (in-list worlds)]
  154. [n (in-list (random-world-ids n))])
  155. (struct-copy world r [n n])))
  156. (begin0 worlds*
  157. (worlds-update! worlds*)))))
  158. (response/json
  159. (map world->hash worlds)))]))
  160. (module+ main
  161. (require web-server/servlet-dispatch
  162. web-server/web-server)
  163. (define stop
  164. (serve
  165. #:dispatch (dispatch/servlet dispatch)
  166. #:listen-ip "0.0.0.0"
  167. #:port 8080))
  168. (with-handlers ([exn:break?
  169. (lambda (_e)
  170. (stop))])
  171. (sync/enable-break never-evt)))