app.rkt 6.4 KB

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