Disable transactions for read-only requests

This commit is contained in:
Joe Nelson
2014-11-06 14:19:19 -08:00
parent 0e57bee90d
commit f58e3db924
5 changed files with 33 additions and 18 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
name: dbapi name: dbapi
version: 0.2.4.4 version: 0.2.4.5
synopsis: The database is your api synopsis: The database is your api
license: MIT license: MIT
license-file: LICENSE license-file: LICENSE
+3 -3
View File
@@ -4,7 +4,7 @@ import Paths_dbapi (version)
import Dbapi import Dbapi
import Middleware (inTransaction, authenticated, withSavepoint, clientErrors, import Middleware (inTransaction, authenticated, withSavepoint, clientErrors,
redirectInsecure, withDBConnection) redirectInsecure, withDBConnection, Environment(..))
import Network.Wai.Handler.Warp hiding (Connection) import Network.Wai.Handler.Warp hiding (Connection)
import Data.String.Conversions (cs) import Data.String.Conversions (cs)
@@ -54,8 +54,8 @@ main = do
runSettings settings $ (if configSecure conf then redirectInsecure else id) runSettings settings $ (if configSecure conf then redirectInsecure else id)
. gzip def . cors corsPolicy . clientErrors . gzip def . cors corsPolicy . clientErrors
. staticPolicy (only [("favicon.ico", "static/favicon.ico")]) . staticPolicy (only [("favicon.ico", "static/favicon.ico")])
. withDBConnection pool . inTransaction . withDBConnection pool . inTransaction Production
. authenticated (cs $ configAnonRole conf) . withSavepoint $ app . authenticated (cs $ configAnonRole conf) . withSavepoint Production $ app
) )
where where
describe = progDesc "create a REST API to an existing Postgres database" describe = progDesc "create a REST API to an existing Postgres database"
+25 -10
View File
@@ -20,26 +20,41 @@ import Network.HTTP.Types.Header (RequestHeaders, hContentType, hAuthorization,
hLocation) hLocation)
import Network.HTTP.Types.Status (status400, status401, status404, status301) import Network.HTTP.Types.Status (status400, status401, status404, status301)
import Network.Wai (Application, requestHeaders, responseLBS, rawPathInfo, import Network.Wai (Application, requestHeaders, responseLBS, rawPathInfo,
rawQueryString, isSecure) rawQueryString, isSecure, requestMethod, Request)
import Network.URI (URI(..), parseURI) import Network.URI (URI(..), parseURI)
import PgQuery(LoginAttempt(..), signInRole, setRole, resetRole) import PgQuery(LoginAttempt(..), signInRole, setRole, resetRole)
import Codec.Binary.Base64.String (decode) import Codec.Binary.Base64.String (decode)
import Debug.Trace
data Environment = Test | Production deriving (Eq)
withDBConnection :: Pool Connection -> (Connection -> Application) -> Application withDBConnection :: Pool Connection -> (Connection -> Application) -> Application
withDBConnection pool app req respond = withDBConnection pool app req respond =
withResource pool (\c -> app c req respond) withResource pool (\c -> app c req respond)
inTransaction :: (Connection -> Application) -> Connection -> Application inTransaction :: Environment -> (Connection -> Application) ->
inTransaction app conn req respond = Connection -> Application
finally (runRaw conn "begin" >> app conn req respond) (runRaw conn "commit") inTransaction env app conn req respond =
if env == Production && readOnlyRequest req
then
app conn req respond
else
finally (runRaw conn "begin" >> app conn req respond) (runRaw conn "commit")
withSavepoint :: (Connection -> Application) -> Connection -> Application readOnlyRequest :: Request -> Bool
withSavepoint app conn req respond = do readOnlyRequest req = requestMethod req `elem` ["GET", "HEAD", "OPTIONS"]
runRaw conn "savepoint req_sp"
catch (app conn req respond) (\e -> let _ = (e::SomeException) in withSavepoint :: Environment -> (Connection -> Application) ->
runRaw conn "rollback to savepoint req_sp" >> throw e) Connection -> Application
withSavepoint env app conn req respond =
if env == Production && readOnlyRequest req
then app conn req respond
else do
runRaw conn "savepoint req_sp"
catch (app conn req respond) (\e -> let _ = (e::SomeException) in
runRaw conn "rollback to savepoint req_sp" >> throw e)
authenticated :: BS.ByteString -> (Connection -> Application) -> authenticated :: BS.ByteString -> (Connection -> Application) ->
Connection -> Application Connection -> Application
@@ -84,7 +99,7 @@ clientErrors app req respond =
where where
isPgException :: SqlError -> Maybe SqlError isPgException :: SqlError -> Maybe SqlError
isPgException = Just isPgException x = Just (traceShow x x)
redirectInsecure :: Application -> Application redirectInsecure :: Application -> Application
+2 -2
View File
@@ -18,7 +18,7 @@ import Text.Regex.TDFA ((=~))
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import Network.Wai.Middleware.Cors (cors) import Network.Wai.Middleware.Cors (cors)
import Middleware(clientErrors, withSavepoint, authenticated) import Middleware(clientErrors, withSavepoint, authenticated, Environment(..))
import Dbapi (app, corsPolicy, AppConfig(..)) import Dbapi (app, corsPolicy, AppConfig(..))
import PgQuery(addUser) import PgQuery(addUser)
@@ -65,7 +65,7 @@ appWithFixture :: ActionWith Application -> IO ()
appWithFixture action = withDatabaseConnection $ \c -> do appWithFixture action = withDatabaseConnection $ \c -> do
runRaw c "begin;" runRaw c "begin;"
action $ cors corsPolicy . clientErrors $ action $ cors corsPolicy . clientErrors $
(authenticated "dbapi_anonymous" . withSavepoint) app c (authenticated "dbapi_anonymous" . withSavepoint Test) app c
rollback c rollback c
rangeHdrs :: ByteRange -> [Header] rangeHdrs :: ByteRange -> [Header]
+2 -2
View File
@@ -4,7 +4,7 @@ import Test.Hspec
import Database.HDBC (runRaw, quickQuery, fromSql, SqlError) import Database.HDBC (runRaw, quickQuery, fromSql, SqlError)
import SpecHelper (dbWithSchema) import SpecHelper (dbWithSchema)
import Middleware (withSavepoint) import Middleware (withSavepoint, Environment(..))
import PgQuery (insert) import PgQuery (insert)
import Types(SqlRow(..)) import Types(SqlRow(..))
import Control.Exception(catch) import Control.Exception(catch)
@@ -24,7 +24,7 @@ spec = let
describe "withSavepoint" $ describe "withSavepoint" $
it "allows partial rollback of request" $ \c -> do it "allows partial rollback of request" $ \c -> do
let app = withSavepoint dbErrApp c let app = withSavepoint Test dbErrApp c
[[beforeCount]] <- quickQuery c "select count(*) from \"1\".items" [] [[beforeCount]] <- quickQuery c "select count(*) from \"1\".items" []
runRaw c "set role dbapi_anonymous" runRaw c "set role dbapi_anonymous"
_ <- insert "1" "items" (SqlRow []) c _ <- insert "1" "items" (SqlRow []) c