> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackshift.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Performance Insights and events

> See what a Dedicated database spends its time on, find its slowest queries, and read a feed of failovers, backups and maintenance.

> [!NOTE]
>
> **Not yet released.** This guide describes implemented changes awaiting rollout. Availability requires the corresponding backend and dashboard release.

## Goal

Find out why a database is slow, and what has happened to it recently, without logging in to the server.

## Prerequisites

* A Dedicated database that is running
* Read access to the database. Changing the history length needs write access.

## Workflow

**Step 1**

Open the database’s Performance Insights page, or run `stackshift database insights-load DATABASE --query group_by=wait` (`GET /api/v1/databases/{id}/insights/load`). The default range is the last hour.

**Step 2**

Group the load by `wait` (what sessions were waiting on), `sql` (which statements) or `user` (which database user) with `group_by`.

**Step 3**

Compare the load with the line for your vCPU count. Load above that line means sessions are queuing for CPU.

**Step 4**

Open the top statements with `stackshift database insights-statements DATABASE` (`GET /api/v1/databases/{id}/insights/statements`) and pick the one using the most total time.

**Step 5**

Ask for its plan with `stackshift database insights-explain DATABASE --data '{"query_id":"..."}'` (`POST /api/v1/databases/{id}/insights/explain`). Add an index or rewrite the query, then check the load again.

**Step 6**

Read what happened to the database with `stackshift database events DATABASE` (`GET /api/v1/databases/{id}/events`).

## Database load

The load chart shows average active sessions: how many sessions were busy at the same moment, per time bucket. Idle moments read as 0. The bucket size grows with the range, from one minute up to a day.

* `from` and `to` are RFC 3339 times (`--query from=… --query to=…` on the CLI). The range is limited to your history length, and `to` stops at the newest collected minute, so the chart can end a minute or two before now.
* `group_by=wait` groups by wait type, for example `CPU`, `IO`, `Lock` or `LWLock`. `group_by=sql` groups by statement, and `group_by=user` by database user.
* At most 8 series are returned. The rest are merged into one series named `other`.
* Add `instance_id` to look at one server (the primary, standby or a replica). Without it, the load of every server is added up.
* An empty database returns no series.

## Top statements

* Statements are ranked by total time. Each shows its calls, total and mean time, rows, the database users that ran it and its share of the total time.
* `limit` defaults to 25, at most 100. `from`, `to` and `instance_id` work as for the load.
* PostgreSQL statements are normalised, so values show as `$1`.

## Explain a statement

* Send either a `query_id` from the top statements or your own `sql`, never both.
* You get the engine’s plan as JSON. The statement is never run, so there is no `ANALYZE` and nothing is changed.
* Only a single read-only `SELECT` is accepted. Inserts, updates, deletes, schema changes, `SELECT … INTO`, `FOR UPDATE`, several statements in one and data-changing `WITH` queries are refused.
* A normalised statement with `$1` placeholders is planned as a generic plan, which needs PostgreSQL 16 or newer. On PostgreSQL 15, send your own SQL with real values.
* A statement that was stored cut short (2,000 characters or more), or one that has aged out of your history, can’t be explained.
* The database must be running, or the answer is `409`.

## How long history is kept

* `stackshift database insights DATABASE` (`GET /api/v1/databases/{id}/insights`) shows your history length in `retention_days` and the options you can choose from.
* 7 days is included. 30 and 90 days cost extra: see [https://stackshift.cloud/pricing](https://stackshift.cloud/pricing).
* Change it with `stackshift database insights-set DATABASE --data '{"retention_days":30}'` (`PUT /api/v1/databases/{id}/insights`). A paid change works like the other paid changes: you get `409 price_confirmation_required` with the quote, then resend with `confirm_charge_minor`. If your wallet is short, the answer is `402 payment_required` with a `checkout_url`.
* Going to a shorter history credits the difference. Older history is deleted at the next daily clean-up.
* Any other number of days is refused with `400`.

## Events feed

`stackshift database events DATABASE` (`GET /api/v1/databases/{id}/events`) lists what happened to the database, newest first. It returns `events` (each with an `id`, `type`, `severity` of `info`, `warning` or `critical`, a `title`, a `message` and `created_at`) and `next_before`. To read older events, pass `next_before` as `before` (`--query before=ID` on the CLI). `limit` defaults to 50, at most 200.

* Types: `failover`, `switchover`, `autogrow`, `backup_failed`, `backup_restored`, `maintenance_scheduled`, `maintenance_applied`, `maintenance_failed`, `storage_80_percent`, `replica_lag`, `pending_restart`, `size_changed`, `backup_history_gap`, `backup_history_restarted`, and `move_started`, `move_cutover`, `move_completed`, `move_failed` for a database moved from Starter.
* Conditions (`backup_failed`, `storage_80_percent`, `replica_lag`, `pending_restart`) raise one event when they start, and again only after they have cleared.
* Each event is also added to your inbox. Warnings, restore notices and maintenance notices are emailed too.

## Availability

Dedicated databases are rolling out. If Create doesn’t offer Dedicated, it isn’t switched on for your account yet, and the API answers `403`.

## Expected result

> [!TIP]
>
> You can name the wait, statement or user that is loading the database, and see the plan for the statement you are fixing.

## Common failures

> [!WARNING]
>
> * `409` “This is available on Dedicated databases.”: Performance Insights and the events feed are not offered on Starter databases.
> * `400` when explaining a statement: send exactly one of `query_id` and `sql`, and keep it to one read-only `SELECT`.
> * The chart ends a minute or two before now: it shows the newest collected minute.
> * `409 price_confirmation_required` when changing the history length: resend with `confirm_charge_minor` set to the quoted charge.

## Related guides

> **[Dedicated databases](https://docs.stackshift.cloud/databases/dedicated-databases.md)**
>
> Create a Dedicated PostgreSQL database, connect to it, resize it and manage its lifecycle.

> **[Standby and high availability](https://docs.stackshift.cloud/databases/standby-and-high-availability.md)**
>
> Add a standby on another server. If the primary fails, the standby takes over and your connection address stays the same.

> **[Read replicas](https://docs.stackshift.cloud/databases/read-replicas.md)**
>
> Add up to 5 read-only copies of a Dedicated database and send read traffic to them through one read address.

> **[Dedicated billing](https://docs.stackshift.cloud/databases/dedicated-billing.md)**
>
> Pay for a Dedicated database monthly, yearly or by the hour, from your wallet or card.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.