Skip to content

Commit 27c16d7

Browse files
committed
add: use SO_REUSEPORT on platform supporting it
1 parent 00b1239 commit 27c16d7

3 files changed

Lines changed: 18 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. From versio
1313
- Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957
1414
+ Removed unnecessary double count when building the `Content-Range`.
1515
- Add config `client_error_verbosity` to customize error verbosity by @taimoorzaeem in #4088, #3980, #3824
16+
- Use SO_REUSEPORT on platforms supporting it by @mkleczek in #4703 #4694
1617

1718
### Changed
1819

src/PostgREST/AppState.hs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ import PostgREST.SchemaCache (SchemaCache (..),
7272
import PostgREST.SchemaCache.Identifiers (quoteQi)
7373
import PostgREST.Unix (createAndBindDomainSocket)
7474

75-
import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP)
75+
import Data.Streaming.Network (HostPreference, bindPortGenEx,
76+
bindPortTCP, bindRandomPortTCP)
7677
import Data.String (IsString (..))
7778
import Protolude
7879

@@ -184,7 +185,7 @@ initSockets AppConfig{..} = do
184185
(_, sock) <-
185186
if cfg'port /= 0
186187
then do
187-
sock <- bindPortTCP cfg'port (fromString $ T.unpack cfg'host)
188+
sock <- bindPortTCPWithReusePort cfg'port (fromString $ T.unpack cfg'host)
188189
pure (cfg'port, sock)
189190
else do
190191
-- explicitly bind to a random port, returning bound port number
@@ -194,12 +195,25 @@ initSockets AppConfig{..} = do
194195

195196
adminSock <- case cfg'adminPort of
196197
Just adminPort -> do
197-
adminSock <- bindPortTCP adminPort (fromString $ T.unpack cfg'adminHost)
198+
adminSock <- bindPortTCPWithReusePort adminPort (fromString $ T.unpack cfg'adminHost)
198199
pure $ Just adminSock
199200
Nothing -> pure Nothing
200201

201202
pure (sock, adminSock)
202203

204+
bindPortTCPWithReusePort :: Int -> HostPreference -> IO NS.Socket
205+
bindPortTCPWithReusePort port hostPreference
206+
= do
207+
-- Some unix variants can expose ReusePort but reject it at runtime.
208+
-- Fall back to the default behavior when that happens.
209+
socketWithReusePort <- try (bindPortGenEx reusePortOpts NS.Stream port hostPreference) :: IO (Either SomeException NS.Socket)
210+
either (const $ bindPortTCP port hostPreference) listenSocket socketWithReusePort
211+
where
212+
reusePortOpts = [(NS.ReusePort, 1)]
213+
listenSocket sock = do
214+
NS.listen sock (max 2048 NS.maxListenQueue)
215+
pure sock
216+
203217
initPool :: AppConfig -> ObservationHandler -> IO SQL.Pool
204218
initPool AppConfig{..} observer = do
205219
SQL.acquire $ SQL.settings

test/io/test_io.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ def test_random_port_bound(defaultenv):
133133
assert True # liveness check is done by run(), so we just need to check that it doesn't fail
134134

135135

136-
@pytest.mark.xfail(reason="SO_REUSEPORT handover is currently failing")
137136
def test_so_reuseport_zero_downtime_handover(defaultenv):
138137
"A second PostgREST instance should take over on the same main/admin ports without request failures."
139138

0 commit comments

Comments
 (0)