Performance
Make apps on Sovaria fast: indexes and the query advisor, pagination, connection pooling, statement timeouts, payload size, function memory and realtime connections.
Most slowness is in the database. Find the slow queries first, then fix them — usually with an index.
Find slow queries
- Observability › Query Performance lists the slowest and most frequent statements with their time.
- Schema › Advisor suggests indexes for the queries your project actually runs.
- In the SQL editor, Explain shows a statement's plan without running it. Look for
Seq Scanon large tables.
Indexes
- Index the columns you filter, join and sort on — and the columns your row-level security policies compare (
owner_id,tenant_id): every query on that table filters by them. - A multi-column index serves filters on its leading columns:
(tenant_id, created_at)serveswhere tenant_id = $1 order by created_at. - Create indexes on big tables with
create index concurrently(it doesn't block writes); it can't run inside a transaction. - Indexes cost writes and disk: drop ones the advisor shows as unused.
create index concurrently orders_customer_created_idx
on orders (customer_id, created_at desc);
explain select id, total from orders
where customer_id = $1 order by created_at desc limit 20;Paginate by key, not by offset
offset 10000 makes Postgres read and skip 10,000 rows. Page with the last value you showed instead:
select id, created_at, total from orders
where customer_id = $1 and created_at < $2 -- last created_at of the previous page
order by created_at desc limit 20;Reads through the REST table API return at most 1,000 rows per request; document queries page with cursors (Querying documents).
Fewer round trips
- Fetch related rows in one query with a join or a view instead of one request per row (the N+1 pattern).
- Write many rows in one statement or one batch rather than a loop of single inserts.
- Select only the columns you show.
Connections and timeouts
- Servers that connect to Postgres directly should use the pooled connection; serverless code should never open a direct connection per request (Database: connection, compute, disk and backups).
- The statement timeout cancels runaway queries; set it under Pooling & timeouts (Pooling and timeouts).
- Concurrent connections are limited by plan:
| Free | Pro | Ultra | Team | Enterprise | |
|---|---|---|---|---|---|
| Concurrent database connections | 20 | 200 | 500 | 2,000 | 10,000 |
| Dedicated connection pooler | Shared | Shared | Shared | Dedicated node | Dedicated cluster |
Functions
- Do the slow work once at module level (clients, configuration), not on every call.
- More memory also means more CPU — but costs more GB-seconds (Function memory, timeout and limits).
- Long jobs belong in a workflow or a background trigger, not in a request the user waits for (Event triggers).
Realtime and storage
- Open one realtime connection per app and join several channels on it.
- Keep messages small; send ids and fetch details when needed.
- Serve public assets from public buckets so they can be cached; resize images before uploading.
Something unclear or missing on this page?Tell us