Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple questions about the scotty Haskell web framework

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.

like image 783
stackoverflowuser Avatar asked Aug 27 '14 15:08

stackoverflowuser


2 Answers

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>"]
like image 124
shang Avatar answered Oct 31 '22 07:10

shang


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.

like image 25
Zeta Avatar answered Oct 31 '22 07:10

Zeta