Skip to content

Commit

Permalink
Merge branch 'main' into fix/limit-offset
Browse files Browse the repository at this point in the history
  • Loading branch information
taimoorzaeem authored Aug 1, 2024
2 parents bb9069d + 4653787 commit df5a1f7
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #3558, Add the `admin-server-host` config to set the host for the admin server - @develop7
- #3607, Log to stderr when the JWT secret is less than 32 characters long - @laurenceisla
- #2858, Performance improvements when calling RPCs via GET using indexes in more cases - @wolfgangwalther
- #3560, Log resolved host in "Listening on ..." messages - @develop7

### Fixed

- #3091, Broken link in OpenAPI description `externalDocs` - @salim-b
- #3659, Embed One-to-One relationship with different column order properly - @wolfgangwalther
- #3504, Remove `format` from `rowFilter` parameters in OpenAPI - @dantheman2865
- #3660, Fix regression that loaded the schema cache before the in-database configuration - @steve-chavez, @laurenceisla

### Changed

Expand Down
2 changes: 2 additions & 0 deletions postgrest.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ library
PostgREST.Logger
PostgREST.MediaType
PostgREST.Metrics
PostgREST.Network
PostgREST.Observation
PostgREST.Query
PostgREST.Query.QueryBuilder
Expand Down Expand Up @@ -115,6 +116,7 @@ library
, heredoc >= 0.2 && < 0.3
, http-types >= 0.12.2 && < 0.13
, insert-ordered-containers >= 0.2.2 && < 0.3
, iproute >= 1.7.0 && < 1.8
, jose-jwt >= 0.9.6 && < 0.11
, lens >= 4.14 && < 5.3
, lens-aeson >= 1.0.1 && < 1.3
Expand Down
4 changes: 3 additions & 1 deletion src/PostgREST/Admin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Network.Socket.ByteString
import PostgREST.AppState (AppState)
import PostgREST.Config (AppConfig (..))
import PostgREST.Metrics (metricsToText)
import PostgREST.Network (resolveHost)
import PostgREST.Observation (Observation (..))

import qualified PostgREST.AppState as AppState
Expand All @@ -31,7 +32,8 @@ runAdmin :: AppState -> Warp.Settings -> IO ()
runAdmin appState settings = do
AppConfig{configAdminServerPort} <- AppState.getConfig appState
whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do
observer $ AdminStartObs configAdminServerPort
host <- resolveHost adminSocket
observer $ AdminStartObs host configAdminServerPort
void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp
where
adminApp = admin appState
Expand Down
4 changes: 3 additions & 1 deletion src/PostgREST/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import PostgREST.Auth (AuthResult (..))
import PostgREST.Config (AppConfig (..), LogLevel (..))
import PostgREST.Config.PgVersion (PgVersion (..))
import PostgREST.Error (Error)
import PostgREST.Network (resolveHost)
import PostgREST.Observation (Observation (..))
import PostgREST.Response.Performance (ServerTiming (..),
serverTimingHeader)
Expand Down Expand Up @@ -82,7 +83,8 @@ run appState = do
observer $ AppServerUnixObs path
Nothing -> do
port <- NS.socketPort $ AppState.getSocketREST appState
observer $ AppServerPortObs port
host <- resolveHost $ AppState.getSocketREST appState
observer $ AppServerPortObs (fromJust host) port

Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app

