Skip to content

Improve hypertable DML performance with chunk exclusion#9315

Open
arajkumar wants to merge 19 commits intomainfrom
arajkumar/fix-ht-update-delete-slowness
Open

Improve hypertable DML performance with chunk exclusion#9315
arajkumar wants to merge 19 commits intomainfrom
arajkumar/fix-ht-update-delete-slowness

Conversation

@arajkumar
Copy link
Copy Markdown
Member

@arajkumar arajkumar commented Feb 26, 2026

Postgres's standard inheritance expansion opens every chunk of a hypertable during UPDATE/DELETE planning, which causes severe performance degradation as chunk count grows. This patch routes
UPDATE/DELETE through TimescaleDB's existing chunk exclusion code (HypertableRestrictInfo), expanding only chunks that match the query's WHERE clause — the same optimization already used for SELECT.

The key change is in planner.c's RTE marking logic: UPDATE/DELETE target relations are now marked for TimescaleDB's custom expansion (via rte_mark_for_expansion), whereas previously they were left to Postgres. MERGE is intentionally excluded since our custom expansion only handles UPDATE/DELETE DML fixups.

The per-chunk expansion in expand_hypertable.c is refactored to use a copied expand_single_inheritance_child (from PG's inherit.c), which handles RTE construction, AppendRelInfo creation, result relation registration, and row identity setup — replacing ~60 lines of hand-rolled equivalent code.

Also fixes one of pre-existing bug exposed by this change:

  • ts_set_append_rel_size: missing varno check in width estimation
    caused out-of-bounds writes when ROWID_VAR entries were present
    in the parent rel's reltarget. Ported from upstream PG.

CPU profile before the fix(2.25.1):
unoptimized

CPU profile after the fix(2.26.0-dev):
optimized

Perf Profile Comparison: Chunk Exclusion for DML

Overall CPU Time

Metric no-opti (2.25.1) opti (2.26.0-dev) Improvement
Samples 184 16 11.5x fewer
Sample duration 8795 ms 6145 ms 1.4x shorter
Event count ~1.86B ~0.16B 11.5x less CPU

Key Hotspots Eliminated

The non optimized profile is dominated by planner overhead

Function no-opti % opti % Purpose
pg_strtok 10.3% - Parsing constraint expressions for all chunks
strtoll 6.0% - Parsing integer constants in constraints
AllocSetAlloc 5.4% - Memory allocation during constraint checking
relation_excluded_by_constraints 4.4% - Checking CHECK constraints per chunk
expression_tree_walker 3.8% - Walking expression trees for exclusion
copyObjectImpl 3.3% - Copying constraint nodes
get_relation_info 1.6% - Opening catalog for each chunk

Single-Row UPDATE Benchmark: TimescaleDB 2.25.1 vs 2.26.0-dev

  • Date: 2026-02-26
  • Benchmark: benchmark_update_single.sql
  • Workload: 50 individual single-row UPDATEs in a loop (realistic CDC pattern)
  • Dataset: 1M rows, 200 chunks (1-day interval), PK on (id, time)
  • Compressed HT: 195 compressed chunks + 5 uncompressed
  • Logical Partition: 200 native PG range partitions
  • Warmup: 3 rounds (custom/generic), 5 rounds (auto)
  • Measured runs: 3 (best-of-3 reported below)

Total Loop Time for 50 Single-Row UPDATEs Across 5 Chunks (ms)

Table Type Plan Mode 2.25.1 2.26.0-dev Speedup
Hypertable force_custom 93.1 8.5 ~11x
Hypertable force_generic 52.1 53.2 ~1x
Hypertable auto 52.7 8.9 ~6x
Compressed HT force_custom 229.8 9.3 ~25x
Compressed HT force_generic 52.6 52.4 ~1x
Compressed HT auto 218.1 9.2 ~24x
Logical Partition force_custom 3.5 3.4 ~1x
Logical Partition force_generic 1.8 1.9 ~1x
Logical Partition auto 3.5 3.3 ~1x
Plain Table force_custom 2.6 2.5 ~1x
Plain Table force_generic 1.0 1.0 ~1x
Plain Table auto 1.0 1.0 ~1x

Total Loop Time for 50 Single-Row UPDATEs Within 1 Chunk (ms)

Table Type Plan Mode 2.25.1 2.26.0-dev Speedup
Hypertable force_custom 94.3 8.3 ~11x
Hypertable force_generic 53.1 52.4 ~1x
Hypertable auto 54.9 8.9 ~6x
Compressed HT force_custom 221.8 9.0 ~25x
Compressed HT force_generic 52.9 53.5 ~1x
Compressed HT auto 209.8 9.6 ~22x
Logical Partition force_custom 3.4 3.4 ~1x
Logical Partition force_generic 1.9 1.9 ~1x
Logical Partition auto 3.5 3.5 ~1x
Plain Table force_custom 2.5 2.5 ~1x
Plain Table force_generic 1.0 1.0 ~1x
Plain Table auto 1.0 1.0 ~1x

Planning Time Per Query Comparison (ms)

Table Type Plan Mode 2.25.1 2.26.0-dev
Hypertable force_custom 2.117 0.118
Compressed HT force_custom 4.581 0.130
Compressed HT auto 4.548 0.131

Planning Buffer Hits Per Query

Table Type Plan Mode 2.25.1 2.26.0-dev
Hypertable force_custom 10 N/A
Compressed HT force_custom 2,609 N/A

In 2.25.1 the compressed HT custom plan touches 2,609 shared buffers during planning (scanning all chunk metadata). This overhead is eliminated in 2.26.0-dev via chunk exclusion for DML.

Key Takeaways

Hypertable UPDATEs with custom plan are ~11x faster.
Planning time dropped from ~2.1ms to ~0.12ms per query. Chunk exclusion for DML now prunes irrelevant chunks during planning rather than scanning all 200.

Compressed Hypertable UPDATEs with custom plan are ~25x faster.
This was the worst case in 2.25.1 - each custom plan touched 2,609 shared buffers to inspect compressed chunk metadata. In 2.26.0-dev, chunk exclusion eliminates this entirely.

auto mode now correctly picks the fast custom plan.
In 2.25.1, auto mode chose generic plan for uncompressed HT (~53ms) but was stuck with expensive custom plan for compressed HT (~218ms). In 2.26.0-dev, auto correctly uses custom plan at ~9ms for both, since the custom plan is now cheaper than the generic plan.

force_generic_plan is unchanged (~53ms).
Generic plans skip per-query planning and scan all chunks at execution time. No change is expected here.

Logical Partition and Plain Table are unchanged.
The fix only affects hypertable code paths. These baselines confirm there is no regression.

@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch from 45bfb0e to 15758a1 Compare February 26, 2026 14:07
@arajkumar arajkumar changed the title Enable chunk exclusion for UPDATE/DELETE during planning on PG18+ Enable chunk exclusion for DML on PG18+ Feb 26, 2026
@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch 5 times, most recently from 2e19fc6 to 2981e5d Compare February 27, 2026 13:25
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 92.52336% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/import/ts_inherit.c 91.37% 3 Missing and 2 partials ⚠️
src/planner/expand_hypertable.c 90.32% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch 2 times, most recently from 5f34e38 to 4773fb5 Compare February 27, 2026 13:50
@arajkumar arajkumar changed the title Enable chunk exclusion for DML on PG18+ Enable chunk exclusion for DML Feb 27, 2026
@arajkumar arajkumar marked this pull request as ready for review February 27, 2026 15:11
@arajkumar arajkumar requested a review from a team February 27, 2026 15:11
Comment on lines +439 to +447
{
if (IS_UPDL_CMD(context->rootquery))
{
if (rti == (Index) query->resultRelation)
rte_mark_for_expansion(rte);
}
else if (query->resultRelation == 0)
rte_mark_for_expansion(rte);
}
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.

Let's explain this new logic in the comments (i.e. what every branch condition means and why we handle only these cases and not others).

Node *childvar = (Node *) lfirst(childvars);

if (IsA(parentvar, Var))
/* Only process Vars that belong to the parent rel */
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 seems like a general change not related to update/delete specifically -- why did it become necessary? The check below should probably filter out the different relations already.

Copy link
Copy Markdown
Member Author

@arajkumar arajkumar Feb 28, 2026

Choose a reason for hiding this comment

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

It is a direct port from upstream allpaths.c. https://github.com/postgres/postgres/blob/c2c1962a64b547412c88fa2728e4fa35e65f4c90/src/backend/optimizer/path/allpaths.c#L1193

Without this fix, The tests failing with the "WARNING: problem in alloc set MessageContext: req size > alloc size for chunk" in Debug mode PG18.2 with --enable-cassert:

  1. cagg_invalidation — UPDATE on inval_update table triggered the warning
  2. cagg_watermark — also showed the warning during UPDATE/DELETE
  3. triggers — also showed the warning during UPDATE/DELETE

Here is a simple test reproducing the above warning without the fix on debug builds

CREATE EXTENSION timescaledb;

DROP TABLE IF EXISTS transition_test;
  CREATE OR REPLACE FUNCTION test_trigger() RETURNS TRIGGER AS $$
  BEGIN RAISE WARNING 'trigger fired'; RETURN NULL; END;
  $$ LANGUAGE plpgsql;

  CREATE TABLE transition_test(time timestamptz NOT NULL);
  SELECT create_hypertable('transition_test','time');

  INSERT INTO transition_test VALUES ('2020-01-10');

  -- These trigger "problem in alloc set MessageContext: req size > alloc size"
  UPDATE transition_test SET time = '2020-01-12' WHERE time = '2020-01-11';

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.

Oh, it's for the ROWID_VAR = -4. I think it only reproduces with update/delete/merge, this is why we're not hitting it now. I'm not sure, maybe it makes sense to update the entire function to the latest PG version? Just to have a consistent shapshot and make sure we're not missing anything when changing it piece-wise.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, now I synched the latest code.

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.

Let's update it separately. I'd like to move in small steps to see precisely when we're breaking something. I'll do it myself here: #9354

}
}

