rent
PostgreSQL extensions

TimescaleDB

What it is

TimescaleDB extends PostgreSQL for time-series and event workloads. Its hypertables automatically partition rows into chunks while remaining queryable as ordinary PostgreSQL tables.

What it provides

  • Automatic time-based partitioning through hypertables
  • Retention, compression, and tiering policies
  • Continuous aggregates and time-series analytical functions

Use it for application metrics, IoT readings, financial ticks, observability events, audit histories, or any append-heavy table primarily queried by time range.

Use it with Rent

extension timescaledb {
  name = "timescaledb"
}

The RSL declaration makes installation and migration intent part of the reviewed application schema. Register the typed pack at runtime to call its APIs:

let timescale = extensions.register_pack::<TimescaleDb>()?;
let client = timescale.client(&pool);
let metrics = client.hypertable("metrics");

metrics.create("recorded_at").await?;
metrics.retain_for("90 days").await?;
metrics.remove_retention().await?;

Retention and compression policies return the 32-bit job ID as an Option<i32>. Removing a retention policy returns (). Durations are PostgreSQL interval strings, such as "90 days"; invalid intervals return database errors. Enable compression on the hypertable before calling metrics.compress_after("7 days"). Compression settings depend on the table's workload and selected TimescaleDB version.

The TimescaleDB profile is tested with its own PostgreSQL image rather than assuming the functions exist in stock PostgreSQL.

Integer time axes

Some event streams use a bigint counter or Unix timestamp instead of a timestamp column. Use ticks consistently: the column, chunk width, retention window, and clock function must all use the same unit.

For a table post_ticks with a bigint tick column, register an existing zero-argument SQL clock that returns bigint. For example, an epoch-seconds clock can return extract(epoch FROM CURRENT_TIMESTAMP)::bigint.

let events = client.hypertable("public.post_ticks");

events.create_integer("tick", 3_600).await?;
events.integer_clock("public.epoch_seconds").await?;
let retention_job = events.retain_ticks(86_400).await?;

Here each chunk spans an hour and the retention window is one day. retain_ticks returns a non-null job ID. Use retain_for("1 day") for timestamp columns; an interval is not interchangeable with integer ticks. Missing clocks, mismatched time axes, and duplicate policies return errors. Policy registration uses the bound transaction, so rolling back also rolls back registration. Background jobs execute separately after commit.

Run the application

cargo run -p rent --example extension_06_timescaledb

Set DATABASE_URL to a disposable TimescaleDB server whose role can create databases. The application creates a post_metrics hypertable, inserts two view-count observations, sums them, and checks that real chunks exist. It adds and removes retention, enables compression, and verifies the compression job in TimescaleDB's catalog. Duplicate policies and malformed intervals are checked as errors. The isolated database is removed afterward.

The same application also creates an integer-time hypertable, registers its clock, checks transaction rollback, and runs its retention job against old and recent chunks. It verifies that only the recent observation remains.

Creating a retention or compression policy registers a background job; it does not immediately delete or compress your data. Choose intervals for your retention requirements before applying policies in production. Its standalone extension declaration is crates/rent/examples/extension_06_timescaledb/schema.rsl.

On this page