DbHandler.hs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. {-# LANGUAGE DataKinds #-}
  2. {-# LANGUAGE FlexibleContexts #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. {-# LANGUAGE QuasiQuotes #-}
  5. {-# LANGUAGE RebindableSyntax #-}
  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 Control.Monad.IO.Class.Linear (MonadIO)
  16. import qualified Control.Monad.Linear.Builder as Linear
  17. import Data.Aeson (ToJSON(..), encode, object, (.=))
  18. import Data.ByteString.Lazy (toStrict)
  19. import Data.Int (Int32)
  20. import Data.String (fromString)
  21. import Foreign.JNI.Safe (newGlobalRef_)
  22. import qualified Language.Java as NonLinear
  23. import Language.Java.Inline.Safe
  24. import Language.Java.Function (createIntIntToObjFunction)
  25. import Language.Java.Safe (J, JType(..), type (<>))
  26. import Wizzardo.Http.Handler (JHandler, createHandler)
  27. import Prelude (IO, Show, fromInteger, ($))
  28. import Prelude.Linear (Unrestricted(..))
  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 =
  40. let Linear.Builder{..} = Linear.monadBuilder in do
  41. encodeDbResponse <- createIntIntToObjFunction encodeDbResponseAsJSON
  42. Unrestricted jGlobalEncodeDbResponse <- newGlobalRef_ encodeDbResponse
  43. byteBufferProviderThreadLocal <- createThreadLocalByteBufferProvider
  44. Unrestricted jGlobalByteBufferProviderThreadLocal <-
  45. newGlobalRef_ byteBufferProviderThreadLocal
  46. poolRef <- createPgPoolRef
  47. Unrestricted jGlobalPoolRef <- newGlobalRef_ poolRef
  48. createHandler $ \req resp -> Linear.withLinearIO $ do
  49. let uPoolRef = Unrestricted jGlobalPoolRef
  50. uByteBufferProviderThreadLocal = Unrestricted jGlobalByteBufferProviderThreadLocal
  51. uEncodeDbResponse = Unrestricted jGlobalEncodeDbResponse
  52. [java| {
  53. int genWorldId = 1 + ThreadLocalRandom.current().nextInt(10000);
  54. $resp.async();
  55. $uPoolRef.get().preparedQuery("SELECT * FROM World WHERE id=$1", Tuple.of(genWorldId), dbRes -> {
  56. if (dbRes.succeeded()) {
  57. PgIterator resultSet = dbRes.result().iterator();
  58. if (!resultSet.hasNext()) {
  59. $resp.status(Status._404);
  60. } else {
  61. Tuple row = resultSet.next();
  62. $resp.appendHeader(Header.KV_CONTENT_TYPE_APPLICATION_JSON);
  63. $resp.setBody($uEncodeDbResponse.apply(row.getInteger(0), row.getInteger(1)));
  64. }
  65. } else {
  66. dbRes.cause().printStackTrace();
  67. $resp.status(Status._500).body(dbRes.cause().getMessage());
  68. }
  69. // commit async response
  70. ByteBufferProvider bufferProvider = $uByteBufferProviderThreadLocal.get();
  71. HttpConnection connection = $req.connection();
  72. $resp.commit(connection, bufferProvider);
  73. connection.flush(bufferProvider);
  74. $resp.reset();
  75. });
  76. } |]
  77. return (Unrestricted ())
  78. data World = World { worldId :: Int32, worldRandomNumber :: Int32 }
  79. deriving Show
  80. instance ToJSON World where
  81. toJSON w = object ["id" .= worldId w, "randomNumber" .= worldRandomNumber w]
  82. createThreadLocalByteBufferProvider
  83. :: MonadIO m
  84. => m (J ('Class "java.lang.ThreadLocal" <>
  85. '[ 'Iface "com.wizzardo.epoll.ByteBufferProvider"]
  86. )
  87. )
  88. createThreadLocalByteBufferProvider =
  89. [java| new ThreadLocal<ByteBufferProvider>() {
  90. @Override
  91. public ByteBufferProvider initialValue() {
  92. ByteBufferWrapper wrapper = new ByteBufferWrapper(64 * 1024);
  93. return () -> wrapper;
  94. }
  95. } |]
  96. createPgPoolRef
  97. :: MonadIO m
  98. => m (J ('Class "java.lang.ThreadLocal" <> '[ 'Class "io.reactiverse.pgclient.PgPool"]))
  99. createPgPoolRef =
  100. [java|
  101. new ThreadLocal() {
  102. @Override
  103. public PgPool initialValue() {
  104. WizzardoPgPoolOptions options = new WizzardoPgPoolOptions();
  105. options.setDatabase("hello_world");
  106. options.setHost("tfb-database");
  107. options.setPort(5432);
  108. options.setUser("benchmarkdbuser");
  109. options.setPassword("benchmarkdbpass");
  110. options.setCachePreparedStatements(true);
  111. options.setMaxSize(1);
  112. return new WizzardoPgPool(options);
  113. }
  114. }
  115. |]
  116. encodeDbResponseAsJSON
  117. :: Int32 -> Int32 -> IO (NonLinear.J ('Array ('Prim "byte")))
  118. encodeDbResponseAsJSON rowId rowRandomInt =
  119. NonLinear.reflect $ toStrict $ encode $ World rowId rowRandomInt