diff --git a/installer.py b/installer.py index 6bc3cba74bc..bbdb6e78d95 100644 --- a/installer.py +++ b/installer.py @@ -54,7 +54,7 @@ def __install_server_software(self): # Java # - self.__run_command("sudo apt-get install openjdk-7-jdk=", True) + self.__run_command("sudo apt-get install openjdk-7-jdk", True) self.__run_command("sudo apt-get remove --purge openjdk-6-jre openjdk-6-jre-headless", True) # @@ -94,6 +94,12 @@ def __install_server_software(self): self.__run_command("sudo cp ../config/php-fpm.conf /usr/local/lib/php-fpm.conf") self.__run_command("rm php-5.4.13.tar.gz") + # + # Haskell + # + + self.__run_command("sudo apt-get install ghc cabal-install", True) + ####################################### # Webservers ####################################### @@ -218,6 +224,12 @@ def __install_server_software(self): ############################## self.__run_command("go/bin/go get github.com/hoisie/web") + ############################## + # Yesod + ############################## + self.__run_command("cabal update") + self.__run_command("cabal install yesod persistent-mysql") + ############################################################## # # System Tools diff --git a/yesod/README.md b/yesod/README.md new file mode 100755 index 00000000000..8b7d40ac185 --- /dev/null +++ b/yesod/README.md @@ -0,0 +1,24 @@ +# Yesod Benchmarking Test + +This is the Yesod portion of a [benchmarking test suite](../) comparing a variety of web development platforms. + +* [Controllers](bench/Application.hs) +* [Model](bench/config/models) + +## Infrastructure Software Versions +The tests were run with: +* GHC 7.4.1 +* Yesod 1.1.9.2 + +## Test URLs +### JSON Encoding Test + +http://localhost:3000/json + +### Data-Store/Database Mapping Test + +http://localhost:3000/db + +### Variable Query Test + +http://localhost:3000/db2/2 \ No newline at end of file diff --git a/yesod/__init__.py b/yesod/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/yesod/bench/.gitignore b/yesod/bench/.gitignore new file mode 100644 index 00000000000..660c9989f9c --- /dev/null +++ b/yesod/bench/.gitignore @@ -0,0 +1,8 @@ +dist* +static/tmp/ +config/client_session_key.aes +*.hi +*.o +*.sqlite3 +.hsenv* +yesod-devel/ diff --git a/yesod/bench/Application.hs b/yesod/bench/Application.hs new file mode 100755 index 00000000000..3c47553a962 --- /dev/null +++ b/yesod/bench/Application.hs @@ -0,0 +1,52 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} +module Application + ( makeApplication + , makeFoundation + ) where + +import Import +import Control.Monad +import System.Random + +import qualified Database.Persist.Store +import Database.Persist.Store (PersistValue (..)) +import Network.HTTP.Conduit (newManager, def) +import Yesod.Default.Config + + +import Settings + +getJsonR :: Handler RepJson +getJsonR = jsonToRepJson $ object ["message" .= ("Hello, World!" :: Text)] + +getDBR :: Handler RepJson +getDBR = do + (i, _) <- liftIO $ randomR (1, 10000) <$> newStdGen + liftIO $ print i + Just o <- runDB $ get $ Key $ PersistInt64 i + jsonToRepJson $ object ["id" .= i, "randomNumber" .= worldRandomNumber o] + +getDB2R :: Int -> Handler RepJson +getDB2R n = do + os <- runDB $ replicateM n $ do + (i, _) <- liftIO $ randomR (1, 10000) <$> newStdGen + Just o <- get $ Key $ PersistInt64 i + return $ object ["id" .= i, "randomNumber" .= worldRandomNumber o] + + jsonToRepJson $ array os + +mkYesodDispatch "App" resourcesApp + +makeApplication :: AppConfig DefaultEnv Extra -> IO Application +makeApplication conf = makeFoundation conf >>= toWaiAppPlain + +makeFoundation :: AppConfig DefaultEnv Extra -> IO App +makeFoundation conf = do + manager <- newManager def + dbconf <- withYamlEnvironment "config/mysql.yml" (appEnv conf) + Database.Persist.Store.loadConfig >>= + Database.Persist.Store.applyEnv + p <- Database.Persist.Store.createPoolConfig (dbconf :: Settings.PersistConfig) + let foundation = App conf p manager dbconf + + return foundation diff --git a/yesod/bench/Foundation.hs b/yesod/bench/Foundation.hs new file mode 100755 index 00000000000..c30e3bcf931 --- /dev/null +++ b/yesod/bench/Foundation.hs @@ -0,0 +1,36 @@ +module Foundation where + +import Prelude +import Yesod +import Yesod.Default.Config +import Network.HTTP.Conduit (Manager) +import qualified Settings +import qualified Database.Persist.Store +import Database.Persist.GenericSql +import Settings (Extra (..)) + +data App = App + { settings :: AppConfig DefaultEnv Extra + , connPool :: Database.Persist.Store.PersistConfigPool Settings.PersistConfig -- ^ Database connection pool. + , httpManager :: Manager + , persistConfig :: Settings.PersistConfig + } + +mkYesodData "App" $(parseRoutesFile "config/routes") + +type Form x = Html -> MForm App App (FormResult x, Widget) + +instance Yesod App where + approot = ApprootMaster $ appRoot . settings + +instance YesodPersist App where + type YesodPersistBackend App = SqlPersist + runDB f = do + master <- getYesod + Database.Persist.Store.runPool + (persistConfig master) + f + (connPool master) + +getExtra :: Handler Extra +getExtra = fmap (appExtra . settings) getYesod diff --git a/yesod/bench/Import.hs b/yesod/bench/Import.hs new file mode 100755 index 00000000000..524cb1a17e5 --- /dev/null +++ b/yesod/bench/Import.hs @@ -0,0 +1,27 @@ +module Import + ( module Import + ) where + +import Prelude as Import hiding (head, init, last, + readFile, tail, writeFile) +import Yesod as Import hiding (Route (..)) + +import Control.Applicative as Import (pure, (<$>), (<*>)) +import Data.Text as Import (Text) + +import Foundation as Import +import Model as Import +import Settings as Import + +#if __GLASGOW_HASKELL__ >= 704 +import Data.Monoid as Import + (Monoid (mappend, mempty, mconcat), + (<>)) +#else +import Data.Monoid as Import + (Monoid (mappend, mempty, mconcat)) + +infixr 5 <> +(<>) :: Monoid m => m -> m -> m +(<>) = mappend +#endif diff --git a/yesod/bench/Model.hs b/yesod/bench/Model.hs new file mode 100755 index 00000000000..d35d5b54950 --- /dev/null +++ b/yesod/bench/Model.hs @@ -0,0 +1,12 @@ +module Model where + +import Prelude +import Yesod +import Database.Persist.Quasi + +-- You can define all of your database entities in the entities file. +-- You can find more information on persistent and how to declare entities +-- at: +-- http://www.yesodweb.com/book/persistent/ +share [mkPersist sqlOnlySettings, mkMigrate "migrateAll"] + $(persistFileWith lowerCaseSettings "config/models") diff --git a/yesod/bench/Settings.hs b/yesod/bench/Settings.hs new file mode 100755 index 00000000000..8e2ea2c960c --- /dev/null +++ b/yesod/bench/Settings.hs @@ -0,0 +1,21 @@ +-- | Settings are centralized, as much as possible, into this file. This +-- includes database connection settings, static file locations, etc. +-- In addition, you can configure a number of different aspects of Yesod +-- by overriding methods in the Yesod typeclass. That instance is +-- declared in the Foundation.hs file. +module Settings where + +import Prelude +import Database.Persist.MySQL (MySQLConf) +import Yesod.Default.Config +import Yesod.Default.Util +import Data.Yaml + +type PersistConfig = MySQLConf + +data Extra = Extra + { + } deriving Show + +parseExtra :: DefaultEnv -> Object -> Parser Extra +parseExtra _ o = return Extra diff --git a/yesod/bench/app/main.hs b/yesod/bench/app/main.hs new file mode 100644 index 00000000000..a059fcb194a --- /dev/null +++ b/yesod/bench/app/main.hs @@ -0,0 +1,8 @@ +import Prelude (IO) +import Yesod.Default.Config (fromArgs) +import Yesod.Default.Main (defaultMain) +import Settings (parseExtra) +import Application (makeApplication) + +main :: IO () +main = defaultMain (fromArgs parseExtra) makeApplication diff --git a/yesod/bench/bench.cabal b/yesod/bench/bench.cabal new file mode 100755 index 00000000000..0dd173ea7ae --- /dev/null +++ b/yesod/bench/bench.cabal @@ -0,0 +1,89 @@ +name: bench +version: 0.0.0 +cabal-version: >= 1.8 +build-type: Simple + +Flag dev + Description: Turn on development settings, like auto-reload templates. + Default: False + +Flag library-only + Description: Build for use with "yesod devel" + Default: False + +library + exposed-modules: Application + Foundation + Import + Model + Settings + + if flag(dev) || flag(library-only) + cpp-options: -DDEVELOPMENT + ghc-options: -Wall -O0 + else + ghc-options: -Wall -O2 + + extensions: TemplateHaskell + QuasiQuotes + OverloadedStrings + NoImplicitPrelude + CPP + MultiParamTypeClasses + TypeFamilies + GADTs + GeneralizedNewtypeDeriving + FlexibleContexts + EmptyDataDecls + NoMonomorphismRestriction + + build-depends: base >= 4 && < 5 + -- , yesod-platform >= 1.1 && < 1.2 + , yesod >= 1.1.5 && < 1.2 + , yesod-core >= 1.1.7 && < 1.2 + , yesod-default >= 1.1 && < 1.2 + , text >= 0.11 && < 0.12 + , persistent >= 1.1 && < 1.2 + , persistent-mysql >= 1.1 && < 1.2 + , persistent-template >= 1.1.1 && < 1.2 + , template-haskell + , monad-control >= 0.3 && < 0.4 + , wai-extra >= 1.3 && < 1.4 + , yaml >= 0.8 && < 0.9 + , http-conduit >= 1.8 && < 1.10 + , directory >= 1.1 && < 1.3 + , warp >= 1.3 && < 1.4 + , data-default + , aeson + , conduit >= 1.0 + , monad-logger >= 0.3 + , fast-logger >= 0.3 + , random >= 1.0 + +executable bench + if flag(library-only) + Buildable: False + + main-is: main.hs + hs-source-dirs: app + build-depends: base + , bench + , yesod-default + + ghc-options: -threaded -O2 -rtsopts + +test-suite test + type: exitcode-stdio-1.0 + main-is: main.hs + hs-source-dirs: tests + ghc-options: -Wall + + build-depends: base + , bench + , yesod-test >= 0.3 && < 0.4 + , yesod-default + , yesod-core + , persistent + , persistent-mysql + , resourcet + , monad-logger diff --git a/yesod/bench/config/models b/yesod/bench/config/models new file mode 100755 index 00000000000..bd36bfd8664 --- /dev/null +++ b/yesod/bench/config/models @@ -0,0 +1,2 @@ +World + randomNumber Int sql=randomNumber diff --git a/yesod/bench/config/mysql.yml b/yesod/bench/config/mysql.yml new file mode 100755 index 00000000000..8b0b7f63c60 --- /dev/null +++ b/yesod/bench/config/mysql.yml @@ -0,0 +1,21 @@ +Default: &defaults + user: benchmarkdbuser + password: benchmarkdbpass + host: 127.0.0.1 + port: 3306 + database: hello_world + poolsize: 10 + +Development: + <<: *defaults + +Testing: + <<: *defaults + +Staging: + poolsize: 100 + <<: *defaults + +Production: + poolsize: 100 + <<: *defaults diff --git a/yesod/bench/config/routes b/yesod/bench/config/routes new file mode 100755 index 00000000000..19a303230e2 --- /dev/null +++ b/yesod/bench/config/routes @@ -0,0 +1,3 @@ +/json JsonR GET +/db DBR GET +/db2/#Int DB2R GET \ No newline at end of file diff --git a/yesod/bench/config/settings.yml b/yesod/bench/config/settings.yml new file mode 100755 index 00000000000..4ce74250065 --- /dev/null +++ b/yesod/bench/config/settings.yml @@ -0,0 +1,18 @@ +Default: &defaults + host: "*4" # any IPv4 host + port: 3000 + approot: "http://localhost:3000" + #analytics: UA-YOURCODE + +Development: + <<: *defaults + +Testing: + <<: *defaults + +Staging: + <<: *defaults + +Production: + #approot: "http://www.example.com" + <<: *defaults diff --git a/yesod/benchmark_config b/yesod/benchmark_config new file mode 100644 index 00000000000..ec8077e670d --- /dev/null +++ b/yesod/benchmark_config @@ -0,0 +1,13 @@ +{ + "framework": "yesod", + "tests": [{ + "default": { + "setup_file": "setup", + "json_url": "/json", + "db_url": "/db", + "query_url": "/db2/", + "port": 3000, + "sort": 37 + } + }] +} diff --git a/yesod/setup.py b/yesod/setup.py new file mode 100755 index 00000000000..208fa1956af --- /dev/null +++ b/yesod/setup.py @@ -0,0 +1,28 @@ + +import subprocess +import sys +import setup_util +import os + +def start(args): + setup_util.replace_text("yesod/bench/config/mysql.yml", "host: .*", "host: " + args.database_host) + + subprocess.check_call("cabal configure", shell=True, cwd="yesod/bench") + subprocess.check_call("cabal build", shell=True, cwd="yesod/bench") + + heap = args.max_threads + subprocess.Popen("dist/build/bench/bench Production +RTS -A"+str(heap)+"m -N" + str(args.max_threads) + " > /dev/null", shell=True, cwd="yesod/bench") + return 0 + +def stop(): + p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE) + out, err = p.communicate() + for line in out.splitlines(): + if 'bench' in line: + try: + pid = int(line.split(None, 2)[1]) + os.kill(pid, 9) + except OSError: + pass + + return 0 \ No newline at end of file