feat: Add Retry-After hint (#1916)

Add Retry-After header when response status is 503.
Its value is the connection worker delay(seconds) when
it's recovering.

This closes issue #1817.
This commit is contained in:
Gaurav Gautam
2021-08-18 10:14:29 -05:00
committed by GitHub
parent 5874482f03
commit d9f1ae6a46
4 changed files with 23 additions and 4 deletions
+1
View File
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added ### Added
- #1783, Include partitioned tables into the schema cache. Allows embedding, UPSERT, INSERT with Location response, OPTIONS request and OpenAPI support for partitioned tables - @laurenceisla - #1783, Include partitioned tables into the schema cache. Allows embedding, UPSERT, INSERT with Location response, OPTIONS request and OpenAPI support for partitioned tables - @laurenceisla
- #1878, Add Retry-After hint header when in recovery mode - @gautam1168
### Fixed ### Fixed
+9 -3
View File
@@ -152,13 +152,19 @@ postgrest logLev appState connWorker =
runExceptT $ postgrestResponse conf maybeDbStructure jsonDbS pgVer (AppState.getPool appState) time req runExceptT $ postgrestResponse conf maybeDbStructure jsonDbS pgVer (AppState.getPool appState) time req
response <- either Error.errorResponseFor identity <$> eitherResponse response <- either Error.errorResponseFor identity <$> eitherResponse
-- Launch the connWorker when the connection is down. The postgrest -- Launch the connWorker when the connection is down. The postgrest
-- function can respond successfully (with a stale schema cache) before -- function can respond successfully (with a stale schema cache) before
-- the connWorker is done. -- the connWorker is done.
when (Wai.responseStatus response == HTTP.status503) connWorker let isPGAway = Wai.responseStatus response == HTTP.status503
when isPGAway connWorker
resp <- addRetryHint isPGAway appState response
respond resp
respond response addRetryHint :: Bool -> AppState -> Wai.Response -> IO Wai.Response
addRetryHint shouldAdd appState response = do
delay <- AppState.getRetryNextIn appState
let h = ("Retry-After", BS8.pack $ show delay)
return $ Wai.mapResponseHeaders (\hs -> if shouldAdd then h:hs else hs) response
postgrestResponse postgrestResponse
:: AppConfig :: AppConfig
+11
View File
@@ -10,6 +10,7 @@ module PostgREST.AppState
, getPgVersion , getPgVersion
, getPool , getPool
, getTime , getTime
, getRetryNextIn
, init , init
, initWithPool , initWithPool
, logWithZTime , logWithZTime
@@ -18,6 +19,7 @@ module PostgREST.AppState
, putIsWorkerOn , putIsWorkerOn
, putJsonDbS , putJsonDbS
, putPgVersion , putPgVersion
, putRetryNextIn
, releasePool , releasePool
, signalListener , signalListener
, waitListener , waitListener
@@ -60,6 +62,8 @@ data AppState = AppState
, stateGetZTime :: IO ZonedTime , stateGetZTime :: IO ZonedTime
-- | Used for killing the main thread in case a subthread fails -- | Used for killing the main thread in case a subthread fails
, stateMainThreadId :: ThreadId , stateMainThreadId :: ThreadId
-- | Keeps track of when the next retry for connecting to database is scheduled
, stateRetryNextIn :: IORef Int
} }
init :: AppConfig -> IO AppState init :: AppConfig -> IO AppState
@@ -79,6 +83,7 @@ initWithPool newPool conf =
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime } <*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime }
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getZonedTime } <*> mkAutoUpdate defaultUpdateSettings { updateAction = getZonedTime }
<*> myThreadId <*> myThreadId
<*> newIORef 0
initPool :: AppConfig -> IO P.Pool initPool :: AppConfig -> IO P.Pool
initPool AppConfig{..} = initPool AppConfig{..} =
@@ -115,6 +120,12 @@ getIsWorkerOn = readIORef . stateIsWorkerOn
putIsWorkerOn :: AppState -> Bool -> IO () putIsWorkerOn :: AppState -> Bool -> IO ()
putIsWorkerOn = atomicWriteIORef . stateIsWorkerOn putIsWorkerOn = atomicWriteIORef . stateIsWorkerOn
getRetryNextIn :: AppState -> IO Int
getRetryNextIn = readIORef . stateRetryNextIn
putRetryNextIn :: AppState -> Int -> IO ()
putRetryNextIn = atomicWriteIORef . stateRetryNextIn
getConfig :: AppState -> IO AppConfig getConfig :: AppState -> IO AppConfig
getConfig = readIORef . stateConf getConfig = readIORef . stateConf
+1
View File
@@ -144,6 +144,7 @@ connectionStatus appState =
"Attempting to reconnect to the database in " "Attempting to reconnect to the database in "
<> (show delay::Text) <> (show delay::Text)
<> " seconds..." <> " seconds..."
when itShould $ AppState.putRetryNextIn appState delay
return itShould return itShould
-- | Load the DbStructure by using a connection from the pool. -- | Load the DbStructure by using a connection from the pool.