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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user