Inspired by the blog post Why I use Twain I am trying to make a simple application which uses Twain and PostreSQL.Simple.
The motivation is that I currently have a couple of websites using Spock, and they leak memory (multiple gigabytes per week), as the author of the blog post above also reports.
As you’ll see in a moment, I’m a beginner in combining libraries/Haskell, so instead of converting my Spock based application directly, I started from the Twain demo and tried to incorporate a database connection pool.
I’m trying to avoid too much boilerplate when running the database queries. This is where I got stuck:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Text hiding (foldr, index, head)
import Data.Text.Lazy as TL hiding (foldr, index, head, Text)
import Data.Text.Lazy.Encoding as TL
import Database.PostgreSQL.Simple
import qualified Database.PostgreSQL.Simple as Pg
import qualified Database.PostgreSQL.Simple.FromRow as Pg
import Data.Pool
import Network.Wai.Handler.Warp (run)
import Web.Twain
import Control.Monad.IO.Class (liftIO)
main :: IO ()
main = do
pool <- myPool
putStrLn "mark listening on port 8080 ..."
run 8080 $ foldr ($) (notFound missing) (routes pool)
routes :: Pool Connection -> [Middleware]
routes pool = [ get "/" (liftIO . withResource pool $ index)
, get "/echo/:name" echoName
]
connectionInfo :: ConnectInfo
connectionInfo = defaultConnectInfo { connectHost = "localhost"
, connectPort = 5432
, connectUser = "lantern"
, connectPassword = "xxxxxx"
, connectDatabase = "lantern"
}
-- max idle time (s)
-- subpools |
myPool :: IO (Pool Connection) -- | | max connections
myPool = createPool (connect connectionInfo) close 2 60 20
data Count = Count { countInt :: Int } deriving Show
instance FromRow Count where
fromRow = do
count <- Pg.field
return (Count count)
index :: Connection -> IO (ResponderM a)
index db = do
count <- Pg.query_ db "SELECT COUNT(*) FROM article"
print count
return $ send $ html ("Hello World " <> TL.encodeUtf8 (TL.pack (show (countInt (head count)))))
echoName :: ResponderM a
echoName = do
name <- param "name"
send $ html $ "Hello " <> name
missing :: ResponderM a
missing = send $ html "Not found…"
This compiles and outputs:
mark listening on port 8080 ...
[Count {countInt = 2372}]
when hit http://localhost:8080/ but the browser shows “Nothing&” rather than the expected “Hello World 2372”.
If I move the liftIO . withResource pool
from the routes
function and into the index
function, on the Pg.query_
line, and pass the pool
rather than the db
, I get the page content I want. But having that boilerplate for every query is going to drive me bonkers for the real application.
I tried finding Twain examples for inspiration, but only found tutorials without database usage.
Any tips are welcome!