# Postgres metrics refreshes skip the legacy runs column

> Agno's PostgresDb and AsyncPostgresDb refresh metrics on a migrated database without loading the legacy sessions.runs column, so peak memory stays flat however much run history that backup holds.

- Published: 2026-10-22
- Author: Sannya Singal
- Categories: Changelog
- Canonical: https://www.agno.com/blog/postgres-metrics-refreshes-skip-the-legacy-runs-column
- Markdown: https://www.agno.com/blog/postgres-metrics-refreshes-skip-the-legacy-runs-column.md

Agno's `calculate_metrics()` on `PostgresDb` and `AsyncPostgresDb` now reads only each run's `model` and `model_provider`, so a refresh leaves the legacy `sessions.runs` column in Postgres. The runs migration keeps that column as a backup until someone calls `cleanup_legacy_runs_column()`, and earlier releases selected it on every refresh. Every stored run came along, messages and tool calls included, only to be thrown away because the runs table already held the same data.

That hurt most on the first refresh after migrating, which covers the full history and also fires on its own when the metrics page opens. In one reported case, 51 GB of legacy runs ran an 8 GB container out of memory. An earlier metrics fix dealt with bind-parameter limits and the migration's own memory; this one shrinks what the refresh reads.

Runs that never reached the runs table still count. Postgres expands the legacy column with `jsonb_array_elements` inside the query and sends back two fields per run.

A script seeded an embedded Postgres with 1,500 sessions of 20 runs each, ran the migration, kept the legacy column in place, then called `calculate_metrics()` in a fresh process and measured peak memory growth.

| Legacy runs JSON | Adapter           | Runs counted | Peak memory before | Peak memory now |
| ---------------- | ----------------- | ------------ | ------------------ | --------------- |
| 304 MB           | `PostgresDb`      | 30,000       | +687 MB            | +20 MB          |
| 304 MB           | `AsyncPostgresDb` | 30,000       | +689 MB            | +20 MB          |

The sessions query shows where the memory went.

```text
# real output, first sessions query of the refresh, captured with a SQLAlchemy event

Before
SELECT demo.agno_sessions.session_id, demo.agno_sessions.user_id, demo.agno_sessions.session_data, demo.agno_sessions.created_at, demo.agno_sessions.session_type, demo.agno_sessions.runs

Now
SELECT demo.agno_sessions.session_id, demo.agno_sessions.user_id, demo.agno_sessions.session_data, demo.agno_sessions.created_at, demo.agno_sessions.session_type
```

See the [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/05_agent_os/13_observability/metrics.py), and learn more about [refreshing AgentOS metrics](https://docs.agno.com/examples/agent-os/observability/metrics) and [migrating your Agno database](https://docs.agno.com/other/v3-migration#migrating-your-agno-db) in the documentation.
