From 36300da16dbd0237d853639539930589d518901e Mon Sep 17 00:00:00 2001 From: Steve Chavez Date: Sun, 7 May 2023 12:25:12 -0300 Subject: [PATCH] connection pool reference (#616) * add link in index to external jwt * change pgrst errors location for better reading --- docs/admin.rst | 24 ------------ docs/configuration.rst | 6 +-- docs/connection_pool.rst | 83 ++++++++++++++++++++++++++++++++++++++++ docs/errors.rst | 47 ++++++++++++----------- docs/index.rst | 8 ++++ docs/install.rst | 12 +++--- 6 files changed, 124 insertions(+), 56 deletions(-) create mode 100644 docs/connection_pool.rst diff --git a/docs/admin.rst b/docs/admin.rst index 3f427feac..ecd7972d1 100644 --- a/docs/admin.rst +++ b/docs/admin.rst @@ -132,19 +132,6 @@ The burst argument tells Nginx to start dropping requests if more than five queu Nginx rate limiting is general and indiscriminate. To rate limit each authenticated request individually you will need to add logic in a :ref:`Custom Validation ` function. -.. _external_connection_poolers: - -Using External Connection Poolers ---------------------------------- - -PostgREST manages its :ref:`own pool of connections ` and uses prepared statements by default in order to increase performance. However, this setting is incompatible with external connection poolers such as PgBouncer working in transaction pooling mode. In this case, you need to set the :ref:`db-prepared-statements` config option to ``false``. On the other hand, session pooling is fully compatible with PostgREST, while statement pooling is not compatible at all. - -.. note:: - - If prepared statements are enabled, PostgREST will quit after detecting that transaction or statement pooling is being used. - -You should also set the :ref:`db-channel-enabled` config option to ``false``, due to the ``LISTEN`` command not being compatible with transaction pooling, although it should not give any errors if it's left enabled by default. - Debugging ========= @@ -205,17 +192,6 @@ A great way to inspect incoming HTTP requests including headers and query parame The options to ngrep vary depending on the address and host on which you've bound the server. The binding is described in the :ref:`configuration` section. The ngrep output isn't particularly pretty, but it's legible. -.. _automatic_recovery: - -Automatic Connection Recovery ------------------------------ - -When PostgREST loses the connection to the database, it retries the connection using capped exponential backoff, with 32 seconds being the maximum backoff time. - -This retry behavior is triggered immediately after the connection is lost if :ref:`db-channel-enabled` is set to true(the default), otherwise it will be activated once a request is made. - -To notify the client when the next reconnection attempt will be, PostgREST responds with ``503 Service Unavailable`` and the ``Retry-After: x`` header, where ``x`` is the number of seconds programmed for the next retry. - Database Logs ------------- diff --git a/docs/configuration.rst b/docs/configuration.rst index e43f2d003..25d8e9282 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -330,7 +330,7 @@ db-pool **In-Database** `n/a` =============== ================= - Number of connections to keep open in PostgREST's database pool. Having enough here for the maximum expected simultaneous client connections can improve performance. Note it's pointless to set this higher than the :code:`max_connections` GUC in your database. + Number of maximum connections to keep open in PostgREST's database pool. .. _db-pool-acquisition-timeout: @@ -342,7 +342,7 @@ db-pool-acquisition-timeout **In-Database** `n/a` =============== ================= - Specifies the maximum time in seconds that the request will wait for the pool to free up a connection slot to the database. If it times out without acquiring a connection, then the request is aborted and a ``504`` error is returned. + Specifies the maximum time in seconds that the request will wait for the pool to free up a connection slot to the database. .. _db-pool-max-lifetime: @@ -354,7 +354,7 @@ db-pool-max-lifetime **In-Database** `n/a` =============== ================= - Specifies the maximum time in seconds of an existing connection in the pool. When this lifetime is reached, then the connection will be closed and returned to the pool. + Specifies the maximum time in seconds of an existing connection in the pool. .. _db-pre-request: diff --git a/docs/connection_pool.rst b/docs/connection_pool.rst new file mode 100644 index 000000000..9bb38c3c6 --- /dev/null +++ b/docs/connection_pool.rst @@ -0,0 +1,83 @@ +Connection Pool +--------------- + +Every request to an :doc:`API resource ` borrows a connection from the connection pool to start a :doc:`transaction `. + +A connection pool is a cache of reusable database connections. It allows serving many HTTP requests using few database connections. + +Minimizing connections it’s paramount to performance. Each PostgreSQL connection creates a process, having too many can exhaust available resources. + +.. _pool_growth_limit: + +Growth Limit +------------ + +If all the connections are being used, a new connection is added to the pool. The pool can grow until it reaches the :ref:`db-pool` size. + +Note it’s pointless to set this higher than the ``max_connections`` setting in your database. + +Connection lifetime +------------------- + +After a period of time, connections from the pool will be released and news ones will be created. This time is specified by :ref:`db-pool-max-lifetime`. + +The lifetime doesn't affect running requests. Only unused connections will be released. + +For knowing why a connection lifetime is necessary, see the following discussion: +https://www.postgresql.org/message-id/flat/CA%2Bmi_8bnvpxHZtb6EgHSHY-xn29W8VJMzjPU3fiCOv1bfjrNuA%40mail.gmail.com. + +Acquisition Timeout +------------------- + +If all the available connections in the pool are busy, an HTTP request will wait until reaching a timeout. You can configure this timeout with :ref:`db-pool-acquisition-timeout`. + +If the request reaches the timeout, it will be aborted with the following response: + +.. code-block:: http + + HTTP/1.1 504 Gateway Timeout + + {"code":"PGRST003", + "details":null, + "hint":null, + "message":"Timed out acquiring connection from connection pool."} + +Getting this error message is an indicator of a performance issue. To solve it, you can: + +- Reduce your queries execution time. + + - Check the request :ref:`explain_plan` to tune your query, this usually means adding indexes. + +- Reduce the amount of requests. + + - Reduce write requests. Do :ref:`bulk_insert` (or :ref:`upsert`) instead of inserting rows one by one. + - Reduce read requests. Use :ref:`resource_embedding`. Combine unrelated data into a single request using custom database views or functions. + - Use :ref:`s_procs` for combining read and write logic into a single request. + +- Increase the :ref:`pool growth limit `. + + - Not a panacea since connections can't grow infinitely. Try the previous recommendations before this. + +.. _automatic_recovery: + +Automatic Recovery +------------------ + +If the pool loses the connection to the database, it will retry reconnecting using exponential backoff. With 32 seconds being the maximum backoff time between retries. + +The retries happen immediately after a connection loss, if :ref:`db-channel-enabled` is set to true(the default). Otherwise they'll happen once a request arrives. + +To notify the client of the next retry, the server sends a ``503 Service Unavailable`` status with the ``Retry-After: x`` header. Where ``x`` is the number of seconds programmed for the next retry. + +.. _external_connection_poolers: + +Using External Connection Poolers +--------------------------------- + +It's possible to use external connection poolers, such as PgBouncer. Session pooling is compatible, while transaction pooling requires :ref:`db-prepared-statements` set to ``false``. Statement pooling is not compatible with PostgREST. + +Also set :ref:`db-channel-enabled` to ``false`` since ``LISTEN`` is not compatible with transaction pooling. Although it should not give any errors if left enabled. + +.. note:: + + It’s not recommended to use an external connection pooler. `Our benchmarks `_ indicate it provides much lower performance than PostgREST built-in pool. diff --git a/docs/errors.rst b/docs/errors.rst index 5fe83b14d..74c4a3f84 100644 --- a/docs/errors.rst +++ b/docs/errors.rst @@ -28,29 +28,6 @@ PostgREST will forward errors coming from PostgreSQL. For instance, when queryin "message": "relation \"api.nonexistent_table\" does not exist" } -Errors from PostgREST ---------------------- - -Errors that come from PostgREST itself maintain the same structure. But differ in the ``PGRST`` prefix in the ``code`` field. For instance, when querying a function that does not exist in the :doc:`schema cache `: - -.. code-block:: http - - POST /rpc/nonexistent_function HTTP/1.1 - -.. code-block:: http - - HTTP/1.1 404 Not Found - Content-Type: application/json; charset=utf-8 - -.. code-block:: json - - { - "hint": "...", - "details": null - "code": "PGRST202", - "message": "Could not find the api.nonexistent_function() function in the schema cache" - } - .. _status_codes: HTTP Status Codes @@ -119,6 +96,30 @@ PostgREST translates `PostgreSQL error codes `: + +.. code-block:: http + + POST /rpc/nonexistent_function HTTP/1.1 + +.. code-block:: http + + HTTP/1.1 404 Not Found + Content-Type: application/json; charset=utf-8 + +.. code-block:: json + + { + "hint": "...", + "details": null + "code": "PGRST202", + "message": "Could not find the api.nonexistent_function() function in the schema cache" + } + + .. _pgrst_errors: PostgREST Error Codes diff --git a/docs/index.rst b/docs/index.rst index fa98bb6b5..24ff6547b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -148,6 +148,12 @@ Technical references for PostgREST's functionality. transactions.rst +.. toctree:: + :caption: Connection Pool + :hidden: + + connection_pool.rst + .. toctree:: :caption: Configuration :hidden: @@ -169,6 +175,7 @@ Technical references for PostgREST's functionality. - :doc:`Authentication ` - :doc:`API ` - :doc:`Transactions ` +- :doc:`Connection pool ` - :doc:`configuration` - :doc:`Schema Cache ` - :doc:`Errors ` @@ -235,6 +242,7 @@ PostgREST has a growing ecosystem of examples, libraries, and experiments. Here ecosystem.rst +* :ref:`external_jwt` * :ref:`community_tutorials` * :ref:`templates` * :ref:`eco_example_apps` diff --git a/docs/install.rst b/docs/install.rst index 8ba9f56d4..508f830b0 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -289,7 +289,7 @@ Deploying to Heroku # wait until the add-on is available heroku pg:wait -a ${YOUR_APP_NAME} -4. Create the necessary user roles according to the +4. Create the necessary user roles according to the `PostgREST documentation `_: .. code-block:: bash @@ -330,7 +330,7 @@ Deploying to Heroku web: PGRST_SERVER_HOST=0.0.0.0 PGRST_SERVER_PORT=${PORT} PGRST_DB_URI=${PGRST_DB_URI:-${DATABASE_URL}} ./postgrest-${POSTGREST_VER} .. - + Set the following environment variables on Heroku: .. code-block:: bash @@ -340,7 +340,7 @@ Deploying to Heroku heroku config:set PGRST_DB_ANON_ROLE=api_user .. - PGRST_DB_URI can be set if an external database is used or if it's different from the default Heroku DATABASE_URL. This latter is used if nothing is provided. + PGRST_DB_URI can be set if an external database is used or if it's different from the default Heroku DATABASE_URL. This latter is used if nothing is provided. POSTGREST_VER is mandatory to select and build the required PostgREST release. See https://postgrest.org/en/stable/configuration.html#environment-variables for the full list of environment variables. @@ -368,9 +368,9 @@ Deploying to Heroku From a different terminal retrieve with curl the records previously created: .. code-block:: bash - + curl https://${YOUR_APP_NAME}.herokuapp.com/todos - .. + .. and test that any attempt to modify the table via a read-only user is not allowed: @@ -378,4 +378,4 @@ Deploying to Heroku curl https://${YOUR_APP_NAME}.herokuapp.com/todos -X POST \ -H "Content-Type: application/json" \ - -d '{"task": "do bad thing"}' + -d '{"task": "do bad thing"}'