if (ts >= TS_TIMESTAMP_END)
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.

Here too -- general change not immediately related to update/delete, is this something we're missing on the select paths too?

Copy link
Copy Markdown
Member Author

@arajkumar arajkumar Feb 28, 2026

Choose a reason for hiding this comment

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

Without this fix, extreme date/timestamp values (e.g. year 294247) would error instead of clamping to INT64_MIN/MAX during chunk exclusion.

cagg_union_view was the failing test. It inserts '294247-01-01' into timestamp/timestamptz hypertables, then CALL refresh_continuous_aggregate('timestamp_agg', NULL, NULL) errors with "timestamp out of
range".

DELETE FROM test_large_ts WHERE time < '294247-01-02' simple DELETE with an overflowing timestamp in the WHERE clause reproduces ERROR: timestamp out of range.

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.

Let's try to reproduce this with pure SELECT and fix separately then. This PR touches a very important part of code, so it would be good to keep all independent changes out of it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sure, I could reproduce with pure SELECT too. PR is on the way! Thank you!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

/* Parent is not a leaf result rel; children are. */
root->leaf_result_relids = bms_del_member(root->leaf_result_relids, rti);

foreach (lc, appinfos)
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.

At this point, we're basically reimplementing all of the pieces of expand_single_inheritance_child, but in different places. Can we make this more straightforward and use this function entirely?

