Commit Graph
57 Commits
Author SHA1 Message Date
Michał KłeczekandSteve Chavez 4fc47754c2 refactor: do not open a TCP connection in liveness check
This change introduces a new way to perform liveness check - instead of trying to open a TCP connection to main server, we check if main socket is listening and if main server accept loop thread is alive.

Opening a TCP connection in liveness check was problematic because:
* it used available file descriptors which might have been a problem under load
* made liveness check unreliable when multiple PostgREST instances are available on the same port (eg. using SO_REUSEPORT)
2026-07-10 12:45:59 -05:00
Michał KłeczekandSteve Chavez c5c9dc33c9 refactor: make sure proper sockets cleanup is performed in App.run
Right now code in App.run does not properly use bracket/finally to close sockets and clean-up mainSocketRef. This is not a big problem at the moment because the application is going to exit enyway but introducting proper resource handling will make future refactorings safer.
2026-07-06 10:59:05 -05:00
Michał KłeczekandSteve Chavez 56df1dc532 refactor: encapsulate application termination
This change makes AppState and Listener modules independent from the way how application is terminated. It removes stateMainThreadId from AppState and introduces stateKillApp. It also removes exported  function AppState.getMainThreadId replacing it with more general killApp.
2026-07-06 10:29:17 -05:00
Michał KłeczekandSteve Chavez 06bda07db1 refactor: encapsulate how main application liveness check is done
This change makes Admin module independent from how liveness check is performed. It moves reachMainApp function to App module and makes Admin.runAdmin, instead of action to get the main socket, take an IO action checking liveness as a parameter.

Thanks to this, any change in how liveness check is performed and, more importantly, what data it uses, does not require changing multiple modules (Admin and App).
2026-07-05 15:22:37 -05:00
Michał KłeczekandTaimoor Zaeem 9f97147cc3 perf: add ghc 9.12 options -fexpose-overloaded-unfoldings -fspecialise-aggressively
Before 9.12.1 it was necessary to mark functions as INLINABLE or INLINE to make GHC consider cross-module specialization of polymorphic functions.
9.12 added a new -fexpose-overloaded-unfoldings flag that exposes optimized polymorphic functions in interface files. -fspecialise-aggressively then makes GHC apply aggresive specialization.
2026-06-17 13:13:48 +05:00
Michał KłeczekandSteve Chavez 8fa26ee865 fix: Start listening after schema cache load
This change ensures PostgREST starts listening on a server socket only after it loaded the schema cache and is ready to handle requests. It is no longer going to return 503 errors during startup until the schema cache is loaded.
2026-06-16 12:30:51 -05:00
Michał KłeczekandWolfgang Walther fae6253932 refactor: make nextListenerDelay local to Listener module
Right now stateNextListenerDelay is only used in Listener module.
This change is a small refactoring moving nextListenerDelay to Listener to increase cohesion and decrease coupling.
2026-06-14 18:39:59 +00:00
Michał KłeczekandSteve Chavez 54430c3a2c fix: Do not clear the schema cache during retries
retryingSchemaCacheLoad should not clear existing schema cache upon failure - there is no reason to do that. If there is a communication issue with the database server or db is down, clients are going to get 502 anyway. If it was a glitch when loading the schema cache - the clients are going to use old (stale) schema cache for some time until next retry re-loads it successfully.
2026-06-11 13:14:37 -05:00
Michał KłeczekandTaimoor Zaeem 13c0e7061e refactor: simplify control flow in App.postgrest
Currently, authentication and response execution each unwrap ExceptT with separate runExceptT calls, which split the main request flow across nested pattern matching and Either handling. Control flow is complex and difficult to understand.

The goal of this change is to make request execution as sequential
monadic code with clear error handling.

To implement that, request handling is now run in ExceptT over WriterT (Last ByteString) IO monad stack. Auth role is written after authentication succeeds and further returned along the response. Thanks to it response observation generation is centralized at the end of request handling.

It was necessary to abstract monad stack in getAuthResult, lookupJwtCache, postgrestResponse, and withTiming to enable introduction of WriterT.
2026-06-02 11:28:19 +05:00
Michał KłeczekandSteve Chavez 0df56f9ea8 refactor: assemble main transaction session in MainTx
This change makes the API surface between MainTx and App smaller.

