metricscron fails on TimescaleDB 2.29.0 - chunk.schema_name removed (Daphnie/Query/Generic.php)

apnscp version: 3.2.0 (build 74b0e2ca2, dirty: false)
OS: AlmaLinux 9.8
Reproducible: happened once so far - the 2026-07-30 00:00 run, which is the first nightly run after the TimescaleDB 2.29.0 upgrade landed. The failing query is reached unconditionally, so it should recur every night.

What happens

metricscron.service exits 255 a fraction of a second after start:

metricscron.service: Main process exited, code=exited, status=255/EXCEPTION

The retention DELETE still completes (“Deleted metrics older than …”), but none of the three zip tiers (5 minutes / 1 hour / 4 hours) complete, so the unit stays in failed and a daily failed-unit check flags it every morning.

Backtrace from the panel’s own exception output:

(PDOException) SQLSTATE[42703]: Undefined column: column c.schema_name does not exist
HINT: Perhaps you meant to reference the column "h.schema_name".
[/usr/local/apnscp/lib/Daphnie/Chunker.php:280]
   0. PDO->query("SELECT c.id AS id, int8range(range_start, range_end),
         c.schema_name||'.'||c.table_name AS chunk_name
         FROM _timescaledb_catalog.dimension_slice ds JOIN ...")
   1. Daphnie\Chunker->getChunksFromRange()
   2. Daphnie\Chunker->decompressRange()
   3. Daphnie\Collector->zip(..., "5 minutes", false)
      [/usr/local/apnscp/bin/scripts/metricscron.php:89]

(Error text is localized on this box; the SQLSTATE and column name are as shown.)

Why

TimescaleDB 2.29.0, released 2026-07-28, reworked _timescaledb_catalog.chunk: schema_name and table_name are gone, replaced by relid. Changelog entry: “Use regclass for storing relation reference in chunk table” (PR #10231).

SELECT ordinal_position, column_name FROM information_schema.columns
 WHERE table_schema='_timescaledb_catalog' AND table_name='chunk';

1|id
2|relid
3|hypertable_id
4|status
5|osm_chunk
6|creation_time

hypertable still has schema_name, which is where the Postgres hint comes from. The query built in lib/Daphnie/Query/Generic.php:77 selects c.schema_name||'.'||c.table_name, so it can no longer resolve.

Possibly useful for anyone else hitting this: the in-DB extension update to 2.29.0 was applied by the panel’s own post-transaction trigger (Trigger\Timescaledb2Postgresql16) right after the dnf upgrade, so a box on automatic updates gets the new catalog with no manual step.

Steps to reproduce

With timescaledb-2-postgresql-16-2.29.0 installed and the extension updated to 2.29.0, the nightly timer run is enough - that is how it surfaced here. systemctl start metricscron.service should reproduce it on demand; to be precise, the trace above comes from the timer run rather than a manual start, and zip() is called unconditionally on every run.

Fix

relid is a regclass and already renders schema-qualified, so this returns the same values the concatenation used to:

c.relid::regclass::text AS chunk_name

Checked against a live 2.29.0 catalog - the output form is unchanged, e.g. _timescaledb_internal._hyper_5_299_chunk.

Same columns used elsewhere

Not seen failing yet, but these reference the removed columns:

  • lib/Daphnie/Query/v2.php lines 96, 122-124 and 140 (chunk filter, pg_total_relation_size/pg_indexes_size, and pns.nspname = c.schema_name)
  • lib/modules/telemetry.php:220 - $chunker->decompressRange(null) reaches the same query

One more detail on the same query: on 2.29.0 _timescaledb_catalog.chunk_constraint is a view rather than a table (pg_class.relkind = 'v', while chunk, dimension_slice and hypertable are still 'r').

Impact

Minor, in case it helps with priority: collection is unaffected, retention still runs, and nothing is permanently lost - updateLastRun() is only called after the compression loop finishes, and the window widens by the elapsed gap ($slide), so the first successful run back-fills what was missed. The visible symptom is the nightly failed unit.

Environment: apnscp 3.2.0 (74b0e2ca2), AlmaLinux 9.8, PostgreSQL 16.14, timescaledb-2-postgresql-16 2.29.0, systemd 252.

Follow-up with a bit more data.

Still failing on ApisCP 3.2.0 (rev 74b0e2ca2) with timescaledb-2-postgresql-16-2.29.0-0.el9, four nights running (2026-07-30 to 08-02).

I verified the replacement on the live instance instead of assuming it. This returns 0:

SELECT count(*) AS mismatches FROM (
  SELECT show_chunks('metrics')::text
  EXCEPT
  SELECT c.relid::regclass::text
  FROM _timescaledb_catalog.chunk c
  JOIN _timescaledb_catalog.hypertable h ON (h.id = c.hypertable_id)
  WHERE h.table_name = 'metrics'
) x;

So c.relid::regclass::text matches show_chunks() output exactly.

Correction to my earlier impact note: it does not back-fill on the next successful run. timescaledb_information.jobs lists only policy_telemetry, policy_job_stat_history_retention and policy_refresh_continuous_aggregate — no compression policy — so metrics is compressed only by metricscron. The exception is raised in decompressRange() before the compress step, so metrics (compression_enabled = t) stays at 0 of 4 chunks compressed while the cron fails. Collection, retention and the continuous aggregate are unaffected.

Thanks for the bug report. This has been fixed in edge. As a heads-up, we may encounter something similar in later releases in _timescaledb_catalog.hypertable. This still refers to schema_name and table_name separately.

1 Like