@@ -35,7 +35,8 @@ import Data.Either.Combinators (whenLeft)
3535import qualified Data.Text as T (unpack )
3636import qualified Hasql.Pool as SQL
3737import qualified Hasql.Pool.Config as SQL
38- import qualified Hasql.Session as SQL
38+ import qualified Hasql.Session as SQL hiding (statement )
39+ import qualified Hasql.Transaction as SQL hiding (sql )
3940import qualified Hasql.Transaction.Sessions as SQL
4041import qualified Network.HTTP.Types.Status as HTTP
4142import qualified Network.Socket as NS
@@ -72,9 +73,14 @@ import PostgREST.SchemaCache (SchemaCache (..),
7273import PostgREST.SchemaCache.Identifiers (quoteQi )
7374import PostgREST.Unix (createAndBindDomainSocket )
7475
75- import Data.Streaming.Network (bindPortTCP , bindRandomPortTCP )
76- import Data.String (IsString (.. ))
77- import Protolude
76+ import Control.Monad.Random (MonadRandom (getRandomR ))
77+ import Data.Streaming.Network (bindPortTCP ,
78+ bindRandomPortTCP )
79+ import Data.String (IsString (.. ))
80+ import qualified Hasql.Decoders as HD
81+ import qualified Hasql.Encoders as HE
82+ import qualified Hasql.Statement as SQL
83+ import Protolude
7884
7985data AppState = AppState
8086 -- | Database connection pool
@@ -401,9 +407,16 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
401407 qSchemaCache :: IO (Maybe SchemaCache )
402408 qSchemaCache = do
403409 conf@ AppConfig {.. } <- getConfig appState
410+ withTxLock <- do
411+ -- Allow 10 concurrent schema cache loads, guarded by advisory locks.
412+ -- This is to prevent thundering herd problem on startup or when many PostgREST instances receive "reload schema" notifications at the same time
413+ lockId <- getRandomR (50168275 :: Int64 , 50168275 + 10 )
414+ let stmt = SQL. Statement " SELECT pg_catalog.pg_try_advisory_lock($1)" (HE. param $ HE. nonNullable HE. int8) HD. noResult configDbPreparedStatements
415+ pure $ SQL. statement lockId stmt
416+
404417 (resultTime, result) <-
405418 let transaction = if configDbPreparedStatements then SQL. transaction else SQL. unpreparedTransaction in
406- timeItT $ usePool appState (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
419+ timeItT $ usePool appState (transaction SQL. ReadCommitted SQL. Read $ withTxLock *> querySchemaCache conf)
407420 case result of
408421 Left e -> do
409422 putSCacheStatus appState SCPending
0 commit comments