Copy link
Copy Markdown
Member Author

@arajkumar arajkumar Feb 28, 2026

Choose a reason for hiding this comment

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

@akuzm expand_single_inheritance_child is a static function. Do you mean to copy into our code and use it?

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.

Yes. This is a maintainability problem, but sometimes we have do this for static functions. We try to maintain a pure copy of the PG code w/o any changes, so that it's easier to keep up to date.

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.

It should go somewhere to the "import" dir like the similar ones.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@akuzm expand_single_inheritance_child is imported into src/import/ts_inherit.c as ts_expand_single_inheritance_child.

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.

I'm going to try to merge this separately too: #9373

@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch 4 times, most recently from ac13d22 to 8d5de8d Compare March 2, 2026 10:28
@arajkumar arajkumar requested a review from akuzm March 2, 2026 10:28
@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch from 8d5de8d to 0e3214f Compare March 2, 2026 10:34
@akuzm
Copy link
Copy Markdown
Member

akuzm commented Mar 2, 2026

Another thing that is relevant for this PR: while looking into it, I noticed that row mark handling is not implemented in our expansion. This is expected, since at the moment it's not used if there are row marks. The only exception was expansion for FKs, and I'm working on a fix for this: #9324

Given that you're not hitting test failures with this, probably the test coverage is missing -- can you add some tests for this? This case is pretty important. I'd expect the tests to fail w/o my fix and pass with it.

@akuzm
Copy link
Copy Markdown
Member

