Category: All posts
Aug 06, 2025
Posted by
Brandon Purcell
We're thrilled to announce TimescaleDB 2.21, a release that continues our relentless pursuit of making Tiger Postgres the fastest PostgreSQL for time-series and real-time analytical workloads. Building on the foundational improvements of 2.20, this version delivers more performance enhancements and crucial advancements in usability, all while preserving the flexibility, full SQL capabilities, and rich ecosystem you expect from PostgreSQL.
At TigerData, our grand ambition is to build the fastest Postgres, driven by exponential data growth and sophisticated customer-facing apps that require real-time analytics and AI capabilities. To achieve this, we remain laser-focused on two core areas: performance and usability.
TimescaleDB 2.21 continues to build on these themes, further improving performance while enhancing the experience for our users. Get ready for impressive speed-ups and powerful enhancements that help you move faster and scale smarter.
TimescaleDB 2.21 makes PostgreSQL for demanding applications and real-time analytics even faster and simpler to use. Key highlights include:
With the TL;DR out of the way, let's deep-dive into everything 2.21 has to offer!
For high-throughput, real-time analytical applications such as observability platforms, IoT systems, or financial data pipelines, efficient ingestion is critical. These workloads generate massive volumes of data that need to be stored and queried with minimal delay. To support these use cases, TimescaleDB 2.21 introduces a new ingestion path: Direct to Columnstore. This feature allows data to be ingested directly into the optimized columnar format, reducing unnecessary I/O and enabling significantly higher throughput.
In most databases, including PostgreSQL, ingestion performance is typically limited by I/O throughput. Each insert operation writes to multiple places on disk: the heap (rowstore), indexes, and the write-ahead log (WAL). At high ingest rates, this disk activity becomes a major bottleneck. By default, TimescaleDB’s columnstore required data to be ingested into the rowstore first, then later compressed and moved to the columnstore by a background policy. This two-step process requires additional I/O overhead.
Direct-to-columnstore eliminates that extra step. Instead of writing to the rowstore and compressing later, data is immediately written to the optimized columnstore during ingestion. This bypasses redundant I/O and index maintenance.
Our goal is to make ingestion faster so users can scale to higher ingest rates without hitting I/O limits. Direct-to-columnstore reduces the disk activity required during ingestion, which lowers system overhead and simplifies operations. In our internal testing, this approach reduced I/O by up to 80 percent and enabled ingestion rates up to 9,000 times faster than the default path, with tested bursts reaching 100 million rows per second. By making ingestion CPU-bound instead of I/O-bound, it allows users to handle larger volumes of real-time data with less hardware and fewer tuning requirements, all while still benefiting from the compression and query performance advantages of columnar storage.
Key benefits:
We believe these improvements to ingestion will be a game-changer for high throughput TimescaleDB users, but it's important to note that within 2.21 it’s a tech preview feature. This means it's a significant step in an ongoing journey that will take time to fully mature for all use cases.
Current limitations for 2.21:
COPY
operations. Support for INSERT
statements is planned for an upcoming release.timescaledb.enable_direct_compress_copy_client_sorted
(default: off) that, if incorrectly set (e.g., guaranteeing data order when it's not actually ordered), can lead to inaccurate query results. We advise users to consult with our team on the best approaches to leverage this feature. It only takes a few steps to start ingesting directly into the columnstore. Below is a simple example using CREATE TABLE
, enabling the feature with a GUC, and loading data using COPY
. For additional details check out our documentation and we will be launching a blog article that will dive much deeper into this topic.
tsdb=> CREATE TABLE t(time timestamptz, device text, value float) WITH (tsdb.hypertable,tsdb.partition_column='time');
CREATE TABLE
Time: 17.940 ms
tsdb=> SET timescaledb.enable_direct_compress_copy;
SET
Time: 1.103 ms
-- binary format will achieve highest insert rate
-- but csv and text format are supported as well
tsdb=> COPY t FROM '/tmp/t.binary' WITH (format binary);
COPY 10000000
Time: 0.730 ms
We are actively seeking design partners to test this feature in real-world environments. Ideal partners are those experiencing ingestion bottlenecks, have strong control over their data pipelines (can reliably batch and order data consistently), and require real-time or very fast access to freshly ingested data. If this sounds like you, please reach out to our team!
TimescaleDB 2.21 delivers significant improvements to how you manage and optimize your data within the columnstore, making common operations faster and more efficient.
Backfilling or updating data into compressed columnstore chunks has been a pain point for some users. TimescaleDB 2.20 brought a massive 10x improvement, and 2.21 continues this momentum. For heavily constrained UPSERT
or ON CONFLICT
operations, performance is now 2x times faster than in 2.20.
Consider a worst-case scenario backfill of 3,000 records where every record conflicts with an existing unique constraint:
INSERT INTO uk_ts_comp_table
SELECT * FROM insert_sample on conflict (uk,ts) do nothing;
TimescaleDB 2.19: ~17 seconds
TimescaleDB 2.20: ~1.7 seconds
TimescaleDB 2.21: ~.7 seconds (a 20x improvement!)
This brings UPSERT performance for append-only workloads almost on par with rowstore chunks, truly delivering speed without sacrifice.
Deleting records from non-segmentby columns previously required decompressing batches to validate conditions, an expensive operation that also generated bloat. TimescaleDB 2.21 introduces a new optimization that validates if a whole batch can be deleted at once based on the given conditions.
This results in:
With recent architectural changes to improve read/write during recompression, a trade-off was made to use DELETE
over TRUNCATE
, which could lead to bloat (deleted records occupying storage space until garbage collected). In 2.21, we implemented a smarter way to reduce this bloat. Customers will notice this improvement after larger recompression operations, resulting in less disk usage and more efficient storage.
TimescaleDB 2.20 introduced the ability to split rowstore chunks. In 2.21, we completed this feature by adding support for splitting columnstore chunks. This provides full flexibility to manage your data, complementing our existing merge capabilities for both columnstore and rowstore data. This is particularly useful for fixing chunk interval configuration mistakes or managing very large chunks.
Continuous Aggregates (CAggs) are a cornerstone of managing and querying large time-series datasets efficiently. TimescaleDB 2.21 introduces more flexibility and power to how you define and manage them, making them even more robust for real-time analytics.
Previously, refreshing Continuous Aggregates could involve locking the materialization hypertable, preventing concurrent executions. This often led to customers creating complex workarounds when they had multiple CAggs against the same hypertable.
TimescaleDB 2.21 lifts this limitation by introducing an improved locking approach and smart decision logic. Now, multiple refresh policies can run concurrently, refreshing only ranges that are not overlapping with other running policies.
For example, in a multi-tenant application, a new customer onboarding might involve loading both historic data and real-time data. With parallel refresh policies, separate policies for historic and real-time data can run concurrently, ensuring faster materialization and improved data availability without conflicts. This is a significant win for use cases requiring faster materialization. The example below illustrates this.
-- Refresh policy for recent data (last 24 h)
SELECT add_continuous_aggregate_policy('conditions_summary_daily',
start_offset => INTERVAL '1 day',
end_offset => INTERVAL '1 h',
schedule_interval => INTERVAL '1 h');
---- Refresh policy for historical data (older than 24 h)
SELECT add_continuous_aggregate_policy('conditions_summary_daily',
start_offset => NULL
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 hour');
Starting in 2.19 you could opt-in to batched refreshes, which slice a continuous-aggregate refresh into smaller pieces so results appear sooner and large jobs don’t monopolize CPU, memory, or I/O. In 2.21 this is now on by default:
buckets_per_batch = 1
How it works: each batch processes one time bucket. Raising the value refreshes more buckets per batch but uses more resources. Lowering it keeps resource usage minimal at the cost of more batches.
max_batches_per_execution = 0
(unlimited).
How it works: controls how many batches a single policy run will execute. Setting a positive number caps work per run; zero lets the job finish all pending batches.
With these defaults, even very large Continuous Aggregates refresh in bite-sized chunks, keeping the system responsive while making new data visible quickly. The next step is to have TimescaleDB pick these values automatically based on the size of the refresh window and recent workload patterns.
Beyond the features, we are accelerating our release schedule and have new recommendations on best practices.
The "25% rule" for chunk sizing, which previously advised setting chunk size to approximately 25% of system memory based on total chunk size, is outdated. We've evolved this metric. We are now advising users to reference the index size on the chunks with reference to 25% of memory for uncompressed chunks. This change is already reflected in our documentation, and the Tiger Cloud Console now displays index size alongside chunk size for your reference. This ensures more accurate guidance for optimal memory utilization and query performance. You can find more details in our updated documentation.
To accelerate our pace of innovation and deliver value to you faster, TimescaleDB will be changing its release cadence. Starting with the 2.22 release in September, we will shift from an 8-week minor release cycle to a 4-week minor release cycle, with one planned patch release two weeks after each minor release. This "always be launching" approach allows us to get new features into your hands more quickly..
As announced with TimescaleDB 2.19.0, version 2.20 dropped support for PostgreSQL 14. TimescaleDB 2.21 is compatible with PostgreSQL 15, 16, and 17. We recommend users aim for the latest PostgreSQL versions to benefit from their own performance and security enhancements.
For Tiger Cloud customers still on PostgreSQL 13 or 14, a forced upgrade to PostgreSQL 15+ is planned for mid-September. Please plan your PostgreSQL upgrades accordingly to take advantage of the latest TimescaleDB features.
TimescaleDB 2.21 marks another significant leap forward, reinforcing its position as the fastest PostgreSQL for time-series and real-time analytics workloads. We've delivered substantial speed improvements for ingestion, data management, and continuous aggregates, all while maintaining the robust capabilities, full SQL, and developer experience you expect from PostgreSQL.
For Tiger Cloud users on PostgreSQL 15 and above, these enhancements are already live and available. Self-hosted users can upgrade now to explore these benefits firsthand.
Check out our release notes for a full list of improvements and bug fixes. As always, we welcome your feedback in our Community Forum or on GitHub.
About the author:
Brandon Purcell is a Staff Product Manager at TigerData, where he leads the Database team with a focus on delivering a world-class experience out of the box for developers using the Tiger Postgres platform, both in the cloud and open source. His work centers on improving core database functionality and helping customers unlock value from their time-series, event, and real-time analytics workloads.
Prior to joining TigerData, Brandon was responsible for leading Product Development within the Analytics Product Development group at Insurity, overseeing Geospatial, Predictive, and Data products. He was also a co-founder and CTO of SpatialKey, a geospatial analytics platform acquired by Insurity in 2020. At SpatialKey, he guided the company’s evolution from a general-purpose geospatial tool to a vertically focused insurance analytics product supporting underwriting, event response, and portfolio management.
With over two decades of experience leading globally distributed teams, Brandon’s career began at Macromedia and Adobe, where he supported mobile and developer tooling programs. Earlier still, he served as a Communications Officer in the U.S. Army, where he authored TACWEB, one of the Army’s first web-based command and control systems. Brandon holds a degree in Electrical Engineering from Southern Illinois University and lives in upstate New York.