Skip to main content
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

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.
2
Group the load by wait (what sessions were waiting on), sql (which statements) or user (which database user) with group_by.
3
Compare the load with the line for your vCPU count. Load above that line means sessions are queuing for CPU.
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.
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.
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.
  • 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

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

  • 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.

Dedicated databases

Create a Dedicated PostgreSQL database, connect to it, resize it and manage its lifecycle.

Standby and high availability

Add a standby on another server. If the primary fails, the standby takes over and your connection address stays the same.

Read replicas

Add up to 5 read-only copies of a Dedicated database and send read traffic to them through one read address.

Dedicated billing

Pay for a Dedicated database monthly, yearly or by the hour, from your wallet or card.