Skip to content

Commit 5796f86

Browse files
mkleczeksteve-chavez
authored andcommitted
fix: ensure Listener connections are released
retryingListen function potentially leaks database connections. This patch ensures the connections are released in case of listen/notify errors. (cherry picked from commit 00c7cb1)
1 parent 101eac1 commit 5796f86

File tree

3 files changed

+33
-16
lines changed

3 files changed

+33
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. From versio
88

99
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
1010

11+
### Fixed
12+
13+
- Ensure Listener connections are released by @mkleczek in #4614
14+
1115
## [14.3] - 2026-01-03
1216

1317
### Fixed

src/PostgREST/Listener.hs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE LambdaCase #-}
12
{-# LANGUAGE MultiWayIf #-}
23
{-# LANGUAGE RecordWildCards #-}
34

@@ -15,6 +16,7 @@ import PostgREST.Version (prettyVersion)
1516
import qualified PostgREST.AppState as AppState
1617
import qualified PostgREST.Config as Config
1718

19+
import Data.Either.Combinators (whenRight)
1820
import Protolude
1921

2022
-- | Starts the Listener in a thread
@@ -46,25 +48,31 @@ retryingListen appState = do
4648

4749
-- forkFinally allows to detect if the thread dies
4850
void . flip forkFinally handleFinally $ do
49-
dbOrError <- SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri)
50-
case dbOrError of
51-
Right db -> do
52-
SQL.listen db $ SQL.toPgIdentifier dbChannel
53-
AppState.putIsListenerOn appState True
51+
-- Make sure we don't leak connections on errors
52+
bracket
53+
-- acquire connection
54+
(SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri))
55+
-- release connection
56+
(`whenRight` releaseConnection) $
57+
-- use connection
58+
\case
59+
Right db -> do
60+
SQL.listen db $ SQL.toPgIdentifier dbChannel
61+
AppState.putIsListenerOn appState True
5462

55-
delay <- AppState.getNextListenerDelay appState
56-
when (delay > 1) $ do -- if we did a retry
57-
-- assume we lost notifications, refresh the schema cache
58-
AppState.schemaCacheLoader appState
59-
-- reset the delay
60-
AppState.putNextListenerDelay appState 1
63+
delay <- AppState.getNextListenerDelay appState
64+
when (delay > 1) $ do -- if we did a retry
65+
-- assume we lost notifications, refresh the schema cache
66+
AppState.schemaCacheLoader appState
67+
-- reset the delay
68+
AppState.putNextListenerDelay appState 1
6169

62-
observer $ DBListenStart dbChannel
63-
SQL.waitForNotifications handleNotification db
70+
observer $ DBListenStart dbChannel
71+
SQL.waitForNotifications handleNotification db
6472

65-
Left err -> do
66-
observer $ DBListenFail dbChannel (Left err)
67-
exitFailure
73+
Left err -> do
74+
observer $ DBListenFail dbChannel (Left err)
75+
exitFailure
6876
where
6977
observer = AppState.getObserver appState
7078
mainThreadId = AppState.getMainThreadId appState
@@ -79,3 +87,5 @@ retryingListen appState = do
7987

8088
cacheReloader =
8189
AppState.schemaCacheLoader appState
90+
91+
releaseConnection = void . forkIO . handle (observer . DBListenerConnectionCleanupFail) . SQL.release

src/PostgREST/Observation.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ data Observation
4848
| DBListenRetry Int
4949
| DBListenerGotSCacheMsg ByteString
5050
| DBListenerGotConfigMsg ByteString
51+
| DBListenerConnectionCleanupFail SomeException
5152
| QueryObs MainQuery Status
5253
| ConfigReadErrorObs SQL.UsageError
5354
| ConfigInvalidObs Text
@@ -118,6 +119,8 @@ observationMessage = \case
118119
"Received a schema cache reload message on the " <> show channel <> " channel"
119120
DBListenerGotConfigMsg channel ->
120121
"Received a config reload message on the " <> show channel <> " channel"
122+
DBListenerConnectionCleanupFail ex ->
123+
"Failed during listener connection cleanup: " <> showOnSingleLine '\t' (show ex)
121124
QueryObs{} ->
122125
mempty -- TODO pending refactor: The logic for printing the query cannot be done here. Join the observationMessage function into observationLogger to avoid this mempty.
123126
ConfigReadErrorObs usageErr ->

0 commit comments

Comments
 (0)