app.rkt 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #lang racket/base
  2. (require db
  3. json
  4. racket/fasl
  5. racket/port
  6. racket/serialize
  7. redis
  8. threading
  9. web-server/dispatch
  10. web-server/http
  11. web-server/http/response
  12. xml)
  13. ;; db ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. (define *db*
  15. (virtual-connection
  16. (connection-pool
  17. (lambda ()
  18. (postgresql-connect #:database "hello_world"
  19. #:user "benchmarkdbuser"
  20. #:password "benchmarkdbpass"
  21. #:server "tfb-database")))))
  22. ;; cache ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  23. (define *redis*
  24. (make-redis-pool
  25. #:pool-size 32))
  26. (define (deserialize* bs)
  27. (deserialize (fasl->s-exp bs)))
  28. (define (serialize* v)
  29. (s-exp->fasl (serialize v)))
  30. ;; helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  31. (define (response/bytes bs
  32. #:code [code 200]
  33. #:headers [headers null]
  34. #:mime-type [mime-type #"text/plain"])
  35. (define len:bs (string->bytes/utf-8 (number->string (bytes-length bs))))
  36. (response/output
  37. #:code code
  38. #:mime-type mime-type
  39. #:headers (cons (make-header #"Content-Length" len:bs) headers)
  40. (lambda (out)
  41. (write-bytes bs out))))
  42. (define (response/json e)
  43. (response/bytes
  44. #:mime-type #"application/json; charset=utf-8"
  45. (jsexpr->bytes e)))
  46. (define (response/xexpr xe)
  47. (response/bytes
  48. #:mime-type #"text/html; charset=utf-8"
  49. (call-with-output-bytes
  50. (lambda (out)
  51. (write-bytes #"<!DOCTYPE html>" out)
  52. (write-xexpr xe out)))))
  53. ;; world ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  54. (serializable-struct world (id n)
  55. #:transparent)
  56. (define select-one-world
  57. (virtual-statement "SELECT id, randomnumber FROM world WHERE id = $1"))
  58. (define update-one-world
  59. (virtual-statement "UPDATE world SET randomnumber = $2 WHERE id = $1"))
  60. (define (random-world-id)
  61. (random 1 10001))
  62. (define (random-world-ids n)
  63. (for/list ([_ (in-range n)])
  64. (random-world-id)))
  65. (define (worlds-ref id)
  66. (for/first ([(id n) (in-query *db* select-one-world id)])
  67. (world id n)))
  68. (define (worlds-ref/random n)
  69. (for*/list ([id (in-list (random-world-ids n))]
  70. [(id n) (in-query *db* select-one-world id)])
  71. (world id n)))
  72. (define (worlds-update! rs)
  73. (for ([r (in-list rs)])
  74. (query-exec *db* update-one-world (world-id r) (world-n r))))
  75. (define (world->hash r)
  76. (hash 'id (world-id r)
  77. 'randomNumber (world-n r)))
  78. ;; fortune ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  79. (struct fortune (id message)
  80. #:transparent)
  81. (define select-fortunes
  82. (virtual-statement "SELECT id, message FROM fortune"))
  83. (define (all-fortunes)
  84. (define fortunes
  85. (cons
  86. (fortune 0 "Additional fortune added at request time.")
  87. (for/list ([(id message) (in-query *db* select-fortunes)])
  88. (fortune id message))))
  89. (sort fortunes string<? #:key fortune-message))
  90. (define (fortune->table-row f)
  91. `(tr
  92. (td ,(number->string (fortune-id f)))
  93. (td ,(fortune-message f))))
  94. ;; web ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  95. (define (parse-queries req)
  96. (or
  97. (and~> (request-bindings/raw req)
  98. (bindings-assq #"queries" _)
  99. (binding:form-value)
  100. (bytes->string/utf-8)
  101. (string->number)
  102. (min 500)
  103. (max 1))
  104. 1))
  105. (define-values (dispatch _url)
  106. (dispatch-rules
  107. [("plaintext")
  108. (lambda (_req)
  109. (response/bytes #"Hello, World!"))]
  110. [("json")
  111. (lambda (_req)
  112. (response/json
  113. (hasheq 'message "Hello, World!")))]
  114. [("db")
  115. (lambda (_req)
  116. (define world-id (random-world-id))
  117. (define world (worlds-ref world-id))
  118. (response/json
  119. (world->hash world)))]
  120. [("fortunes")
  121. (lambda (_req)
  122. (response/xexpr
  123. `(html
  124. (head
  125. (title "Fortunes"))
  126. (body
  127. (table
  128. (tr
  129. (th "id")
  130. (th "message"))
  131. ,@(map fortune->table-row (all-fortunes)))))))]
  132. [("queries")
  133. (lambda (req)
  134. (define n (parse-queries req))
  135. (define worlds (worlds-ref/random n))
  136. (response/json
  137. (map world->hash worlds)))]
  138. [("cached")
  139. (let ([local-cache (make-hasheqv)])
  140. (lambda (req)
  141. (define n (parse-queries req))
  142. (define worlds
  143. (hash-ref! local-cache n (lambda ()
  144. (call-with-redis-client *redis*
  145. (lambda (rc)
  146. (define k (format "worlds:~a" n))
  147. (cond
  148. [(redis-bytes-get rc k)
  149. => deserialize*]
  150. [else
  151. (define worlds (worlds-ref/random n))
  152. (begin0 worlds
  153. (redis-bytes-set! rc k (serialize* worlds)))]))))))
  154. (response/json
  155. (map world->hash worlds))))]
  156. [("updates")
  157. (lambda (req)
  158. (define n (parse-queries req))
  159. (define worlds
  160. (for/list ([r (in-list (worlds-ref/random n))]
  161. [n (in-list (random-world-ids n))])
  162. (struct-copy world r [n n])))
  163. (worlds-update! worlds)
  164. (response/json
  165. (map world->hash worlds)))]))
  166. (module+ main
  167. (require racket/async-channel
  168. racket/cmdline
  169. racket/format
  170. web-server/http/response
  171. web-server/safety-limits
  172. web-server/web-server
  173. "unix-socket-tcp-unit.rkt")
  174. (define port
  175. (command-line
  176. #:args (port)
  177. (string->number port)))
  178. (define (app c req)
  179. (output-response c (dispatch req)))
  180. (define ch (make-async-channel))
  181. (define stop
  182. (serve
  183. #:dispatch app
  184. #:listen-ip "127.0.0.1"
  185. #:port port
  186. #:tcp@ (make-unix-socket-tcp@ port (format "~a.sock" port))
  187. #:confirmation-channel ch
  188. #:safety-limits (make-safety-limits
  189. #:max-waiting 4096
  190. #:request-read-timeout 16
  191. #:response-timeout 16
  192. #:response-send-timeout 16)))
  193. (define ready-or-exn (sync ch))
  194. (when (exn:fail? ready-or-exn)
  195. (raise ready-or-exn))
  196. (call-with-output-file (build-path (~a port ".ready"))
  197. (lambda (out)
  198. (displayln "ready" out)))
  199. (with-handlers ([exn:break?
  200. (lambda (_e)
  201. (stop))])
  202. (sync/enable-break never-evt)))