Skip to content

Commit

Permalink
scratch
Browse files Browse the repository at this point in the history
  • Loading branch information
develop7 committed Jan 3, 2024
1 parent b2095a8 commit 8c0e16a
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 34 deletions.
1 change: 1 addition & 0 deletions postgrest.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ library
, hasql-transaction >= 1.0.1 && < 1.1
, heredoc >= 0.2 && < 0.3
, http-types >= 0.12.2 && < 0.13
, hs-opentelemetry-sdk >= 0.0.3.6 && < 0.0.4
, insert-ordered-containers >= 0.2.2 && < 0.3
, interpolatedstring-perl6 >= 1 && < 1.1
, jose >= 0.8.5.1 && < 0.11
Expand Down
94 changes: 61 additions & 33 deletions src/PostgREST/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import PostgREST.AppState (AppState)
import PostgREST.Auth (AuthResult (..))
import PostgREST.Config (AppConfig (..))
import PostgREST.Config.PgVersion (PgVersion (..))
import PostgREST.Error (Error)
import PostgREST.Error (Error (..))
import PostgREST.Query (DbHandler)
import PostgREST.Response.Performance (ServerTiming (..),
serverTimingHeader)
Expand All @@ -63,6 +63,7 @@ import qualified Network.HTTP.Types as HTTP
import qualified Network.Socket as NS
import Protolude hiding (Handler)
import System.TimeIt (timeItT)
import OpenTelemetry.Trace (getGlobalTracerProvider, defaultSpanArguments, makeTracer, InstrumentationLibrary (..), TracerOptions (..), Tracer, SpanKind (..), SpanArguments(..), inSpan', addEvent, NewEvent (NewEvent, newEventAttributes, newEventTimestamp, newEventName), setStatus, SpanStatus(..), Span, ToAttribute (toAttribute))

type Handler = ExceptT Error

Expand Down Expand Up @@ -134,6 +135,7 @@ postgrestResponse
-> Wai.Request
-> Handler IO Wai.Response
postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@AuthResult{..} req = do
t <- liftIO getTracer
sCache <-
case maybeSchemaCache of
Just sCache ->
Expand All @@ -144,12 +146,20 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@
body <- lift $ Wai.strictRequestBody req

(parseTime, apiRequest) <-
calcTiming configServerTimingEnabled $
calcTiming' configServerTimingEnabled $
liftEither . mapLeft Error.ApiRequestError $
ApiRequest.userApiRequest conf req body sCache

let jwtTime = if configServerTimingEnabled then Auth.getJwtDur req else Nothing
handleRequest authResult conf appState (Just authRole /= configDbAnonRole) configDbPreparedStatements pgVer apiRequest sCache jwtTime parseTime
liftIO $ inSpan' t "handleRequest" defaultSpanArguments {kind = Server} $ \span -> do
result <- runExceptT $ handleRequest span authResult conf appState (Just authRole /= configDbAnonRole) configDbPreparedStatements pgVer apiRequest sCache jwtTime parseTime
case result of
Left err -> do
setStatus span (Error "Boo!")
inSpan' t "errorResponseFor" defaultSpanArguments {kind = Server} $ \span' -> do
_ <- addEvent span' $ NewEvent { newEventName = "error", newEventAttributes = HM.fromList [], newEventTimestamp = Nothing }
return $ Error.errorResponseFor err
Right resp -> return resp

runDbHandler :: AppState.AppState -> AppConfig -> SQL.IsolationLevel -> SQL.Mode -> Bool -> Bool -> DbHandler b -> Handler IO b
runDbHandler appState config isoLvl mode authenticated prepared handler = do
Expand All @@ -163,62 +173,62 @@ runDbHandler appState config isoLvl mode authenticated prepared handler = do

liftEither resp

handleRequest :: AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> PgVersion -> ApiRequest -> SchemaCache -> Maybe Double -> Maybe Double -> Handler IO Wai.Response
handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@ApiRequest{..} sCache jwtTime parseTime =
handleRequest :: Span -> AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> PgVersion -> ApiRequest -> SchemaCache -> Maybe Double -> Maybe Double -> Handler IO Wai.Response
handleRequest span AuthResult{..} conf appState authenticated prepared pgVer apiReq@ApiRequest{..} sCache jwtTime parseTime =
case (iAction, iTarget) of
(ActionRead headersOnly, TargetIdent identifier) -> do
(planTime', wrPlan) <- withTiming $ liftEither $ Plan.wrappedReadPlan identifier conf sCache apiReq
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.wrTxMode wrPlan) $ Query.readQuery wrPlan conf apiReq
(respTime', pgrst) <- withTiming $ liftEither $ Response.readResponse wrPlan headersOnly identifier apiReq resultSet
(planTime', wrPlan) <- withTiming "queryTime" $ liftEither $ Plan.wrappedReadPlan identifier conf sCache apiReq
(txTime', resultSet) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.wrTxMode wrPlan) $ Query.readQuery wrPlan conf apiReq
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.readResponse wrPlan headersOnly identifier apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionMutate MutationCreate, TargetIdent identifier) -> do
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationCreate apiReq identifier conf sCache
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.createQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming $ liftEither $ Response.createResponse identifier mrPlan apiReq resultSet
(planTime', mrPlan) <- withTiming "queryTime" $ liftEither $ Plan.mutateReadPlan MutationCreate apiReq identifier conf sCache
(txTime', resultSet) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.createQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.createResponse identifier mrPlan apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionMutate MutationUpdate, TargetIdent identifier) -> do
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationUpdate apiReq identifier conf sCache
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.updateQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming $ liftEither $ Response.updateResponse mrPlan apiReq resultSet
(planTime', mrPlan) <- withTiming "queryTime" $ liftEither $ Plan.mutateReadPlan MutationUpdate apiReq identifier conf sCache
(txTime', resultSet) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.updateQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.updateResponse mrPlan apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionMutate MutationSingleUpsert, TargetIdent identifier) -> do
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationSingleUpsert apiReq identifier conf sCache
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.singleUpsertQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming $ liftEither $ Response.singleUpsertResponse mrPlan apiReq resultSet
(planTime', mrPlan) <- withTiming "queryTime" $ liftEither $ Plan.mutateReadPlan MutationSingleUpsert apiReq identifier conf sCache
(txTime', resultSet) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.singleUpsertQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.singleUpsertResponse mrPlan apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionMutate MutationDelete, TargetIdent identifier) -> do
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationDelete apiReq identifier conf sCache
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.deleteQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming $ liftEither $ Response.deleteResponse mrPlan apiReq resultSet
(planTime', mrPlan) <- withTiming "queryTime" $ liftEither $ Plan.mutateReadPlan MutationDelete apiReq identifier conf sCache
(txTime', resultSet) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.mrTxMode mrPlan) $ Query.deleteQuery mrPlan apiReq conf
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.deleteResponse mrPlan apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionInvoke invMethod, TargetProc identifier _) -> do
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq invMethod
(txTime', resultSet) <- withTiming $ runQuery (fromMaybe roleIsoLvl $ pdIsoLvl (Plan.crProc cPlan)) (pdTimeout $ Plan.crProc cPlan) (Plan.crTxMode cPlan) $ Query.invokeQuery (Plan.crProc cPlan) cPlan apiReq conf pgVer
(respTime', pgrst) <- withTiming $ liftEither $ Response.invokeResponse cPlan invMethod (Plan.crProc cPlan) apiReq resultSet
(planTime', cPlan) <- withTiming "queryTime" $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq invMethod
(txTime', resultSet) <- withTiming "planTime" $ runQuery (fromMaybe roleIsoLvl $ pdIsoLvl (Plan.crProc cPlan)) (pdTimeout $ Plan.crProc cPlan) (Plan.crTxMode cPlan) $ Query.invokeQuery (Plan.crProc cPlan) cPlan apiReq conf pgVer
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.invokeResponse cPlan invMethod (Plan.crProc cPlan) apiReq resultSet
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionInspect headersOnly, TargetDefaultSpec tSchema) -> do
(planTime', iPlan) <- withTiming $ liftEither $ Plan.inspectPlan apiReq
(txTime', oaiResult) <- withTiming $ runQuery roleIsoLvl Nothing (Plan.ipTxmode iPlan) $ Query.openApiQuery sCache pgVer conf tSchema
(respTime', pgrst) <- withTiming $ liftEither $ Response.openApiResponse (T.decodeUtf8 prettyVersion, docsVersion) headersOnly oaiResult conf sCache iSchema iNegotiatedByProfile
(planTime', iPlan) <- withTiming "queryTime" $ liftEither $ Plan.inspectPlan apiReq
(txTime', oaiResult) <- withTiming "planTime" $ runQuery roleIsoLvl Nothing (Plan.ipTxmode iPlan) $ Query.openApiQuery sCache pgVer conf tSchema
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.openApiResponse (T.decodeUtf8 prettyVersion, docsVersion) headersOnly oaiResult conf sCache iSchema iNegotiatedByProfile
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst

(ActionInfo, TargetIdent identifier) -> do
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoIdentResponse identifier sCache
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.infoIdentResponse identifier sCache
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst

(ActionInfo, TargetProc identifier _) -> do
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq ApiRequest.InvHead
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoProcResponse (Plan.crProc cPlan)
(planTime', cPlan) <- withTiming "planTime" $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq ApiRequest.InvHead
(respTime', pgrst) <- withTiming "responseTime" $ liftEither $ Response.infoProcResponse (Plan.crProc cPlan)
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' Nothing respTime') pgrst

(ActionInfo, TargetDefaultSpec _) -> do
(respTime', pgrst) <- withTiming $ liftEither Response.infoRootResponse
(respTime', pgrst) <- withTiming "responseTime" $ liftEither Response.infoRootResponse
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst

_ ->
Expand All @@ -237,10 +247,20 @@ handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@A
pgrstResponse :: ServerTiming -> Response.PgrstResponse -> Wai.Response
pgrstResponse timing (Response.PgrstResponse st hdrs bod) = Wai.responseLBS st (hdrs ++ ([serverTimingHeader timing | configServerTimingEnabled conf])) bod

withTiming = calcTiming $ configServerTimingEnabled conf
withTiming label = calcTiming span label $ configServerTimingEnabled conf

calcTiming :: Bool -> Handler IO a -> Handler IO (Maybe Double, a)
calcTiming timingEnabled f = if timingEnabled
calcTiming :: Span -> Text -> Bool -> Handler IO a -> Handler IO (Maybe Double, a)
calcTiming span label timingEnabled f = do
(t, r) <- timeItT f
_ <- addEvent span $ NewEvent { newEventName = label, newEventAttributes = HM.fromList [("time", toAttribute t)], newEventTimestamp = Nothing }
if timingEnabled
then do
pure (Just t, r)
else do
pure (Nothing, r)

calcTiming' :: Bool -> Handler IO a -> Handler IO (Maybe Double, a)
calcTiming' timingEnabled f = if timingEnabled
then do
(t, r) <- timeItT f
pure (Just t, r)
Expand All @@ -263,3 +283,11 @@ addRetryHint delay response = do

isServiceUnavailable :: Wai.Response -> Bool
isServiceUnavailable response = Wai.responseStatus response == HTTP.status503

getTracer :: IO Tracer
getTracer = do
tp <- getGlobalTracerProvider
return $
makeTracer tp
InstrumentationLibrary { libraryVersion = decodeUtf8 prettyVersion, libraryName = "postgrest"}
TracerOptions {tracerSchema=Nothing}
9 changes: 8 additions & 1 deletion src/PostgREST/CLI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import qualified PostgREST.Config as Config

import Protolude hiding (hPutStrLn)

import OpenTelemetry.Trace

main :: CLI -> IO ()
main CLI{cliCommand, cliPath} = do
main CLI{cliCommand, cliPath} = withTracer $ \_tracer -> do
conf@AppConfig{..} <-
either panic identity <$> Config.readAppConfig mempty cliPath Nothing mempty mempty

Expand All @@ -46,6 +47,12 @@ main CLI{cliCommand, cliPath} = do
putStr . Config.toText =<< AppState.getConfig appState
CmdDumpSchema -> putStrLn =<< dumpSchema appState
CmdRun -> App.run appState)
where
withTracer :: ((TracerOptions -> Tracer) -> IO c) -> IO c
withTracer f = bracket
initializeGlobalTracerProvider
shutdownTracerProvider
(\tracerProvider -> f $ makeTracer tracerProvider "PostgREST")

-- | Dump SchemaCache schema to JSON
dumpSchema :: AppState -> IO LBS.ByteString
Expand Down
17 changes: 17 additions & 0 deletions stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@ extra-deps:
commit: 890a0a16cf57dd401420fdc6c7d576fb696003bc
- hasql-notifications-0.2.0.6
- hasql-pool-0.10
# - hs-opentelemetry-sdk-0.0.3.6
# - hs-opentelemetry-api-0.1.0.0@sha256:8af01d0c81dd1af6d3293b105178fd9bfa0057c9eb88ac24d3c440bff660abe3,3705
# - hs-opentelemetry-exporter-otlp-0.0.1.5@sha256:89b0a6481096a338fa6383fbdf08ccaa0eb7bb009c4cbb340894eac33e55c5de,2214
# - hs-opentelemetry-propagator-b3-0.0.1.1@sha256:f0e9da77a888b89f81e5f5186788d5ace2a665e8914f6b446712a1c2edf17743,1854
# - hs-opentelemetry-propagator-w3c-0.0.1.3@sha256:5dc2dbdd6b0a4e434ca5fd949e9ebe5611a5d513ef58009b935e9e810cc85d1b,1852
# - hs-opentelemetry-otlp-0.0.1.0@sha256:88bb6b68f172a336f78018b0823f47363fb7408eb19f7301489f81ad4d5c0f33,2307
- github: 'iand675/hs-opentelemetry'
commit: '54251bd15565e3a07cda0cecfa544d209ae63df6'
subdirs:
- sdk
- api
- 'exporters/otlp'
- 'propagators/b3'
- 'propagators/w3c'
- otlp
- thread-utils-context-0.3.0.4@sha256:e763da1c6cab3b6d378fb670ca74aa9bf03c9b61b6fcf7628c56363fb0e3e71e,1671
- thread-utils-finalizers-0.1.1.0@sha256:24944b71d9f1d01695a5908b4a3b44838fab870883114a323336d537995e0a5b,1381
92 changes: 92 additions & 0 deletions stack.yaml.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,98 @@ packages:
size: 346
original:
hackage: hasql-pool-0.10
- completed:
name: hs-opentelemetry-sdk
pantry-tree:
sha256: 42a125d2c00a00055157f8aee729ad5de147170673dea87ae7287e62297ec167
size: 1532
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: sdk
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.0.3.6
original:
subdir: sdk
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
name: hs-opentelemetry-api
pantry-tree:
sha256: 0d0c5c22b3a4d4162e8ced0b0df66def2ddc692cc10eddb80ae6bd0edfc37552
size: 3323
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: api
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.1.0.0
original:
subdir: api
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
name: hs-opentelemetry-exporter-otlp
pantry-tree:
sha256: b3698194a249ca7156da48ae9e518baa4173dbfb1c1ae07eb144183a308fc095
size: 502
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: exporters/otlp
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.0.1.5
original:
subdir: exporters/otlp
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
name: hs-opentelemetry-propagator-b3
pantry-tree:
sha256: 5fe5479ce227168904921716f468fb7ba76f43a370c6de52e72eff42bc101967
size: 584
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: propagators/b3
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.0.1.1
original:
subdir: propagators/b3
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
name: hs-opentelemetry-propagator-w3c
pantry-tree:
sha256: 5e01af30d8c1702608dcfc4b8c3a56549a2e33359b3fa9ea3fd1abc63215ced5
size: 598
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: propagators/w3c
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.0.1.3
original:
subdir: propagators/w3c
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
name: hs-opentelemetry-otlp
pantry-tree:
sha256: 66d9865e4b811a5d60f6ff07f8407d9f72ea4796c03e6c20a4fcca49018b60bb
size: 2355
sha256: eba9c66b5e90e4b4f4a90119053a75b68c0901454a992b463f17192600d034a9
size: 357729
subdir: otlp
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
version: 0.0.1.0
original:
subdir: otlp
url: https://github.com/iand675/hs-opentelemetry/archive/54251bd15565e3a07cda0cecfa544d209ae63df6.tar.gz
- completed:
hackage: thread-utils-context-0.3.0.4@sha256:e763da1c6cab3b6d378fb670ca74aa9bf03c9b61b6fcf7628c56363fb0e3e71e,1671
pantry-tree:
sha256: 57d909a991b5e0b4c7a28121cb52ee9c2db6c09e0419b89af6c82fae52be88d4
size: 397
original:
hackage: thread-utils-context-0.3.0.4@sha256:e763da1c6cab3b6d378fb670ca74aa9bf03c9b61b6fcf7628c56363fb0e3e71e,1671
- completed:
hackage: thread-utils-finalizers-0.1.1.0@sha256:24944b71d9f1d01695a5908b4a3b44838fab870883114a323336d537995e0a5b,1381
pantry-tree:
sha256: 8c2c2e2e22c20bf3696ee6f30b50b3a9eeae187a22beb536441eefb0a3f9c549
size: 400
original:
hackage: thread-utils-finalizers-0.1.1.0@sha256:24944b71d9f1d01695a5908b4a3b44838fab870883114a323336d537995e0a5b,1381
snapshots:
- completed:
sha256: 4905c93319aa94aa53da8f41d614d7bacdbfe6c63a8c6132d32e6e62f24a9af4
Expand Down

0 comments on commit 8c0e16a

Please sign in to comment.