DbHandler.hs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. {-# LANGUAGE DataKinds #-}
  2. {-# LANGUAGE FlexibleContexts #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. {-# LANGUAGE QualifiedDo #-}
  5. {-# LANGUAGE QuasiQuotes #-}
  6. {-# LANGUAGE RecordWildCards #-}
  7. {-# LANGUAGE ScopedTypeVariables #-}
  8. {-# LANGUAGE TemplateHaskell #-}
  9. {-# LANGUAGE TypeOperators #-}
  10. {-# OPTIONS_GHC -Wno-name-shadowing #-}
  11. -- The code in Java here has been copied from the benchmark wizzardo-http
  12. -- in
  13. -- https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/wizzardo-http
  14. module DbHandler (createDbHandler) where
  15. import qualified Control.Functor.Linear as Linear
  16. import Control.Monad.IO.Class.Linear (MonadIO)
  17. import Data.Aeson (ToJSON(..), encode, object, (.=))
  18. import Data.ByteString.Lazy (toStrict)
  19. import Data.Int (Int32)
  20. import Foreign.JNI.Safe (newGlobalRef_)
  21. import qualified Language.Java as NonLinear
  22. import Language.Java.Inline.Safe
  23. import Language.Java.Function (createIntIntToObjFunction)
  24. import Language.Java.Safe
  25. (J, JType(..), UnsafeUnrestrictedReference(..), type (<>))
  26. import Wizzardo.Http.Handler (JHandler, createHandler)
  27. import Prelude (IO, Show, ($))
  28. import Prelude.Linear (Ur(..))
  29. import qualified System.IO.Linear as Linear
  30. imports "java.util.concurrent.ThreadLocalRandom"
  31. imports "com.wizzardo.epoll.*"
  32. imports "com.wizzardo.http.*"
  33. imports "com.wizzardo.http.framework.*"
  34. imports "com.wizzardo.http.request.*"
  35. imports "com.wizzardo.http.response.*"
  36. imports "io.reactiverse.pgclient.*"
  37. imports "io.reactiverse.pgclient.impl.*"
  38. createDbHandler :: MonadIO m => m JHandler
  39. createDbHandler = Linear.do
  40. encodeDbResponse <- createIntIntToObjFunction encodeDbResponseAsJSON
  41. UnsafeUnrestrictedReference jGlobalEncodeDbResponse <-
  42. newGlobalRef_ encodeDbResponse
  43. byteBufferProviderThreadLocal <- createThreadLocalByteBufferProvider
  44. UnsafeUnrestrictedReference jGlobalByteBufferProviderThreadLocal <-
  45. newGlobalRef_ byteBufferProviderThreadLocal
  46. poolRef <- createPgPoolRef
  47. UnsafeUnrestrictedReference jGlobalPoolRef <- newGlobalRef_ poolRef
  48. createHandler $ \req resp -> Linear.withLinearIO $ Linear.do
  49. let uPoolRef = UnsafeUnrestrictedReference jGlobalPoolRef
  50. uByteBufferProviderThreadLocal =
  51. UnsafeUnrestrictedReference jGlobalByteBufferProviderThreadLocal
  52. uEncodeDbResponse =
  53. UnsafeUnrestrictedReference jGlobalEncodeDbResponse
  54. [java| {
  55. int genWorldId = 1 + ThreadLocalRandom.current().nextInt(10000);
  56. $resp.async();
  57. $uPoolRef.get().preparedQuery("SELECT * FROM World WHERE id=$1", Tuple.of(genWorldId), dbRes -> {
  58. if (dbRes.succeeded()) {
  59. PgIterator resultSet = dbRes.result().iterator();
  60. if (!resultSet.hasNext()) {
  61. $resp.status(Status._404);
  62. } else {
  63. Tuple row = resultSet.next();
  64. $resp.appendHeader(Header.KV_CONTENT_TYPE_APPLICATION_JSON);
  65. $resp.setBody($uEncodeDbResponse.apply(row.getInteger(0), row.getInteger(1)));
  66. }
  67. } else {
  68. dbRes.cause().printStackTrace();
  69. $resp.status(Status._500).body(dbRes.cause().getMessage());
  70. }
  71. // commit async response
  72. ByteBufferProvider bufferProvider = $uByteBufferProviderThreadLocal.get();
  73. HttpConnection connection = $req.connection();
  74. $resp.commit(connection, bufferProvider);
  75. connection.flush(bufferProvider);
  76. $resp.reset();
  77. });
  78. } |]
  79. Linear.return (Ur ())
  80. data World = World { worldId :: Int32, worldRandomNumber :: Int32 }
  81. deriving Show
  82. instance ToJSON World where
  83. toJSON w = object ["id" .= worldId w, "randomNumber" .= worldRandomNumber w]
  84. createThreadLocalByteBufferProvider
  85. :: MonadIO m
  86. => m (J ('Class "java.lang.ThreadLocal" <>
  87. '[ 'Iface "com.wizzardo.epoll.ByteBufferProvider"]
  88. )
  89. )
  90. createThreadLocalByteBufferProvider =
  91. [java| new ThreadLocal<ByteBufferProvider>() {
  92. @Override
  93. public ByteBufferProvider initialValue() {
  94. ByteBufferWrapper wrapper = new ByteBufferWrapper(64 * 1024);
  95. return () -> wrapper;
  96. }
  97. } |]
  98. createPgPoolRef
  99. :: MonadIO m
  100. => m (J ('Class "java.lang.ThreadLocal" <> '[ 'Class "io.reactiverse.pgclient.PgPool"]))
  101. createPgPoolRef =
  102. [java|
  103. new ThreadLocal() {
  104. @Override
  105. public PgPool initialValue() {
  106. WizzardoPgPoolOptions options = new WizzardoPgPoolOptions();
  107. options.setDatabase("hello_world");
  108. options.setHost("tfb-database");
  109. options.setPort(5432);
  110. options.setUser("benchmarkdbuser");
  111. options.setPassword("benchmarkdbpass");
  112. options.setCachePreparedStatements(true);
  113. options.setMaxSize(1);
  114. return new WizzardoPgPool(options);
  115. }
  116. }
  117. |]
  118. encodeDbResponseAsJSON
  119. :: Int32 -> Int32 -> IO (NonLinear.J ('Array ('Prim "byte")))
  120. encodeDbResponseAsJSON rowId rowRandomInt =
  121. NonLinear.reflect $ toStrict $ encode $ World rowId rowRandomInt