Conversation
Native persistence service for TimescaleDB using hypertables, in-place downsampling, compression policies, and retention rules. Supports all openHAB item types with per-item aggregation and retention configuration via item metadata. - Implements QueryablePersistenceService and ModifiablePersistenceService - Single hypertable schema with automatic partitioning - Per-item downsampling (AVG, MIN, MAX, SUM) configurable via item metadata - Automatic chunk compression and data retention policies - Full unit support for QuantityType items - Karaf console command for manual downsampling trigger - Comprehensive test coverage with Testcontainers integration tests Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
[timescaledb] Add native TimescaleDB persistence service Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
|
This pull request has been mentioned on openHAB Community. There might be relevant details there: https://community.openhab.org/t/new-timescaledb-persistence-service-for-openhab/168837/1 |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new openHAB persistence add-on that targets TimescaleDB specifically, adding hypertable-backed storage plus built-in downsampling, retention, and optional compression support.
Changes:
- Adds the new
org.openhab.persistence.timescaledbbundle/module and wires it into the reactor + BOM. - Implements schema initialization/migration, JDBC query/insert/delete operations, a daily downsampling/retention job, and a Karaf console command.
- Adds extensive unit + integration tests (mocked JDBC + Testcontainers) and bundle documentation.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| bundles/pom.xml | Adds the TimescaleDB persistence module to the bundles reactor. |
| bom/openhab-addons/pom.xml | Adds the new bundle to the add-ons BOM. |
| CODEOWNERS | Assigns code ownership for the new bundle. |
| bundles/org.openhab.persistence.timescaledb/pom.xml | Declares bundle dependencies and test setup. |
| bundles/org.openhab.persistence.timescaledb/src/main/feature/feature.xml | Declares the Karaf feature and required runtime bundles (PostgreSQL driver, HikariCP, the add-on bundle). |
| bundles/org.openhab.persistence.timescaledb/src/main/resources/OH-INF/config/timescaledb.xml | Adds UI/service configuration description for the persistence service. |
| bundles/org.openhab.persistence.timescaledb/src/main/resources/OH-INF/addon/addon.xml | Registers the add-on in the openHAB UI. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java | Implements the OSGi persistence service lifecycle, pooling, scheduling, store/query/remove. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBSchema.java | Creates/migrates the DB schema (tables, hypertable, constraints, policies). |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBQuery.java | Provides SQL for insert/query/delete and item_id lookup helpers. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBMapper.java | Maps openHAB state types to/from persisted DB row format. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBHistoricItem.java | Implements HistoricItem for query results. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBMetadataService.java | Parses per-item metadata for downsampling/retention configuration. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBDownsampleJob.java | Implements the scheduled downsampling + retention job logic. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBConsoleCommandExtension.java | Adds Karaf console command to run downsampling on-demand. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/DownsampleConfig.java | Holds validated per-item downsampling/retention configuration + interval allowlist. |
| bundles/org.openhab.persistence.timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/AggregationFunction.java | Defines supported aggregation functions and their SQL names. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/BundleManifestTest.java | Validates OSGi manifest imports and addon.xml presence. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBSchemaTest.java | Unit tests for schema DDL generation/migration using mocks. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBQueryTest.java | Unit tests for query/insert/remove SQL construction and result mapping. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceServiceTest.java | Unit tests for service store/query/remove and lifecycle behavior with mocked datasource. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBMetadataServiceTest.java | Unit tests for metadata parsing and interval allowlist enforcement. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBMapperTest.java | Unit tests for state type mapping to/from DB representation. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBDownsampleJobTest.java | Unit tests for downsampling job SQL/transaction behavior with mocks. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBDownsampleSemanticsTest.java | Unit tests for ON/OFF and OPEN/CLOSED aggregation semantics. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBContainerTest.java | Integration tests using Testcontainers for end-to-end persistence flows. |
| bundles/org.openhab.persistence.timescaledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBExternalIT.java | Optional external integration test against a user-provided TimescaleDB instance. |
| bundles/org.openhab.persistence.timescaledb/README.md | End-user documentation for setup, config, metadata syntax, and Grafana querying. |
| bundles/org.openhab.persistence.timescaledb/PERFORMANCE_TESTS.md | Performance/scaling test plan and how to run performance tests. |
| bundles/org.openhab.persistence.timescaledb/AGENTS.md | Developer guide documenting architecture, schema, metadata format, and testing strategy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
...imescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBSchema.java
Outdated
Show resolved
Hide resolved
...b/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBMetadataService.java
Show resolved
Hide resolved
...timescaledb/src/main/java/org/openhab/persistence/timescaledb/internal/DownsampleConfig.java
Show resolved
Hide resolved
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Outdated
Show resolved
Hide resolved
…input validation - query() and remove() now resolve item_id from DB on cache miss instead of returning empty/false immediately; this fixes queries and removals after a service restart before the first store() for that item - deactivate() now nulls downsampleJobInstance so runDownsampleNow() cannot fire against a closed connection pool - chunkInterval is no longer string-formatted into DDL; create_hypertable now uses a PreparedStatement with ?::INTERVAL to prevent SQL injection - negative retainRawDays and retentionDays values in item metadata are now rejected with a warning instead of being passed to SQL unchanged - DownsampleConfig.retentionOnly() enforces retentionDays > 0 with an IllegalArgumentException - unit tests added/updated for all five fix areas Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
|
@copilot thanks for the review - check again... |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
...edb/src/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBDownsampleJob.java
Outdated
Show resolved
Hide resolved
...escaledb/src/main/java/org/openhab/persistence/timescaledb/internal/AggregationFunction.java
Show resolved
Hide resolved
…afety, docs - Warn at activation when compressionAfterDays > 0 so operators know that per-item retainRawDays must stay below compressionAfterDays - Catch Exception (not just SQLException) in DownsampleJob.run() to prevent the scheduled task from being silently cancelled on a RuntimeException from HikariCP (e.g. pool closed during deactivate) - Remove dead emptyResult() method from TimescaleDBQuery - Remove unnecessary -javaagent argLine from pom.xml: all mocked types are interfaces, Mockito 5 needs no instrumentation agent for that - Fix README: last(unit, time) -> MAX(unit) to match implementation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
|
@copilot @wborn Thanks for the review. Hopefully we catched most if it: Fixed
Not changed
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Outdated
Show resolved
Hide resolved
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
bundles/org.openhab.persistence.timescaledb/src/main/feature/feature.xml
Outdated
Show resolved
Hide resolved
...caledb/src/test/java/org/openhab/persistence/timescaledb/internal/TimescaleDBExternalIT.java
Outdated
Show resolved
Hide resolved
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Show resolved
Hide resolved
PostgreSQL and HikariCP are embedded in the bundle JAR (compile scope). Having them also in feature.xml as separate bundles caused a split-package conflict in OSGi. Removed the redundant bundle entries from feature.xml. Corrected the TimescaleDBExternalIT Javadoc: -Dgroups alone does not override <excludedGroups> in Surefire; -DexcludedGroups="" is also needed. Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
|
@wborn
|
|
Thanks for addressing all the comments @ulbi. 👍 Sometimes Copilot hallucinates a bit. 😵💫 Often it does add useful review comments. 😉 |
|
@wborn I know - and it has uncovered some very relevant stuff claude did oversee here. I think the critical things are adressed know. Hope the bundle will make it in the addons. If there is something else required, let me know ;-) |
...rc/main/java/org/openhab/persistence/timescaledb/internal/TimescaleDBPersistenceService.java
Outdated
Show resolved
Hide resolved
…stedStrategies() Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
lsiepel
left a comment
There was a problem hiding this comment.
Thanks, i did a partial and quick review, mainly targeting the metadata and documentation files.
There was a problem hiding this comment.
@lsiepel Please be more specific why this should be removed? This makes live much easier when working with agents on this bundle.
There was a problem hiding this comment.
We have a strict policy about bundle structure and files added to source control. This file is not part of it
There was a problem hiding this comment.
@lsiepel Thanks for your very strict answer, you may think about further evolving the policies, but as you insist on this, I will move the file in my own storage and keep it there.
There was a problem hiding this comment.
@lsiepel - I think we can allow AI agent instructions at binding level to give agents more context and thereby improve the quality of their output. We also have an agent file here by now: https://github.com/openhab/openhab-addons/blob/main/AGENTS.md
bundles/org.openhab.persistence.timescaledb/PERFORMANCE_TESTS.md
Outdated
Show resolved
Hide resolved
…ment Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
…escaledb Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
jlaur
left a comment
There was a problem hiding this comment.
Have to publish review to get my comment out, since I left one yesterday as well. 🙂
...in/java/org/openhab/persistence/timescaledb/internal/TimescaleDBConsoleCommandExtension.java
Show resolved
Hide resolved
There was a problem hiding this comment.
@lsiepel - I think we can allow AI agent instructions at binding level to give agents more context and thereby improve the quality of their output. We also have an agent file here by now: https://github.com/openhab/openhab-addons/blob/main/AGENTS.md
…th changes; enhance TimescaleDB console command extension with completer support; adjust TimescaleDBPersistenceService configuration PID; add test for service PID convention. Signed-off-by: René Ulbricht <rene_ulbricht@outlook.com>
[timescaledb] Initial contribution
Description
This PR adds a new persistence service for TimescaleDB, a time-series extension for PostgreSQL.
Classification: Novel Addition
While the existing JDBC persistence service can connect to TimescaleDB via a PostgreSQL driver, it treats it as a plain relational database and does not use any of its time-series capabilities. This new service is purpose-built for TimescaleDB and takes advantage of its native features to provide better performance, lower storage usage, and more control over how historical data is managed.
What's new for users
timescaledbnamespace). Raw data is aggregated (AVG,MAX,MIN,SUM) and replaced in-place at a configurable interval (e.g.15m,1h,1d). openHAB reads raw and downsampled data from the same table without any query changes needed.QuantityTypeitems have their unit stored alongside the numeric value, making direct SQL and Grafana queries self-describing.How it compares to JDBC persistence
Full persistence service support
The add-on implements both
QueryablePersistenceServiceandModifiablePersistenceService.Implemented query operations include:
historicState,averageSince,sumSince,minSince,maxSince,countSince,getAllStatesBetween,removeAllStatesBetween.In addition, write and delete operations used by openHAB persistence are supported (
store,remove).Schema setup
The service creates all required tables (
itemshypertable +item_metalookup table) automatically on startup. No manual DDL is needed beyond creating the database user and enabling the TimescaleDB extension.Testing
Unit and integration tests
183 tests across 9 test classes, all passing as of 2026-03-13.
BundleManifestTestTimescaleDBMapperTestTimescaleDBMetadataServiceTestTimescaleDBQueryTestTimescaleDBDownsampleJobTestTimescaleDBDownsampleSemanticsTestTimescaleDBSchemaTestTimescaleDBPersistenceServiceTestTimescaleDBContainerTestTimescaleDBContainerTestspins up a real TimescaleDB instance via Testcontainers and covers the full persistence lifecycle including schema init, write, query, downsampling, and retention.Performance tests
TimescaleDBPerformanceITcovers 12 load scenarios: single write latency, sustained 600 writes/s, cold-start burst, concurrent read/write, downsampling job runtime, connection pool saturation, and an opt-in 18-month bulk scenario. These tests are tagged@Tag("performance")and require an external TimescaleDB instance. They are not part of the standard CI run. See PERFORMANCE_TESTS.md for details and how to run them.How to test manually
services/org.openhab.persistence.timescaledb.cfg(or via the UI under Settings → Add-ons → TimescaleDB).timescaledb.persistfile to your persistence folder.timescaledbmetadata to items to enable per-item downsampling or retention.Documentation
The README covers: prerequisites, schema layout, state type mapping, all configuration properties, the
.persistformat, per-item metadata syntax with examples for.itemsfiles and the UI, how in-place downsampling works step by step, the full query mapping table, compression and retention setup, a Grafana example query, and a comparison table with JDBC persistence.Notes
Suggested label:
enhancement