Currently, App reconstructs a database transaction by unpacking the isolation
level, transaction mode, DbHandler, and transaction runner returned by MainTx.
That exposes MainTx internals at the call site even though MainTx already owns
query setup, execution, decoding, and rollback behavior.

The goal is to keep transaction assembly in MainTx while App remains responsible
for pool execution, database error mapping, and response orchestration. DbTx now
carries the assembled SQL session, and App passes that session directly to the
connection pool.
2026-05-31 15:52:56 -05:00
Michał KłeczekandSteve Chavez 56ad5a9324 refactor: get rid of dbQueryTimings from SchemaCache
Schema cache query timings are only needed immediately after a schema-cache reload to emit SchemaCacheQueriedObs. Storing them inside SchemaCache makes the cache carry transient observability data that is not part of the cached schema state and is never used by request handling.

This change makes querySchemaCache to return query timings in a tuple in parallel to SchemaCache and removes dbQueryTimings field.
2026-05-31 14:20:35 -05:00
Michał KłeczekandSteve Chavez 998d232ac0 docs: PGRST107 error should map to 406 not to 415 2026-05-27 14:37:23 -05:00
Michał KłeczekandWolfgang Walther 1a6ba2072c refactor: Simplify App.initServerSocket
This change gets rid of unnecessary explicit bindRandomPortTCP in initServerSocket. Returned port value was ignored in removed code anyway as assigned port retrieval from an open socket is handled elsewhere.
2026-05-24 23:56:05 +02:00
Michał KłeczekandSteve Chavez 913fe001e5 refactor(remove): schema cache load delay config
Remove internal schema cache load and relationship load sleep settings plus
the delay wrappers they enabled. Drop IO tests that depended on the removed
settings.
2026-05-19 10:59:27 -05:00
Michał KłeczekandSteve Chavez 86d6ed10bb test: cover stale schema cache database error
Add an IO test that drops a table while schema cache reload is delayed. It verifies the stale cache path returns PostgreSQL 42P01 and the refreshed cache returns PGRST205.
2026-05-19 09:11:04 -05:00
Michał KłeczekandTaimoor Zaeem 07938aaab2 amend: Move #4622 to proper Changelog section 2026-05-19 10:55:53 +05:00
Michał KłeczekandSteve Chavez a297391071 fix: implement connection tracking in metrics
Right now metrics observation handler does not track database connections but updates a single Gauge based on HasqlPoolObs events. This is problematic because Hasql pool reports various connection events in multiple phases. The connection state machine is not simple and to precisely report the number of connections in various states, it is necessary to track their lifecycles.

