Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimizations for INSERT #2676

Closed
3 tasks done
steve-chavez opened this issue Feb 21, 2023 · 2 comments · Fixed by #3316
Closed
3 tasks done

Optimizations for INSERT #2676

steve-chavez opened this issue Feb 21, 2023 · 2 comments · Fixed by #3316
Labels

Comments

@steve-chavez
Copy link
Member

steve-chavez commented Feb 21, 2023

While working on #2672, I noticed some optimizations we can do for INSERTs.

Our current query on POST /complex_items Prefer: return=representation is:

WITH pgrst_source AS (
  WITH
  pgrst_payload AS (SELECT '[{"id": 4, "name": "Vier"}, {"id": 5, "name": "Funf", "arr_data": null}, {"id": 6, "name": "Sechs", "arr_data": [1, 2, 3], "field-with_sep": 6}]'::json AS json_data),
  pgrst_body AS ( SELECT CASE WHEN json_typeof(json_data) = 'array' THEN json_data ELSE json_build_array(json_data) END AS val FROM pgrst_payload)
  INSERT INTO "test"."complex_items"("arr_data", "field-with_sep", "id", "name")
  SELECT "arr_data", "field-with_sep", "id", "name"
  FROM json_to_recordset ((SELECT val FROM pgrst_body)) AS _ ("arr_data" integer[], "field-with_sep" integer, "id" bigint, "name" text)
  RETURNING "test"."complex_items".*
)
SELECT
  '' AS total_result_set,
  pg_catalog.count(_postgrest_t) AS page_total,
  array[]::text[] AS header,
  coalesce(json_agg(_postgrest_t), '[]')::character varying AS body,
  nullif(current_setting('response.headers', true), '') AS response_headers,
  nullif(current_setting('response.status', true), '') AS response_status
FROM (SELECT "complex_items".* FROM "pgrst_source" AS "complex_items") _postgrest_t;

Optimizations

  • pg_catalog.count(_postgrest_t) AS page_total is always included, but if remove it, this grants us an increase in 12% TPS. AFAICT this is only required for:

    • PUT
    • When using application/vnd.pgrst.object+json
    • When using Prefer: count=exact
    • Cannot be done. See below.
  • Change the CTEs(pgrst_payload, pgrst_body) to LATERAL instead. This increases TPS by 6%. I got the hint of this optimization from limits don't seem to apply directly to the underlying query, causing poor performance #1652 (comment).

  • Use json_to_recordset or json_to_record according to Content type header and inline the json payload as a paremeter. This grants us 31% increase in TPS.

    • Basically removes the internal CTEs(pgrst_payload, pgrst_body) and does FROM json_to_recordset ('[{"id": 4)..') directly.
    • It would require adding an application/vnd.pgrst.array+json media type.
    • It sounds worth it because it's a large increase in TPS.
@steve-chavez
Copy link
Member Author

pg_catalog.count(_postgrest_t) AS page_total is always included, but if remove it, this grants us an increase in 12% TPS. AFAICT this is only required for:

The above cannot be done. We rely on counting the page total even when not using any header. This test proves it:

context "in a nonempty table" $ do
it "can update a single item" $ do
patch "/items?id=eq.2"
[json| { "id":42 } |]
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, "Content-Range" <:> "0-0/*" ]
}

See 0-0. Sharing my attempt on steve-chavez@60a641c, which got 24 test failures.

@steve-chavez
Copy link
Member Author

steve-chavez commented Mar 7, 2024

Use json_to_recordset or json_to_record according to Content type header and inline the json payload as a paremeter. This grants us 31% increase in TPS.

Since the query change on #2677, the above optimization no longer grants such increase. It's only about 10% in TPS now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

1 participant