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.fromandtoare RFC 3339 times (--query from=… --query to=…on the CLI). The range is limited to your history length, andtostops at the newest collected minute, so the chart can end a minute or two before now.group_by=waitgroups by wait type, for exampleCPU,IO,LockorLWLock.group_by=sqlgroups by statement, andgroup_by=userby database user.- At most 8 series are returned. The rest are merged into one series named
other. - Add
instance_idto 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.
limitdefaults to 25, at most 100.from,toandinstance_idwork as for the load.- PostgreSQL statements are normalised, so values show as
$1.
Explain a statement
- Send either a
query_idfrom the top statements or your ownsql, never both. - You get the engine’s plan as JSON. The statement is never run, so there is no
ANALYZEand nothing is changed. - Only a single read-only
SELECTis accepted. Inserts, updates, deletes, schema changes,SELECT … INTO,FOR UPDATE, several statements in one and data-changingWITHqueries are refused. - A normalised statement with
$1placeholders 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 inretention_daysand 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 get409 price_confirmation_requiredwith the quote, then resend withconfirm_charge_minor. If your wallet is short, the answer is402 payment_requiredwith acheckout_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, andmove_started,move_cutover,move_completed,move_failedfor 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 answers403.
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
Related guides
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.