1+ {-# LANGUAGE MonadComprehensions #-}
12module PostgREST.Admin
23 ( runAdmin
34 ) where
45
56import qualified Data.Aeson as JSON
7+ import Foreign.C.Error (Errno (.. ), eMFILE )
8+ import GHC.IO.Exception
69import qualified Network.HTTP.Types.Status as HTTP
710import qualified Network.Wai as Wai
811import qualified Network.Wai.Handler.Warp as Warp
@@ -13,6 +16,7 @@ import Network.Socket.ByteString
1316
1417import PostgREST.AppState (AppState , getConfig )
1518import PostgREST.Config (AppConfig (.. ))
19+ import PostgREST.Debounce (makeDebouncer )
1620import PostgREST.MediaType (MediaType (.. ), toContentType )
1721import PostgREST.Metrics (metricsToText )
1822import PostgREST.Network (resolveSocketToAddress )
@@ -23,18 +27,46 @@ import qualified PostgREST.AppState as AppState
2327import qualified Network.Socket as NS
2428import Protolude
2529
26- runAdmin :: AppState -> Maybe NS. Socket -> IO (Maybe NS. Socket ) -> Warp. Settings -> IO ()
27- runAdmin appState maybeAdminSocket getSocketREST settings = do
30+ runAdmin :: AppState -> Maybe NS. Socket -> IO (Maybe NS. Socket ) -> Warp. ServerState -> Warp. Settings -> IO ()
31+ runAdmin appState maybeAdminSocket getSocketREST serverState settings = do
2832 conf <- getConfig appState
2933 whenJust maybeAdminSocket $ \ adminSocket -> do
3034 address <- resolveSocketToAddress adminSocket
35+ -- log EMFILE at most once every 10s
36+ logEMFILE <- makeDebouncer $ observer AdminServerAcceptEMFILEFailure *> threadDelay 10_000_000
37+ let
38+ -- Keep accepting on eMFILE
39+ safeAccept sock =
40+ accept sock `catchEMFile`
41+ do
42+ logEMFILE
43+
44+ connCount <- Warp. currentOpenConnections serverState
45+ join $ atomically $ do
46+ if connCount > 0 then do
47+ -- There are open connections
48+ -- wait for closing some and restart accepting
49+ nextConnCount <- Warp. currentOpenConnectionsSTM serverState
50+ check (nextConnCount < connCount)
51+ pure $ safeAccept sock
52+ else
53+ pure $ do
54+ -- Backoff for 1ms so that we don't enter busy accept loop
55+ -- this should let the system recover fd's once the pressure is gone
56+ -- then restart accepting
57+ threadDelay 1_000
58+ safeAccept sock
59+
3160 void . forkIO $ handle (onError adminSocket) $
32- Warp. runSettingsSocket (adminServerSettings conf address) adminSocket adminApp
61+ Warp. runSettingsSocket (adminServerSettings conf address safeAccept ) adminSocket adminApp
3362 where
3463 adminApp = admin appState getSocketREST
3564 observer = AppState. getObserver appState
36- adminServerSettings config addr=
65+ isEMFILE err = Just eMFILE == fmap Errno (ioe_errno err)
66+ catchEMFile action handler = handleJust (\ e -> [ e | isEMFILE e]) (const handler) action
67+ adminServerSettings config addr safeAccept = do
3768 settings
69+ & Warp. setAccept safeAccept
3870 & Warp. setBeforeMainLoop (observer $ AdminStartObs addr)
3971 & maybe identity Warp. setPort (configAdminServerPort config)
4072
0 commit comments