diff --git a/CHANGELOG.md b/CHANGELOG.md index 70022f903c..7cb65a3c70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ All notable changes to this project will be documented in this file. From versio - Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359 + Now fails at startup. Prior to this, it failed with `PGRST205` on requests related to these schemas. +### Fixed +- Shutdown should wait for in flight requests by @mkleczek in #4702 + ## [14.8] - 2026-04-03 ### Added diff --git a/nix/overlays/haskell-packages.nix b/nix/overlays/haskell-packages.nix index 36feba7640..6801caf072 100644 --- a/nix/overlays/haskell-packages.nix +++ b/nix/overlays/haskell-packages.nix @@ -78,6 +78,53 @@ let hasql-pool = lib.dontCheck prev.hasql-pool_1_0_1; hasql-transaction = lib.dontCheck prev.hasql-transaction_1_1_0_1; postgresql-binary = lib.dontCheck (lib.doJailbreak prev.postgresql-binary_0_13_1_3); + + http2 = + prev.callHackageDirect + { + pkg = "http2"; + ver = "5.4.0"; + sha256 = "sha256-PeEWVd61bQ8G7LvfLeXklzXqNJFaAjE2ecRMWJZESPE="; + } + { }; + + http-semantics = + prev.callHackageDirect + { + pkg = "http-semantics"; + ver = "0.4.0"; + sha256 = "sha256-rh0z51EKvsu5rQd5n2z3fSRjjEObouNZSBPO9NFYOF0="; + } + { }; + + network-run = + prev.callHackageDirect + { + pkg = "network-run"; + ver = "0.5.0"; + sha256 = "sha256-vbXh+CzxDsGApjqHxCYf/ijpZtUCApFbkcF5gyN0THU="; + } + { }; + + time-manager = + prev.callHackageDirect + { + pkg = "time-manager"; + ver = "0.2.4"; + sha256 = "sha256-sAt/331YLQ2IU3z90aKYSq1nxoazv87irsuJp7ZG3pw="; + } + { }; + + warp = + lib.dontCheck (prev.callCabal2nixWithOptions "warp" + (super.fetchFromGitHub { + owner = "mkleczek"; + repo = "wai"; + rev = "7ca66f023ccaf2e3862ad97392f1f11afea3b6ff"; + #sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + sha256 = "sha256-Z/1yikmlDZhjv4LhewjRvW7g5s8KZrHztFRnefEDu7Y="; + }) "--subpath=warp" + { }); }; in { diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 2938e61843..1d770ba6b5 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -22,6 +22,7 @@ import GHC.IO.Exception (IOErrorType (..)) import System.IO.Error (ioeGetErrorType) import Control.Monad.Except (liftEither) +import Control.Monad.Extra (whenJust) import Data.Either.Combinators (mapLeft, whenLeft) import Data.Maybe (fromJust) import Data.String (IsString (..)) @@ -79,8 +80,10 @@ run appState = do AppState.schemaCacheLoader appState -- Loads the initial SchemaCache (mainSocket, adminSocket) <- initSockets conf - - Unix.installSignalHandlers observer (AppState.getMainThreadId appState) (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState) + let closeSockets = do + whenJust adminSocket NS.close + NS.close mainSocket + Unix.installSignalHandlers observer closeSockets (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState) Listener.runListener appState @@ -92,6 +95,10 @@ run appState = do address <- resolveSocketToAddress mainSocket observer $ AppServerAddressObs address + -- Hardcoding maximum graceful shutdown timeout (arbitrary set to 5 seconds) + -- This is unfortunate but necessary becase graceful shutdowns don't work with HTTP keep-alive + -- causing Warp to handle requests on already opened connections even if the listen socket is closed + -- See: https://github.com/yesodweb/wai/issues/853 Warp.runSettingsSocket (serverSettings conf & setOnException onWarpException) mainSocket app where observer = AppState.getObserver appState diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index 2128c6cb96..3b1080404b 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -19,10 +19,9 @@ import System.Directory (removeFile) import System.IO.Error (isDoesNotExistError) -- | Set signal handlers, only for systems with signals -installSignalHandlers :: Observation.ObservationHandler -> ThreadId -> IO () -> IO () -> IO () +installSignalHandlers :: Observation.ObservationHandler -> IO () -> IO () -> IO () -> IO () #ifndef mingw32_HOST_OS -installSignalHandlers observer tid usr1 usr2 = do - let interrupt = throwTo tid UserInterrupt +installSignalHandlers observer interrupt usr1 usr2 = do install Signals.sigINT $ observer (Observation.TerminationUnixSignalObs "SIGINT") >> interrupt install Signals.sigTERM $ observer (Observation.TerminationUnixSignalObs "SIGTERM") >> interrupt install Signals.sigUSR1 usr1 diff --git a/test/io/test_io.py b/test/io/test_io.py index 6dc81845c8..90d51e74dd 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -105,7 +105,6 @@ def sleep(): t.join() -@pytest.mark.xfail(reason="Graceful shutdown is currently failing", strict=True) def test_graceful_shutdown_waits_for_in_flight_request(defaultenv): "SIGTERM should allow in-flight requests to finish before exiting"