This change adds a ConnTrack data structure and logic to track database connections lifecycles. At the moment it supports "connected" and "inUse" connection counts precisely. The "pgrst_db_pool_available" metric is implemented on top of ConnTrack instead of a simple Gauge.
2026-05-18 10:09:00 -05:00
Michał KłeczekandWolfgang Walther 85d91e5c0a add: Publish GHC runtime metrics to Prometheus metrics endpoint 2026-05-03 11:41:02 +00:00
Michal KleczekandGitHub 04a0e041e4 fix: Restore showing LISTEN pgrst in pg_stat_activity 2026-04-30 17:21:12 -05:00
Michał KłeczekandSteve Chavez baebacf3db fix: shutdown should wait for in flight requests
Upgraded warp to 3.4.13 which fixed https://github.com/yesodweb/wai/issues/853
Changed interrupt handling so that instead of killing the main thread, listening sockets are closed which triggers warp graceful shutdown.
2026-04-20 16:19:13 -05:00
Michal KleczekandGitHub aca58c8571 fix: Flush pool as late as possible during schema cache reloading (#4645)
retryingSchemaCacheLoad flushes the pool upon every retry before it starts reloading the schema. This is too early as schema reloading might take some time during which new connections might be acquired. The consequence is that:
* upon successful schema cache reload we might have some connections created with the old schema cache
* we close connections upon each retry and under load we will keep closing and re-opening connections until schema cache load succeeds

This change is to make sure we flush the pool only after successful schema cache querying but before loading (so that connections acquired during loading wait for it and do not interfere with timing the loading process).
2026-04-14 17:09:25 -05:00
Michał KłeczekandSteve Chavez 381e12efa2 refactor: remove usage of Control.Debounce
This change replaces usage of Control.Debounce in Logger with makeDebouncer function extracted from AppState to a new module PostgREST.Debounce.
2026-04-10 12:22:08 -05:00
Michał KłeczekandSteve Chavez f4fc29855e refactor: remove leftover comment in AppState 2026-04-10 12:22:08 -05:00
Michał KłeczekandSteve Chavez c31aeecdcf test: add test_pool_acquisition_timeout_logs_are_debounced to io tests 2026-04-09 13:59:17 -05:00
Michał KłeczekandSteve Chavez 5d4f82d70f add: log pool flushes
Emit a dedicated PoolFlushed observation when the DB pool is released during schema cache reload.
2026-04-09 10:41:29 -05:00
Michał KłeczekandSteve Chavez ad907219be test(refactor): make debug output testing more robust
This change introduces a match_log function, that implements lenient matching of log output lines. Tests of log output are retrofited to use the function.
2026-04-09 10:41:29 -05:00
Michał KłeczekandSteve Chavez a4c1d945ee refactor: Simplify schema cache loading triggering logic
DISCLAIMER:
This commit was authored entirely by a human without the assistance of LLMs.

Using debouncer to trigger schema cache loading makes it difficult to understand when exactly it is triggered.
2026-04-07 13:44:03 -05:00
Michał KłeczekandSteve Chavez 328598eaed test: Schema cache load debouncing
test: adjust replicateM to 100
2026-04-02 09:02:18 -05:00
Michał KłeczekandSteve Chavez bd5de884fa test(io): add test_so_reuseport_zero_downtime_handover 2026-03-31 13:33:33 -05:00
Michal KleczekandGitHub 8d5b1018d3 refactor(test): provide means to validate metrics and observations
Some helpers are provided for introspecting metrics already (used in JWT cache tests). This change provides facilities to additionally validate emited Observation events.
A new Spec module is also implemented, adding basic tests of schema cache reloading - their main goal is to excercise the new infrastructure.
2026-03-31 13:15:56 -05:00
Michał KłeczekandSteve Chavez bee2ea6fa6 test(io): add test_graceful_shutdown_waits_for_in_flight_request 2026-03-12 15:24:46 -05:00
Michał KłeczekandSteve Chavez a4aa58b4de refactor: move socket creation and management to App module
Right now listening sockets initialization, management and usage is split between App, AppState and Admin modules: they are created in AppState.init and remembered in AppState but used only in App and Admin.

It has several negative consequences:
- sockets are initialized even if not needed (eg. command line invocations like dump-config or dump-schema)
- it is impossible to start listening on a socket after initial schema cache load because it requires AppState

This change decouples listen socket management from AppState. Sockets are created only when needed (ie. not in command line tools invocation) and passed to admin application and to Warp by the App module.
2026-03-12 10:03:47 -05:00
Michał KłeczekandSteve Chavez e741c1bca7 refactor: provide AppState infrastructure to wait for schema cache load
This commit replaces ioRef based implementation of schema cache status tracking to MVar based, so that it is possible to wait for schema cache loading.

Waiting for schema cache loading is necessary to implement zero-downtime upgrades  with SO_REUSEPORT, where listening on a socket must wait for schema cache loading.
2026-03-12 09:04:56 -05:00
Michał KłeczekandSteve Chavez 8e1f0a0471 refactor: standardize query logging to follow code logging other observations
This commit addresses the following TODO in observationMessage function:
"TODO pending refactor: The logic for printing the query cannot be done here. Join the observationMessages function into observationLogger to avoid this mempty."

Changing observationMessage to observationMessages (that returns [Text] instead of Text) made it possible to address issue documented as TODO and harmonize query logging with other observations.
2026-03-02 15:46:17 -05:00
Michał KłeczekandSteve Chavez 2408cd332d refactor: get rid of redundant SchemaCacheSummaryObs
There is unnecessary coupling between observation messages and emited log entries. This causes schema loading logic to emit redundant events: SchemaCacheSummaryObs and SchemaCacheLoadedObs.

Logically - we want to emit a single event containing both summary and timing information. How it is logged is a different matter and should be decoupled.

This commit
* changes observationMessage function returning Text to observationMessages returning [Text] so that it is possible to return multiple (or zero) messages to log based on an observation event
* Removes SchemaCacheSummaryObs constructor from Observation type and adds summary text to SchemaCacheLoadedObs
2026-03-02 14:45:31 -05:00
Michał KłeczekandSteve Chavez 58a973e664 refactor: make Observation Logger and Listener modules more cohesive
Observation module exports observationMessage and isDbListenerBug functions. The first one is used only in Logger module and the second one - only in Listener.

This change moves observationMessage function to Logger module and isDbListenerBug function to Listener module, making all three modules more cohesive and lessening dependencies.
It also gives the compiler more opportunities for intra-module inlining and optimization.
2026-03-02 12:26:22 -05:00
Michał KłeczekandSteve Chavez 78f231c87a refactor: failure in schema cache load should not set listener delay
Listener has its own backoff logic independent of schema cache loading. It also uses listener delay value to identify its retries and to trigger schema cache reloads upon failures.
If schema cache reloading sets listener delay it might lead to some unexpected reloading loops.
That's especially important in set ups where listener connection points to master and the pool to replica(s).
2026-02-25 17:34:38 -05:00
Michał KłeczekandSteve Chavez 85a313a8cc test(spec): Move metrics state helpers from JwtCacheSpec to SpecHelpers
Refactoring: State validation helpers used in JwtCacheSpec moved to SpecHelper
to make them available in other Spec modules.
2026-02-17 17:55:16 -05:00
Michał KłeczekandSteve Chavez ca96328142 refactor: Remove unnecessary lazy initialization of stateLogDebouncePoolTimeout
stateLogDebouncePoolTimeout is an MVar initialized on the first logging of PoolAcqTimeoutObs. The code in logWithDebounce has race condition that could lead to creation of multiple debouncers.

This change simplifies logic by getting rid of lazy initialization of debouncer.
2026-02-15 13:14:10 -05:00
Michał KłeczekandSteve Chavez d6816d8d2a refactor: Remove unnecessary SQL.UsageError parameter from PoolAcqTimeoutObs
PoolAcqTimeoutObs is always created with SQL.AcquisitionTimeoutUsageError so the parameter does not provide any useful information.
On the other hand, it complicates debouncing logic in Logger as logWithDebounce has to lazily create the debouncer (which is error prone and hence the logic has a race condition).

This change removes the parameter from PoolAcqTimeoutObs. It is a prerequisite to simplifying logWithDebounce.
2026-02-15 13:14:10 -05:00
Michal KleczekandGitHub 73a465501e fix: listener running with exception masked after first failure 2026-01-28 17:13:59 -05:00
Michał KłeczekandSteve Chavez 21b8c3458a add: Log pg version details of listener connection
Follow-up to #4617 adding more information to log entry produced upon successful listener connection establishement.
2026-01-27 15:44:05 -05:00
Michał KłeczekandSteve Chavez 5356f4e973 add: Log actual host and port of listener connection
Diagnosing problems with listener channel notifications not being handled properly by PostgREST connected to read replicas is difficult. Issues might be related to lost connections and listener not being connected to the right host after failover or database server restarts.
This patch adds logging of actual host:port used by libpq connection opened by the listener. It should make it easier to find out if PostgREST is connected to the right host.
2026-01-27 11:00:30 -05:00
Michal KleczekandGitHub 00c7cb1a22 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.
2026-01-26 16:26:24 -05:00
Michał KłeczekandSteve Chavez 886df84e87 test: Fix flakiness of test_second_request_for_non_existent_table_should_be_quick
Changed divider in assertion (response.elapsed.total_seconds() < first_duration / divider) to 2 (from 10).
2026-01-13 11:37:50 +07:00
Michał KłeczekandSteve Chavez e592d568c6 fix: Performance and high memory usage of relation hint calculation
* Calculation of hint message when requested relation is not present in schema cache requires creation of a FuzzySet (to use fuzzy search to find candidate tables). For schemas with many tables it is costly.
This patch introduces dbTablesFuzzyIndex in SchemaCache to memoize the FuzzySet creation.

* Additionally, because of FuzzySet large memory requirements, this patch introduces a limit of 500 relations per schema, above which FuzzySet is not created and hint calculation disabled.
2026-01-03 07:56:12 +08:00
Michał KłeczekandSteve Chavez 75d4131aa6 chore: Add commit structuring guidelines to CONTRIBUTING.md 2025-10-28 10:02:56 -05:00
Michał KłeczekandLaurence Isla 66161104bf refactor: Remove redundant VANull constructor in Auth.JWT module 2025-10-22 00:40:30 +00:00
Michał KłeczekandLaurence Isla 5a4e2e4dec refactor: Encapsulate aud config
This change is an initial step to change JWT aud configuration to regular expression.
Exporting function
audMatchesCfg :: AppConfig -> Text -> Bool
from Config module allows changing the way how JWT aud is configured to be isolated and not affect code in Auth.JWT
2025-10-22 00:40:30 +00:00
Michal KleczekandGitHub be9a0ab467 perf: Index various lists in SchemaCache to change complexity from O(n*n) to O(n) (#4396) 2025-10-20 21:17:50 -05:00
Michał KłeczekandSteve Chavez c08b87749b test: Separated query and loading internal sleep configs
To make schema cache loading wait tests robust it is necessary to provide three separate internal config variables:
* "internal-schema-cache-query-sleep" - introduces delay in schema queries execution
* "internal-schema-cache-load-sleep" - introduces delay between schema queries execution and processing their results
* "internal-schema-cache-relationship-load-sleep" - introduces delay in processing relationship query results

Thanks to these changes it is now possible to test various schema loading scenarios with the right granularity robustly (eg. make sure requests wait for schema loading but not for relationship loading).
2025-10-17 13:42:35 -05:00
Michal KleczekandGitHub 77ff11de95 feat: JWT cache implementation based on sieve algorithm (#4084)
Changes:

1. Refactoring and some cleanup of JWT handling code:
* Instead of caching AuthResult cache decoded claims (which signature was verified). Validating claims and determining role is done after cache lookup
* Cleaned up API so that usage of it is simplified: lookupJwtCache cache key >>= parseClaims configJwtAud time
* Handling of JwtCacheState initialization and updates of configuration is encapsulated in Auth.JwtCache module

2. Generic high performance (hopefully) scalable, dynamically resizeable cache implementation based on stm, stm-hamt and sieve algorithm. It also integrates with PostgREST measurements infrastructure providing usage stats (ie. hit ratio, evictions count)
2025-07-29 18:51:41 -05:00
Michal KleczekandGitHub 69072116be test: Use RSA 4096 algorithm for JWT load test (#4118)
Until now we had a load test with 50k unique JWTs signed with symmetric key.
This commit adds a new load test with 10k JWTs signed with RSA 4096.

Existing -k jwt parameter was changed to -k jwt-hs-50k.
New test is run with -k jwt-rsa-10k parameter.

Additionally a new parameter --jwtcache=off was added to turn off JWT caching in the above load tests.
2025-06-05 07:44:05 -05:00
Michał KłeczekandSteve Chavez a409a2cb94 Extracted JWT validation functions to a separate module.
This change introduces a PostgREST.Auth.Jwt module containing JWT validation functions.

The reason to extract them from Auth is to enable JwtCache module to reuse them without introducing module dependency cycle.
2025-06-02 12:26:39 -05:00
Michał KłeczekandSteve Chavez 7e3fb2ba08 Define MetricsState as record and use Applicative to initialize one 2025-06-02 11:49:01 -05:00
Michal KleczekandGitHub 4d8502371d fix: purge JWT cache asynchronously in a separate thread
Otherwise performance was reduced unnecessarily.
2025-04-18 17:50:32 -05:00
Michal KleczekandGitHub 7a87f495df docs: add pg-notify-stdout to ecosystem 2024-06-14 19:25:43 +02:00