r.sendecky
r.sendecky

Reputation: 10383

Haskell: Testing connection availability N times with a delay (scotty to mongodb)

I have a stupid problem with scotty web app and mongodb service starting in the right order. I use systemd to start mongodb first and then the scotty web app. It does not work for some reason. The app errors out with connect: does not exist (Connection refused) from the mongodb driver meaning that the connection is not ready.

So my question. How can I test the connection availability say three times with 0.5s interval and only then error out?

This is the application main function

main :: IO ()
main = do
  pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5
  clearSessions pool
  let r = \x -> runReaderT x pool
  scottyT 3000 r r basal

basal :: ScottyD ()
basal = do
  middleware $ staticPolicy (noDots >-> addBase "static")
  notFound $ runSession
  routes

Although the app service is ordered after mongodb service the connection to mongodb is still unavailable during the app start up. So I get the above mentioned error. This is the systemd service file to avoid questions regarding the correct service ordering.

[Unit]
Description=Basal Web Application
Requires=mongodb.service
After=mongodb.service iptables.service network-online.target

[Service]
User=http
Group=http
WorkingDirectory=/srv/http/basal/
ExecStart=/srv/http/basal/bin/basal
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

I don't know why connection to mongodb is not available given the correct service order. So I want to probe connection availability withing haskell code three times with 0.5s delay and then error out. How can I do it?

Thanks.

Upvotes: 1

Views: 206

Answers (1)

Ganesh Sittampalam
Ganesh Sittampalam

Reputation: 29110

I guess from the functions you're using that you're using something like mongoDB 1.5.0.

Here, connect returns something in the IOE monad, which is an alias for ErrorTIOErrorIO.

So the best approach is to use the retrying mechanisms ErrorT offers. As it's an instance of MonadPlus, we can just use mplus if we don't care about checking for the specific error:

retryConnect :: Int -> Int -> Host -> IOE Pipe
retryConnect retries delayInMicroseconds host
    | retries > 0 =
            connect host `mplus`
                (liftIO (threadDelay delayInMicroseconds) >>
                   retryConnect (retries - 1) delayInMicroseconds host)
    | otherwise = connect host

(threadDelay comes from Control.Concurrent).

Then replace connect with retryConnect 2 500000 and it'll retry twice after the first failure with a 500,000 microsecond gap (i.e. 0.5s).

If you do want to check for a specific error, then use catchError instead and inspect the error to decide whether to swallow it or rethrow it.

Upvotes: 3

Related Questions