---
title: "TimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning"
published: 2026-06-09T07:42:00.000-04:00
updated: 2026-06-09T08:44:23.000-04:00
excerpt: "Up to 160x more efficient UPDATE/DELETE, 30% to 2x faster vectorized execution, and skip unnecessary decompression + compression. Here's what's new in TimescaleDB 2.27."
tags: Announcements & Releases, TimescaleDB
authors: Brandon Purcell
---

> **TimescaleDB is now Tiger Data.**

## Introduction

One of the biggest challenges in analytical databases is avoiding unnecessary work.

As datasets grow, performance increasingly depends on how efficiently the database can eliminate data, batches, and operations that don't need to be processed. Every batch skipped is less CPU, less I/O, and faster query execution.

[TimescaleDB 2.26](https://www.tigerdata.com/blog/timescaledb-2-26) introduced composite bloom filters for compressed data reads. In 2.27, we're extending that work significantly. Bloom filters now accelerate UPDATE, DELETE, and UPSERT operations on compressed data. [Hypercore](https://www.tigerdata.com/docs/learn/columnar-storage/understand-hypercore) expands vectorized execution to cover more query patterns. Continuous aggregates can now refresh and compress data in a single policy execution. And direct compression becomes smarter with automatic `segmentby` selection and more resilient policy execution.

The result is a release focused on making compressed data faster, more efficient, and easier to operate at scale.

## TL;DR

-   **Up to 160x more efficient UPDATE and DELETE on compressed data (**[**PR #9399**](https://github.com/timescale/timescaledb/pull/9399)**):** Bloom filters can now prune compressed batches before decompression during UPDATE and DELETE operations. New EXPLAIN counters show how much work was skipped.
-   **Smarter UPSERT pruning (**[**PR #9374**](https://github.com/timescale/timescaledb/pull/9374)**):** Composite bloom filters now accelerate conflict detection for INSERT ... ON CONFLICT workloads on compressed hypertables. EXPLAIN adds four new visibility stats: batches checked by bloom, batches pruned by bloom, batches without bloom, and bloom false positives.
-   **30% to 2x faster vectorized execution in Hypercore (**[**PR #9443**](https://github.com/timescale/timescaledb/pull/9443)**):** More filter expressions remain in the vectorized execution path, including common patterns used during continuous aggregate refreshes.
-   **Continuous aggregate refresh and compression in one policy:** New `compress_after_refresh` support allows refresh and compression to run together, simplifying operations and reducing policy overhead.
-   **Smarter direct compression:** Automatic `segmentby` selection and partial-success compression policies improve efficiency and reliability for columnstore workloads.

## Vectorized filter evaluation in the Hypercore engine

The Hypercore columnstore engine processes data in batches rather than row by row. The goal is to keep queries in that vectorized path end-to-end. When a filter expression falls out of the columnar pipeline, the engine reverts to row-based processing and the efficiency advantage shrinks.

This was a problem for certain filter expressions, including those used in [continuous aggregate](https://www.tigerdata.com/docs/reference/timescaledb/continuous-aggregates) refreshes. Those filters couldn't be evaluated inline within the vectorized path. They dropped out of the columnar pipeline and were handled through the standard Postgres function path, adding overhead proportional to data volume. For workloads that refresh aggregates frequently, that overhead adds up.

In 2.27, the Hypercore engine evaluates these filters inline through the standard Postgres function path directly within the columnstore pipeline. More queries complete entirely in the vectorized path without any changes to queries or schemas.

```SQL
-- Example: continuous aggregate refresh that now stays in the vectorized path
CALL refresh_continuous_aggregate('hourly_metrics', '2024-01-01', '2024-02-01');
```

In benchmarks, affected workloads show improvements ranging from **30% to 2x**, depending on filter selectivity and data distribution. Queries that previously fell back to row-based processing for filter evaluation now complete the full execution in the columnstore.

## Bloom filters for UPDATE and DELETE

Here's the assumption most databases make: if you want to UPDATE or DELETE rows in compressed data, you decompress the batch first, then look for what you need. Every batch. Even the ones that can't possibly contain a match.

That's a lot of work to throw away.

In 2.27, TimescaleDB uses bloom filters to check each compressed batch before decompression. If the bloom filter can say with certainty that the value you're looking for isn't in this batch, the batch is skipped entirely. When multiple bloom filters apply, they are evaluated in decreasing order of column count, so the most selective filter runs first.

![TimescaleDB UPDATE/DELETE Processing Comparison - Diagram](https://storage.ghost.io/c/6b/cb/6bcb39cf-9421-4bd1-9c9d-fa7b6755ba0e/content/images/2026/06/timescaledb-2.27-update-delete-processing-diagram.svg)

```SQL
-- Example: DELETE with equality predicate on compressed hypertable
DELETE FROM sensor_data
WHERE device_id = 42 AND metric = 'temperature';
```

EXPLAIN output now includes two new counters that make the pruning activity visible:

```SQL
Custom Scan (DecompressChunk)
  Compressed batches filtered: 847
  Batches filtered after decompression: 12
  ->  Seq Scan on _compressed_hypertable_chunk
```

Those 847 batches were never decompressed. The 160x figure reflects end-to-end query execution time. On benchmarks workloads, queries dropped from 820ms to 4.4ms. The gains are largest when equality predicates are highly selective and multiple columns are combined, where composite bloom filters can rule out most batches before decompression. They provide less benefit when TimescaleDB can already skip work through `segmentby` or min/max metadata.

For most workloads, no configuration is required. TimescaleDB creates bloom filters automatically from existing PostgreSQL indexes. If you frequently filter on columns not covered by an index, you can add bloom filters manually.

Note that bloom filters apply to newly compressed data. Existing compressed chunks require recompression to gain them. We are actively working on making this easier in future releases.

## Bloom Filters for UPSERT

INSERT ... ON CONFLICT requires the database to check for conflicting rows before deciding whether to insert or update. On compressed hypertables, that conflict check previously required decompressing batches to evaluate the arbiter predicate, even when the arbiter values were clearly not present.

In 2.27, when the values being checked for a conflict cannot be present in a compressed batch, TimescaleDB can eliminate that batch immediately without decompression. This is especially effective for UPSERT workloads that rely on multi-column conflict keys, where composite bloom filters provide a more precise pruning signal than any individual column alone. When multiple filters apply, the most selective is chosen automatically.

EXPLAIN adds four new statistics so you can see exactly what the optimizer is doing:

```SQL
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
INSERT INTO sensor_data VALUES ('2024-01-01 00:05:30', 5, 'temp', 100)
ON CONFLICT (device_id, metric, ts) DO UPDATE SET value = EXCLUDED.value;
```

```SQL
Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1)
  Batches checked by bloom: 10
  Batches pruned by bloom: 8
  Batches without bloom: 2
  Bloom false positives: 0
  ->  Insert on sensor_data (actual rows=0.00 loops=1)
        Conflict Resolution: UPDATE
        Conflict Arbiter Indexes: idx_sensor_data
        Tuples Inserted: 1
        Conflicting Tuples: 0
        ->  Result (actual rows=1.00 loops=1)
```

With this release, bloom filter pruning now spans the major read and write paths on compressed data. Queries, conflict detection, updates, and deletes can all use compressed metadata to avoid unnecessary decompression, extending the same "skip work whenever possible" philosophy across the columnstore.

## Continuous Aggregates: Refresh and compress in a single policy

One common pattern with continuous aggregates is refreshing data and then running a separate compression policy against the same materialization hypertable. While effective, coordinating these jobs can be challenging. Compression often needs to wait until refreshes complete; otherwise, the jobs can compete for the same chunks, causing lock contention, retries, or failed policy executions.

In TimescaleDB 2.27, continuous aggregate refresh policies can now optionally compress data immediately after a refresh completes. A new `compress_after_refresh` configuration option allows refresh and compression to run together as part of a single policy execution.

```SQL
SELECT add_continuous_aggregate_policy(
  continuous_aggregate => 'metrics_hourly',
  start_offset => INTERVAL '30 days',
  end_offset   => INTERVAL '1 hour',
  schedule_interval => INTERVAL '1 hour',
  config => '{"compress_after_refresh": true}'::jsonb
);
```

This helps simplify continuous aggregate management by reducing the number of separate jobs required to maintain compressed materializations. The behavior is opt-in and only applies when refreshes are executed through a policy. Manual calls to `refresh_continuous_aggregate()` continue to behave as before.

## Smarter direct compression and more resilient compression policies

When you run direct compression without configuring a `segmentby` column, compression still works, but queries against that compressed data may perform worse. The `segmentby` column determines how the columnstore organizes data, and choosing the wrong one, or none at all, affects how efficiently the database can read it back later.

In 2.27, TimescaleDB analyzes your data during the direct compress operation and automatically selects an appropriate `segmentby` column when one isn't explicitly configured. It's enabled by default and requires no schema changes.

#### **More resilient compression policies**

Previously, a single chunk error could take down an entire compression job: a silent failure, a gap in your compressed data, and a retry that may hit the same problem again.

In 2.27, compression jobs no longer fail entirely when some chunks error. They report success with warnings, isolating the failure to the affected chunks. An exception is only raised when no chunks successfully compress during the run.

Together, these improvements make direct compression more adaptive to real-world workloads while reducing operational overhead for large deployments.

## Also in TimescaleDB 2.27

-   **PostgreSQL 15 deprecation notice:** TimescaleDB 2.28 (June 2026) will be the last release to support PostgreSQL 15. Users on PG15 should plan to upgrade to PG16, PG17, or PG18 before the 2.29 release in July 2026.
-   Bug fixes and stability improvements across continuous aggregates, replication, and query correctness. See the [full release notes](https://github.com/timescale/timescaledb/releases/tag/2.27.0) for details.

## Upgrade to TimescaleDB 2.27 today

If you’re an existing Tiger Data (creators of TimescaleDB) customer, you can experience all these improvements on Tiger Cloud today. For customers running production workloads leveraging the columnstore, these improvements reduce CPU consumption, lower storage engine overhead, and improve performance consistency as data volumes grow. The larger the dataset, the more valuable pruning becomes.

To learn more, check out the [full release notes](https://github.com/timescale/timescaledb/releases/tag/2.27.0) for a complete list of improvements, or [_try Tiger Cloud for free_](https://console.cloud.tigerdata.com/signup) _and experience TimescaleDB 2.27 on your largest hypertables_. We welcome your feedback on [GitHub](https://github.com/timescale/timescaledb).