Simple questions about the scotty Haskell web framework
Asked Answered
F

2

12

Consider the simplest scotty app:

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty

import Data.Monoid (mconcat)

main = scotty 3000 $ do
    get "/:word" $ do
        beam <- param "word"
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]

I put this code into app.hs and compile it with GHC. I run it with ./app. Simple.

  1. What really happens when people visit the site? It's only one ./app that's running. Does a new thread get created within this same app whenever each user triggers a get "/:word" $ do line? How many such threads can exist? Thousand? Ten thousand?

  2. After running ./app it shows the message Setting phasers to stun... (port 3000) (ctrl-c to quit). But it shows nothing more. It doesn't output the incoming web requests. How can I make it do that? This would be useful for logging purposes.

Flung answered 27/8, 2014 at 15:46 Comment(0)
R
15

Assuming you are using GHC, each request to the scotty server essentially creates a "green thread" that is scheduled by the GHC runtime. You can easily have thousands of those running at a time.

Scotty itself doesn't do any request logging but since it is built on top of WAI, you can use any middleware component that exists for it, for example RequestLogger.

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger

import Data.Monoid (mconcat)

main = scotty 3000 $ do
    middleware logStdoutDev

    get "/:word" $ do
        beam <- param "word"
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
Rainmaker answered 27/8, 2014 at 17:23 Comment(2)
Thanks, this works. If you know how to answer my next question about a scotty app behind nginx, feel free: #25537995Flung
what manages those green threads? what decides when and how many create and kill?Kuhlmann
M
11

1. What really happens when people visit the site? It's only one ./app that's running. Does a new thread get created within this same app whenever each user triggers a get "/:word" $ do line? How many such threads can exist? Thousand? Ten thousand?

Scotty is build around warp, but could use any other library that implements the web application interface (WAI). A new lightweight thread gets created with forkIOUnmasked (hidden in fork in the module Network.Wai.Handler.Warp.Run). You can have many of those:

Concurrency is "lightweight", which means that both thread creation and context switching overheads are extremely low. Scheduling of Haskell threads is done internally in the Haskell runtime system, and doesn't make use of any operating system-supplied thread packages. (source)

Here's a performance comparison between nginx and warp, which also includes information about the general idea behind warp.

2. After running ./app it shows the message Setting phasers to stun... (port 3000) (ctrl-c to quit). But it shows nothing more. It doesn't output the incoming web requests. How can I make it do that? This would be useful for logging purposes.

What's the type of your do block? It should be ScottyM, since scotty :: Port -> ScottyM () -> IO (). If ScottyM is an instance of MonadIO, you can use liftIO together with putStrLn (or any other IO action).

Now, ScottyM is actually a type synonym for ScottyT, which is in fact a instance of MonadIO. Also, the inner monad ActionM is also a type synonym for ActionT, which is also a MonadIO. Therefore, logging is as easy as

main = scotty 3000 $ do
    liftIO $ putStrLn "incoming request"
    get "/:word" $ do
        beam <- param "word"
        liftIO $ print $ mconcat ["get, word = ", beam]
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]

However, keep in mind that logging to a terminal might not be a good idea when you really expect ten thousand requests per second.

Maag answered 27/8, 2014 at 16:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.