Expand Down
22 changes: 11 additions & 11 deletions src/PostgREST/AppState.hs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
deb <-
let decisecond = 100000 in
mkDebounce defaultDebounceSettings
{ debounceAction = internalSchemaCacheLoad appState
{ debounceAction = retryingSchemaCacheLoad appState
, debounceFreq = decisecond
, debounceEdge = leadingEdge -- runs the worker at the start and the end
}
Expand Down Expand Up @@ -355,30 +355,24 @@ putSCacheStatus = atomicWriteIORef . stateSCacheStatus
getObserver :: AppState -> ObservationHandler
getObserver = stateObserver

internalSchemaCacheLoad :: AppState -> IO ()
internalSchemaCacheLoad appState = do
AppConfig{..} <- getConfig appState
void $ retryingSchemaCacheLoad appState
-- We cannot retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
when configDbConfig $ readInDbConfig False appState

-- | Try to load the schema cache and retry if it fails.
--
-- This is done by repeatedly: 1) flushing the pool, 2) querying the version and validating that the postgres version is supported by us, and 3) loading the schema cache.
-- It's necessary to flush the pool:
--
-- + Because connections cache the pg catalog(see #2620)
-- + For rapid recovery. Otherwise, the pool idle or lifetime timeout would have to be reached for new healthy connections to be acquired.
retryingSchemaCacheLoad :: AppState -> IO (Maybe PgVersion, Maybe SchemaCache)
retryingSchemaCacheLoad :: AppState -> IO ()
retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThreadId=mainThreadId} =
retrying retryPolicy shouldRetry (\RetryStatus{rsIterNumber, rsPreviousDelay} -> do
void $ retrying retryPolicy shouldRetry (\RetryStatus{rsIterNumber, rsPreviousDelay} -> do
when (rsIterNumber > 0) $ do
let delay = fromMaybe 0 rsPreviousDelay `div` oneSecondInUs
observer $ ConnectionRetryObs delay
putNextListenerDelay appState delay

flushPool appState
(,) <$> qPgVersion <*> qSchemaCache

(,) <$> qPgVersion <*> (qInDbConfig *> qSchemaCache)
)
where
qPgVersion :: IO (Maybe PgVersion)
Expand All @@ -400,6 +394,11 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
putPgVersion appState actualPgVersion
return $ Just actualPgVersion

qInDbConfig :: IO ()
qInDbConfig = do
AppConfig{..} <- getConfig appState
when configDbConfig $ readInDbConfig False appState

qSchemaCache :: IO (Maybe SchemaCache)
qSchemaCache = do
conf@AppConfig{..} <- getConfig appState
Expand Down Expand Up @@ -438,6 +437,7 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
oneSecondInUs = 1000000 -- one second in microseconds

-- | Reads the in-db config and reads the config file again
-- | We don't retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
readInDbConfig :: Bool -> AppState -> IO ()
readInDbConfig startingUp appState@AppState{stateObserver=observer} = do
AppConfig{..} <- getConfig appState
Expand Down
17 changes: 17 additions & 0 deletions src/PostgREST/Network.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module PostgREST.Network
( resolveHost
) where

import Data.IP (fromHostAddress, fromHostAddress6)
import Data.String (IsString (..))
import qualified Network.Socket as NS

import Protolude

resolveHost :: NS.Socket -> IO (Maybe Text)
resolveHost sock = do
sn <- NS.getSocketName sock
case sn of
NS.SockAddrInet _ hostAddr -> pure $ Just $ fromString $ show $ fromHostAddress hostAddr
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ show $ fromHostAddress6 hostAddr6
_ -> pure Nothing
12 changes: 6 additions & 6 deletions src/PostgREST/Observation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import Protolude
import Protolude.Partial (fromJust)

data Observation
= AdminStartObs (Maybe Int)
= AdminStartObs (Maybe Text) (Maybe Int)
| AppStartObs ByteString
| AppServerPortObs NS.PortNumber
| AppServerPortObs Text NS.PortNumber
| AppServerUnixObs FilePath
| ExitUnsupportedPgVersion PgVersion PgVersion
| ExitDBNoRecoveryObs
Expand Down Expand Up @@ -60,12 +60,12 @@ type ObservationHandler = Observation -> IO ()

observationMessage :: Observation -> Text
observationMessage = \case
AdminStartObs port ->
"Admin server listening on port " <> show (fromIntegral (fromJust port) :: Integer)
AdminStartObs host port ->
"Admin server listening on " <> fromJust host <> ":" <> show (fromIntegral (fromJust port) :: Integer)
AppStartObs ver ->
"Starting PostgREST " <> T.decodeUtf8 ver <> "..."
AppServerPortObs port ->
"Listening on port " <> show port
AppServerPortObs host port ->
"Listening on " <> host <> ":" <> show port
AppServerUnixObs sock ->
"Listening on unix socket " <> show sock
DBConnectedObs ver ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,40 @@
pdSchema: public
pdVolatility: Volatile

- - qiName: reset_db_schemas_config
qiSchema: public
- - pdDescription: null
pdFuncSettings: []
pdHasVariadic: false
pdName: reset_db_schemas_config
pdParams: []
pdReturnType:
contents:
contents:
qiName: void
qiSchema: pg_catalog
tag: Scalar
tag: Single
pdSchema: public
pdVolatility: Volatile

- - qiName: change_db_schemas_config
qiSchema: public
- - pdDescription: null
pdFuncSettings: []
pdHasVariadic: false
pdName: change_db_schemas_config
pdParams: []
pdReturnType:
contents:
contents:
qiName: void
qiSchema: pg_catalog
tag: Scalar
tag: Single
pdSchema: public
pdVolatility: Volatile

- - qiName: change_db_schema_and_full_reload
qiSchema: public
- - pdDescription: null
Expand Down
17 changes: 17 additions & 0 deletions test/io/fixtures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ GRANT
CREATE SCHEMA v1;
GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous;

CREATE SCHEMA test;
GRANT USAGE ON SCHEMA test TO postgrest_test_anonymous;

CREATE TABLE authors_only ();
GRANT SELECT ON authors_only TO postgrest_test_author;

Expand Down Expand Up @@ -226,3 +229,17 @@ $$ language sql;
create function get_statement_timeout(items) returns text as $$
select current_setting('statement_timeout', true) as statement_timeout
$$ language sql;

create function change_db_schemas_config() returns void as $_$
begin
alter role postgrest_test_authenticator set pgrst.db_schemas = 'test';
end $_$ volatile security definer language plpgsql;

create function reset_db_schemas_config() returns void as $_$
begin
alter role postgrest_test_authenticator reset pgrst.db_schemas;
end $_$ volatile security definer language plpgsql ;

create function test.get_current_schema() returns text as $$
select current_schema()::text;
$$ language sql;
30 changes: 30 additions & 0 deletions test/io/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,19 @@ def test_log_postgrest_version(defaultenv):
assert "Starting PostgREST %s..." % version in output[0]


def test_log_postgrest_host_and_port(defaultenv):
"PostgREST should output the host and port it is bound to."
host = "127.0.0.1"
port = freeport()

with run(
env=defaultenv, host=host, port=port, no_startup_stdout=False
) as postgrest:
output = postgrest.read_stdout(nlines=10)

assert f"Listening on {host}:{port}" in output[2] # output-sensitive


def test_succeed_w_role_having_superuser_settings(defaultenv):
"Should succeed when having superuser settings on the impersonated role"

Expand Down Expand Up @@ -1618,3 +1631,20 @@ def test_admin_metrics(defaultenv):
assert "pgrst_db_pool_waiting" in response.text
assert "pgrst_db_pool_available" in response.text
assert "pgrst_db_pool_timeouts_total" in response.text


def test_schema_cache_startup_load_with_in_db_config(defaultenv, metapostgrest):
"verify that the Schema Cache loads correctly at startup, using the in-db `pgrst.db_schemas` config"

response = metapostgrest.session.post("/rpc/change_db_schemas_config")
assert response.text == ""
assert response.status_code == 204

with run(env=defaultenv) as postgrest:
response = postgrest.session.get("/rpc/get_current_schema")
assert response.text == '"test"'
assert response.status_code == 200

response = metapostgrest.session.post("/rpc/reset_db_schemas_config")
assert response.text == ""
assert response.status_code == 204

0 comments on commit df5a1f7

Please sign in to comment.