akuzm commented Mar 2, 2026

Another thing that is relevant for this PR: while looking into it, I noticed that row mark handling is not implemented in our expansion. This is expected, since at the moment it's not used if there are row marks. The only exception was expansion for FKs, and I'm working on a fix for this: #9324

Given that you're not hitting test failures with this, probably the test coverage is missing -- can you add some tests for this? This case is pretty important. I'd expect the tests to fail w/o my fix and pass with it.

Thinking more about this, it seems to be a different thing. Row marks are not used on the DML target relation.

Postgres's standard inheritance expansion opens every chunk of a
hypertable during UPDATE/DELETE planning, which causes severe
performance degradation as chunk count grows. This patch routes
UPDATE/DELETE through TimescaleDB's existing chunk exclusion code
(HypertableRestrictInfo), expanding only chunks that match the
query's WHERE clause — the same optimization already used for SELECT.

The key change is in planner.c's RTE marking logic: UPDATE/DELETE
target relations are now marked for TimescaleDB's custom expansion
(via `rte_mark_for_expansion`), whereas previously they were left to
Postgres. MERGE is intentionally excluded since our custom expansion
only handles UPDATE/DELETE DML fixups.

The per-chunk expansion in expand_hypertable.c is refactored to use
a copied `expand_single_inheritance_child` (from PG's inherit.c),
which handles RTE construction, AppendRelInfo creation, result relation
registration, and row identity setup — replacing ~60 lines of
hand-rolled equivalent code.

Also fixes two pre-existing bugs exposed by this change:
- ts_set_append_rel_size: missing varno check in width estimation
  caused out-of-bounds writes when ROWID_VAR entries were present
  in the parent rel's reltarget. Ported from upstream PG.
- ts_time_value_to_internal_or_infinite: extreme date/timestamp
  values would error instead of clamping to INT64_MIN/MAX.

Signed-off-by: Arunprasad Rajkumar <ar.arunprasad@gmail.com>
@akuzm
Copy link
Copy Markdown
Member

akuzm commented Mar 2, 2026

@arajkumar
Copy link
Copy Markdown
Member Author

@arajkumar
Copy link
Copy Markdown
Member Author

arajkumar commented Mar 3, 2026

Left-side: 2.25.1
Right-side: 2.26.0-dev + fix

➜  tsbench git:(main) ✗ python3 -m src.tsbench \
    --with-connection "postgresql://postgres@:5433/postgres?host=/var/run/postgresql" \
    --with-connection reuse-and-pause \
    --benchmarks "hypertable_update_delete" "hypercore_uncompressed_update_delete" \
    --no-requires
*** Processing benchmarks on connection: postgresql://postgres@:5433/postgres?host=/var/run/postgresql
*** Executing benchmark 'hypercore_uncompressed_update_delete'
*** Executing benchmark 'hypertable_update_delete'
Reusing connection, please make the needed adjustments and press enter...
*** Processing benchmarks on connection: postgresql://postgres@:5433/postgres?host=/var/run/postgresql
*** Executing benchmark 'hypercore_uncompressed_update_delete'
*** Executing benchmark 'hypertable_update_delete'
*** All benchmarks are executed - done
============================================
Report for benchmark suite 'hypercore_uncompressed_update_delete'
+-----------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+------------------------------------------+
| Query                                                                                                                             | 62885bd90795d4da86cc4d2e2bb64c54e78fdec3 | e288dc0f987e796779483add460da4c5ae2510ee |
+-----------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+------------------------------------------+
| DEALLOCATE ALL;                                                                                                                   |                                     0.07 |                                     0.20 |
| PREPARE up_hc AS UPDATE hypercore_update_delete SET value = 99.9, name = 'updated' WHERE id = $1::int AND time = $2::timestamptz; |                                     0.10 |                                     0.09 |
| EXECUTE up_hc(0, '2025-01-15 00:00:00+00');                                                                                       |                                    34.68 |                                     0.34 |
| EXECUTE up_hc(0, '2025-01-15 01:00:00+00');                                                                                       |                                    33.15 |                                     0.35 |
| EXECUTE up_hc(0, '2025-01-15 02:00:00+00');                                                                                       |                                    32.86 |                                     0.34 |
| EXECUTE up_hc(0, '2025-01-15 03:00:00+00');                                                                                       |                                    32.77 |                                     0.33 |
| EXECUTE up_hc(0, '2025-01-15 04:00:00+00');                                                                                       |                                    32.28 |                                     0.34 |
| DEALLOCATE ALL;                                                                                                                   |                                     0.06 |                                     0.20 |
| PREPARE up_hc AS UPDATE hypercore_update_delete SET value = 99.9, name = 'updated' WHERE id = $1::int AND time = $2::timestamptz; |                                     0.09 |                                     0.09 |
| EXECUTE up_hc(0, '2025-01-20 00:00:00+00');                                                                                       |                                    34.88 |                                     0.36 |
| EXECUTE up_hc(1, '2025-01-20 00:00:01+00');                                                                                       |                                    32.75 |                                     0.31 |
| EXECUTE up_hc(2, '2025-01-20 00:00:02+00');                                                                                       |                                     5.55 |                                     0.27 |
| EXECUTE up_hc(3, '2025-01-20 00:00:03+00');                                                                                       |                                     4.99 |                                     0.27 |
| EXECUTE up_hc(4, '2025-01-20 00:00:04+00');                                                                                       |                                     4.78 |                                     0.25 |
| DEALLOCATE ALL;                                                                                                                   |                                     0.08 |                                     0.18 |
| PREPARE del_hc AS DELETE FROM hypercore_update_delete WHERE id = $1::int AND time = $2::timestamptz;                              |                                     0.10 |                                     0.08 |
| EXECUTE del_hc(0, '2025-01-15 00:00:00+00');                                                                                      |                                    34.94 |                                     0.22 |
| EXECUTE del_hc(0, '2025-01-15 01:00:00+00');                                                                                      |                                    32.93 |                                     0.21 |
| EXECUTE del_hc(0, '2025-01-15 02:00:00+00');                                                                                      |                                    32.96 |                                     0.21 |
| EXECUTE del_hc(0, '2025-01-15 03:00:00+00');                                                                                      |                                    32.92 |                                     0.21 |
| EXECUTE del_hc(0, '2025-01-15 04:00:00+00');                                                                                      |                                    32.71 |                                     0.21 |
| DEALLOCATE ALL;                                                                                                                   |                                     0.17 |                                     0.18 |
| PREPARE del_hc AS DELETE FROM hypercore_update_delete WHERE id = $1::int AND time = $2::timestamptz;                              |                                     0.09 |                                     0.09 |
| EXECUTE del_hc(0, '2025-01-20 00:00:00+00');                                                                                      |                                    34.94 |                                     0.24 |
| EXECUTE del_hc(1, '2025-01-20 00:00:01+00');                                                                                      |                                    33.10 |                                     0.22 |
| EXECUTE del_hc(2, '2025-01-20 00:00:02+00');                                                                                      |                                     5.73 |                                     0.23 |
| EXECUTE del_hc(3, '2025-01-20 00:00:03+00');                                                                                      |                                     4.80 |                                     0.23 |
| EXECUTE del_hc(4, '2025-01-20 00:00:04+00');                                                                                      |                                     4.64 |                                     0.22 |
+-----------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+------------------------------------------+
Report for benchmark suite 'hypertable_update_delete'
+------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+------------------------------------------+
| Query                                                                                                                              | 62885bd90795d4da86cc4d2e2bb64c54e78fdec3 | e288dc0f987e796779483add460da4c5ae2510ee |
+------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+------------------------------------------+
| DEALLOCATE ALL;                                                                                                                    |                                     0.06 |                                     0.04 |
| PREPARE up_ht AS UPDATE hypertable_update_delete SET value = 99.9, name = 'updated' WHERE id = $1::int AND time = $2::timestamptz; |                                     0.09 |                                     0.09 |
| EXECUTE up_ht(0, '2025-01-15 00:00:00+00');                                                                                        |                                     4.86 |                                     0.23 |
| EXECUTE up_ht(0, '2025-01-15 01:00:00+00');                                                                                        |                                     4.48 |                                     0.21 |
| EXECUTE up_ht(0, '2025-01-15 02:00:00+00');                                                                                        |                                     4.51 |                                     0.20 |
| EXECUTE up_ht(0, '2025-01-15 03:00:00+00');                                                                                        |                                     4.48 |                                     0.22 |
| EXECUTE up_ht(0, '2025-01-15 04:00:00+00');                                                                                        |                                     4.49 |                                     0.22 |
| DEALLOCATE ALL;                                                                                                                    |                                     0.05 |                                     0.04 |
| PREPARE up_ht AS UPDATE hypertable_update_delete SET value = 99.9, name = 'updated' WHERE id = $1::int AND time = $2::timestamptz; |                                     0.09 |                                     0.09 |
| EXECUTE up_ht(0, '2025-01-20 00:00:00+00');                                                                                        |                                     5.05 |                                     0.21 |
| EXECUTE up_ht(1, '2025-01-20 00:00:01+00');                                                                                        |                                     4.59 |                                     0.20 |
| EXECUTE up_ht(2, '2025-01-20 00:00:02+00');                                                                                        |                                     4.59 |                                     0.19 |
| EXECUTE up_ht(3, '2025-01-20 00:00:03+00');                                                                                        |                                     4.55 |                                     0.19 |
| EXECUTE up_ht(4, '2025-01-20 00:00:04+00');                                                                                        |                                     4.56 |                                     0.19 |
| DEALLOCATE ALL;                                                                                                                    |                                     0.06 |                                     0.04 |
| PREPARE del_ht AS DELETE FROM hypertable_update_delete WHERE id = $1::int AND time = $2::timestamptz;                              |                                     0.08 |                                     0.07 |
| EXECUTE del_ht(0, '2025-01-15 00:00:00+00');                                                                                       |                                     4.86 |                                     0.22 |
| EXECUTE del_ht(0, '2025-01-15 01:00:00+00');                                                                                       |                                     4.47 |                                     0.19 |
| EXECUTE del_ht(0, '2025-01-15 02:00:00+00');                                                                                       |                                     4.48 |                                     0.18 |
| EXECUTE del_ht(0, '2025-01-15 03:00:00+00');                                                                                       |                                     4.46 |                                     0.18 |
| EXECUTE del_ht(0, '2025-01-15 04:00:00+00');                                                                                       |                                     4.44 |                                     0.18 |
| DEALLOCATE ALL;                                                                                                                    |                                     0.05 |                                     0.04 |
| PREPARE del_ht AS DELETE FROM hypertable_update_delete WHERE id = $1::int AND time = $2::timestamptz;                              |                                     0.08 |                                     0.08 |
| EXECUTE del_ht(0, '2025-01-20 00:00:00+00');                                                                                       |                                     5.20 |                                     0.20 |
| EXECUTE del_ht(1, '2025-01-20 00:00:01+00');                                                                                       |                                     4.69 |                                     0.19 |
| EXECUTE del_ht(2, '2025-01-20 00:00:02+00');                                                                                       |                                     4.65 |                                     0.19 |
| EXECUTE del_ht(3, '2025-01-20 00:00:03+00');                                                                                       |                                     4.61 |                                     0.19 |
| EXECUTE del_ht(4, '2025-01-20 00:00:04+00');                                                                                       |                                     4.65 |                                     0.18 |
+------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------+---
---------------------------------------+
============================================

Comment on lines +446 to +448
* MERGE is not marked: our custom expansion in
* expand_hypertable.c only handles UPDATE/DELETE
* DML fixups. MERGE is left to PG's standard
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.

Should we enable it for MERGE too, or are there more problems with it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

MERGE can also lead to INSERTS right? AFAIK, we have separate path for INSERT and I'm not really sure how to wire those together now.

I would rather simply focus on the UPDATE and DELETE in this PR.

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.

I'm not familiar with the details. The comment sounds a little confusing like this, I think it would be more straightforward if it mentioned some actual reason or just didn't mention it at all.

@arajkumar arajkumar changed the title Enable chunk exclusion for DML Improve hypertable UPDATE/DELETE performance with chunk exclusion Mar 4, 2026
@arajkumar arajkumar changed the title Improve hypertable UPDATE/DELETE performance with chunk exclusion Improve hypertable DML performance with chunk exclusion Mar 4, 2026
@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch 3 times, most recently from 130c308 to 9ba1b77 Compare March 17, 2026 07:42
@arajkumar arajkumar force-pushed the arajkumar/fix-ht-update-delete-slowness branch from 7cb0964 to a30845e Compare April 1, 2026 19:57
@arajkumar arajkumar requested a review from akuzm April 2, 2026 06:19
Signed-off-by: Arunprasad Rajkumar <ar.arunprasad@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants