-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #203 from tuomohopia/dev
Added a skeleton benchmark suite + mockup queries
- Loading branch information
Showing
5 changed files
with
324 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE OverloadedLabels #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
-- Don't define 'module Main where' here | ||
-- or otherwise 'stack bench' won't work. | ||
|
||
import Squeal.PostgreSQL hiding ( defaultMain ) | ||
import Criterion.Main | ||
import GHC.Generics | ||
import qualified Generics.SOP as SOP | ||
import Test.QuickCheck | ||
-- Project imports | ||
import Schema | ||
import Queries | ||
|
||
main = defaultMain | ||
[ bgroup | ||
"Render Queries" | ||
[ bench "createUser: weak head normal form" $ whnf renderSQL createUser | ||
, bench "createUser: normal form" $ nf renderSQL createUser | ||
, bench "userDetails: weak head normal form" $ whnf renderSQL userDetails | ||
, bench "userDetails: normal form" $ nf renderSQL userDetails | ||
, bench "insertDeviceDetails: weak head normal form" | ||
$ whnf renderSQL insertDeviceDetails | ||
, bench "insertDeviceDetails: normal form" | ||
$ nf renderSQL insertDeviceDetails | ||
] | ||
] | ||
|
||
{- | ||
To benchmark actual IO actions like supplying parameters to a query, | ||
we would generate samples via QuickCheck like this: | ||
``` | ||
d :: [InsertUser] <- sample' arbitrary | ||
``` | ||
Then start testing those generated values with `manipulateParams`. | ||
-} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Microbenchark suite for Squeal | ||
> Benchmarking & profiling query rendering performance | ||
## Running | ||
|
||
Run benchmark suite with: | ||
``` | ||
stack bench | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE OverloadedLabels #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
{-# LANGUAGE DerivingStrategies #-} | ||
|
||
module Queries where | ||
|
||
import Squeal.PostgreSQL | ||
import GHC.Generics hiding ( from ) | ||
import qualified Generics.SOP as SOP | ||
import Data.Text ( Text ) | ||
import Data.Int ( Int16 | ||
, Int64 | ||
) | ||
import Data.Time ( UTCTime(..) | ||
, fromGregorian | ||
, secondsToDiffTime | ||
) | ||
import Test.QuickCheck ( Arbitrary(..) | ||
, PrintableString(..) | ||
, listOf | ||
, arbitraryPrintableChar | ||
) | ||
import Generic.Random ( genericArbitrarySingle ) | ||
-- Import Orphan instances | ||
import Test.QuickCheck.Instances ( ) | ||
-- Project imports | ||
import Schema | ||
|
||
|
||
-- Types | ||
|
||
type UserId = Int64 | ||
-- Insert user | ||
data InsertUser = InsertUser | ||
{ userEmail :: Text | ||
, userPassword :: Text | ||
, userFirstName :: Maybe Text | ||
, userBirthyear :: Maybe Int16 | ||
, timeNow :: UTCTime | ||
} | ||
deriving (Show, Generic) | ||
instance SOP.Generic InsertUser | ||
instance SOP.HasDatatypeInfo InsertUser | ||
-- Arbitrary instances for producing values with quickcheck | ||
instance Arbitrary InsertUser where | ||
arbitrary = genericArbitrarySingle | ||
|
||
utcTime :: UTCTime | ||
utcTime = UTCTime (fromGregorian 2019 7 4) (secondsToDiffTime 5800) | ||
|
||
sampleInsertUser :: InsertUser | ||
sampleInsertUser = InsertUser { userEmail = "[email protected]" | ||
, userPassword = "MySecretPassword" | ||
, userFirstName = Just "Mark" | ||
, userBirthyear = Just 1980 | ||
, timeNow = utcTime | ||
} | ||
|
||
data APIDBUser_ = APIDBUser_ | ||
{ userId :: UserId | ||
, email :: Text | ||
, first_name :: Maybe Text | ||
, birthyear :: Maybe Int16 | ||
} | ||
deriving (Show, Generic) | ||
instance SOP.Generic APIDBUser_ | ||
instance SOP.HasDatatypeInfo APIDBUser_ | ||
-- Arbitrary instances for producing values with quickcheck | ||
instance Arbitrary APIDBUser_ where | ||
arbitrary = genericArbitrarySingle | ||
|
||
data Row4 a b c d = Row4 | ||
{ col1 :: a | ||
, col2 :: b | ||
, col3 :: c | ||
, col4 :: d | ||
} | ||
deriving stock Generic | ||
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) | ||
|
||
-- (UserId, Token, OS) | ||
type DeviceDetailsRow = Row4 UserId Text (Enumerated DeviceOS) UTCTime | ||
|
||
-- Queries | ||
|
||
createUser :: Manipulation_ Schemas InsertUser APIDBUser_ | ||
createUser = insertInto | ||
#users | ||
(Values_ | ||
( Default | ||
`as` #id | ||
:* Set (param @1) | ||
`as` #email | ||
:* Set (param @2) | ||
`as` #password | ||
:* Set (param @3) | ||
`as` #first_name | ||
:* Set (param @4 & cast int2) | ||
`as` #birthyear | ||
:* Set (param @5) | ||
`as` #inserted_at | ||
:* Set (param @5) | ||
`as` #updated_at | ||
) | ||
) | ||
OnConflictDoRaise | ||
(Returning_ | ||
( #id | ||
`as` #userId | ||
`as` #email | ||
:* #first_name | ||
`as` #first_name | ||
:* #birthyear | ||
`as` #birthyear | ||
) | ||
) | ||
|
||
userDetails :: Query_ Schemas (Only UserId) APIDBUser_ | ||
userDetails = select_ | ||
( #id | ||
`as` #userId | ||
`as` #email | ||
:* #first_name | ||
`as` #first_name | ||
:* #birthyear | ||
`as` #birthyear | ||
) | ||
(from (table #users) & where_ (#id .== (param @1 & cast int8))) | ||
|
||
insertDeviceDetails :: Manipulation_ Schemas DeviceDetailsRow () | ||
insertDeviceDetails = insertInto | ||
#user_devices | ||
(Values_ | ||
( Default | ||
`as` #id | ||
:* Set (param @1) | ||
`as` #user_id | ||
:* Set (param @2) | ||
`as` #token | ||
:* Set (parameter @3 (typedef #device_os)) | ||
`as` #os | ||
:* Set (param @4) | ||
`as` #inserted_at | ||
:* Set (param @4) | ||
`as` #updated_at | ||
) | ||
) | ||
OnConflictDoRaise | ||
(Returning_ Nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE OverloadedLabels #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
|
||
module Schema where | ||
|
||
import Squeal.PostgreSQL | ||
import GHC.Generics | ||
import qualified Generics.SOP as SOP | ||
|
||
|
||
-- Type | ||
|
||
data DeviceOS = Android | IOS | ||
deriving (Show, Read, Eq, Generic) | ||
-- DeviceOS is converted to PG Enum type | ||
instance SOP.Generic DeviceOS | ||
instance SOP.HasDatatypeInfo DeviceOS | ||
|
||
-- Defined extra types for the database | ||
-- Operating system enum | ||
type PGDeviceOS = PG (Enumerated DeviceOS) | ||
type DeviceOSType = 'Typedef PGDeviceOS | ||
|
||
-- SCHEMA | ||
|
||
-- | Helper: Joining timestamps to every table from here | ||
type TimestampColumns = '[ | ||
"inserted_at" ::: 'NoDef :=> 'NotNull 'PGtimestamptz | ||
, "updated_at" ::: 'NoDef :=> 'NotNull 'PGtimestamptz | ||
] | ||
|
||
-- Users | ||
|
||
type UsersColumns = '[ | ||
"id" ::: 'Def :=> 'NotNull 'PGint8 | ||
, "email" ::: 'NoDef :=> 'NotNull 'PGtext | ||
, "password" ::: 'NoDef :=> 'NotNull 'PGtext | ||
, "first_name" ::: 'NoDef :=> 'Null 'PGtext | ||
, "birthyear" ::: 'NoDef :=> 'Null 'PGint2 | ||
] | ||
|
||
type UsersConstraints = '[ | ||
"pk_users" ::: 'PrimaryKey '["id"] | ||
, "email" ::: 'Unique '["email"] | ||
] | ||
|
||
type UsersTable = 'Table (UsersConstraints :=> Join UsersColumns TimestampColumns) | ||
|
||
-- User devices | ||
type UserDevicesColumns = '[ | ||
"id" ::: 'Def :=> 'NotNull 'PGint8 -- ID as PK because user might have many same OS devices | ||
, "user_id" ::: 'NoDef :=> 'NotNull 'PGint8 | ||
, "token" ::: 'NoDef :=> 'NotNull 'PGtext | ||
, "os" ::: 'NoDef :=> 'NotNull PGDeviceOS | ||
] | ||
|
||
type UserDevicesConstraints = '[ | ||
"pk_user_devices" ::: 'PrimaryKey '["id"] | ||
, "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"] | ||
, "token" ::: 'Unique '["token"] | ||
] | ||
|
||
type UserDevicesTable = 'Table (UserDevicesConstraints :=> Join UserDevicesColumns TimestampColumns) | ||
|
||
-- Schema | ||
-- Make sure to put types before tables, otherwise won't compile | ||
type Schema = '[ | ||
-- Enum types: | ||
"device_os" ::: DeviceOSType | ||
-- Composite types: | ||
, "users" ::: UsersTable | ||
, "user_devices" ::: UserDevicesTable | ||
] | ||
|
||
type Schemas = '["public" ::: Schema] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters