Skip to content

add: per request statement timeout using prefer header#4584

Draft
taimoorzaeem wants to merge 1 commit intoPostgREST:mainfrom
taimoorzaeem:add/per-request-timeout
Draft

add: per request statement timeout using prefer header#4584
taimoorzaeem wants to merge 1 commit intoPostgREST:mainfrom
taimoorzaeem:add/per-request-timeout

Conversation

@taimoorzaeem
Copy link
Copy Markdown
Member

@taimoorzaeem taimoorzaeem commented Dec 29, 2025

Adds a feature to set statement_timeout using Prefer: timeout header. This also introduces a PGRST129 error which is returned when the timeout preferred exceeds the per-role timeout, which prevents misuse of this feature.

Closes #4381.

  • Implementation
  • Tests
  • Docs

@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch from d074eb9 to 2c5589f Compare December 29, 2025 16:37
@taimoorzaeem taimoorzaeem marked this pull request as draft December 29, 2025 16:40
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 3 times, most recently from 5f1d601 to 6963e9a Compare January 1, 2026 07:48
@taimoorzaeem taimoorzaeem changed the title [WIP] add: per request statement timeout using prefer header add: per request statement timeout using prefer header Jan 1, 2026
@taimoorzaeem taimoorzaeem marked this pull request as ready for review January 1, 2026 07:48
Comment thread docs/references/api/preferences.rst Outdated
@steve-chavez
Copy link
Copy Markdown
Member

We should also fail in case a value exceeds the per role timeout. Otherwise it's abusable.

This failure should happen at the plan level, before hitting the db. I think it will need a new error code.

@taimoorzaeem taimoorzaeem marked this pull request as draft January 2, 2026 08:23
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 5 times, most recently from a82c07f to 0d6370e Compare January 7, 2026 07:13
@taimoorzaeem taimoorzaeem marked this pull request as ready for review January 7, 2026 07:15
@taimoorzaeem taimoorzaeem added the enhancement a feature, ready for implementation label Jan 7, 2026
Comment thread src/PostgREST/Plan.hs Outdated
@taimoorzaeem taimoorzaeem marked this pull request as draft January 12, 2026 07:27
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch from 0d6370e to 6481e8e Compare January 13, 2026 05:40
@taimoorzaeem taimoorzaeem marked this pull request as ready for review January 13, 2026 05:41
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch from 6481e8e to a37d497 Compare January 15, 2026 14:56
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 2 times, most recently from 1998a61 to cef3feb Compare January 25, 2026 07:09
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 2 times, most recently from cd53ca3 to 356cecf Compare January 28, 2026 07:32
@taimoorzaeem taimoorzaeem marked this pull request as draft January 29, 2026 12:03
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch from 356cecf to a1f19e7 Compare February 3, 2026 11:30
Comment thread src/PostgREST/ApiRequest/Preferences.hs Outdated
Comment thread test/io/test_prefer_header.py Outdated
Comment thread src/PostgREST/Config/Database.hs Outdated
@taimoorzaeem taimoorzaeem marked this pull request as draft February 7, 2026 09:34
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 4 times, most recently from 001ee79 to e77acb3 Compare February 11, 2026 14:32
Comment thread docs/references/api/preferences.rst
Comment thread test/io/test_prefer_header.py
Comment thread test/io/fixtures/roles.sql Outdated
Comment thread src/PostgREST/Config/Database.hs Outdated
Comment thread src/PostgREST/Config/Database.hs Outdated
Comment thread src/PostgREST/Query/PreQuery.hs Outdated
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 2 times, most recently from e52e45f to 77602ac Compare February 14, 2026 06:25
@taimoorzaeem taimoorzaeem marked this pull request as ready for review February 14, 2026 06:27
Comment thread src/PostgREST/Config/Database.hs Outdated
Comment thread src/PostgREST/Config/Database.hs Outdated
Comment thread src/PostgREST/Config/Database.hs Outdated
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch 2 times, most recently from 4a92e87 to c8f4f27 Compare February 17, 2026 05:35
@taimoorzaeem taimoorzaeem marked this pull request as draft February 17, 2026 05:40
Adds a feature to set `statement_timeout` using `Prefer: timeout`
header. This also introduces a `PGRST129` error which is returned
when the timeout preferred exceeds the per-role or global timeout.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
@taimoorzaeem taimoorzaeem force-pushed the add/per-request-timeout branch from c8f4f27 to 7c49b66 Compare February 18, 2026 14:48
@taimoorzaeem taimoorzaeem marked this pull request as ready for review February 18, 2026 14:49
Comment thread src/PostgREST/Plan.hs
failPreferTimeout Preferences{preferTimeout=(Just (PreferTimeout t)), preferHandling=Just Strict} AppConfig{..} AuthResult{..} =
case roleTimeout of
Nothing -> Right ()
Just rt -> when (t > rt) $ Left $ ApiRequestError $ TimeoutConstraintError rt
Copy link
Copy Markdown
Collaborator

@mkleczek mkleczek Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is problematic because it undermines the whole idea of this header.

Who would want to set the timeout header to a value less than the role setting? What for?

The only realistic use case is that a client might want to increase statement_timeout for a particular query.

So sensible limit is greater than the role setting. Don't know: maybe 2x role setting should be the limit? But then - why not simply set the role setting to a greater value?

Thinking about it: it looks like having any limit is contradictory to the idea of this header. OTOH not having a limit is a security vulnerability. No good choice here, I'm afraid.

@taimoorzaeem @steve-chavez WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is problematic because it undermines the whole idea of this header.
Thinking about it: it looks like having any limit is contradictory to the idea of this header. OTOH not having a limit is a security vulnerability. No good choice here, I'm afraid.

I think you're right. My idea with the limit was to make the header safe to expose to untrusted clients but that makes it less useful. Also, it might never be totally safe because setting a 1s timeout could force some queries to fail and flood logs on the server.

So this header should fall in the same category as prefer:tx and Accept: application/vnd.pgrst.plan, it can only be exposed to trusted users.

This means it should be gated by a config and that we should remove the statement_timeout limit.

Thoughts?


To expose this header to an untrusted network, a similar thing to https://docs.postgrest.org/en/latest/references/observability.html#securing-the-execution-plan could be used.

Copy link
Copy Markdown
Member

@wolfgangwalther wolfgangwalther Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who would want to set the timeout header to a value less than the role setting? What for?

In way this would be similar to soft and hard limits in Linux, set via ulimit. You can vary the soft limit on your own as long as you stay below the hard limit. Just because there is some higher limit available doesn't mean that you will always make full use of it.

I think it only really becomes useful when you have both:

  • a low default for the soft limit, which you can override per request, and
  • a higher hard limit

IIUC, we currently only have the limit defaulting to the hard limit, which then becomes the odd case of "you can only lower your limit".

Edit: This specifically only answers this question - I'm not saying we should implement any of this and also made no judgement on the PR or the related issue.

Copy link
Copy Markdown
Collaborator

@mkleczek mkleczek Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who would want to set the timeout header to a value less than the role setting? What for?

In way this would be similar to soft and hard limits in Linux, set via ulimit.

To be honest, I always had a problem with understanding the purpose of soft limits. The only sensible use case IMHO is "grace periods" - ie. exceeding soft limit triggers a warning and requires a corrective action in a specified time period. But that really only makes sense for disk quotas etc. (and is not exactly the use case of user-provided limit).

Our situation is: a user has a query to run. The query will take x seconds to execute or will fail if the limit is lower. As a user I want to make sure my query does not fail so the only sensible strategy (as I don't know how long it will take) is not to set any limit by myself (and deal with possibility that system enforced limit is lower).

Of course - we could implement tracking of resource usage (per user? per tenant?) and have a policy like allow 10 queries above soft limit per month - but I don't think we want to do that :)

@taimoorzaeem taimoorzaeem marked this pull request as draft February 20, 2026 06:02
@taimoorzaeem taimoorzaeem removed the enhancement a feature, ready for implementation label Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Per-request timeout

4 participants