Database and query performance
Reduce database work with close placement, bounded pools, indexes, selection, and deliberate relationship population.
Start with the database round trip and query shape. A fast handler cannot compensate for a remote database, an unindexed frequent filter, or a response that expands far more content than the caller uses.
Keep the database close
Run Ridu and its database in the same region. Prefer the provider’s private network when available, and measure latency from the running application rather than from a laptop. Relationship population, access checks that read related records, nested local operations, authentication, and tasks can add database round trips to an ordinary request.
SQLite is an embedded local-file database for one host and small or local workloads. Do not place its database on a network filesystem or use it as a horizontally shared database. Use PostgreSQL for a networked multi-replica application. Use MongoDB only inside its documented replica-set and release-qualified operating envelope.
Size the connection pool from real concurrency
The official adapters use bounded pools. Zero values select Ridu’s defaults, which are a safer starting point than an unbounded pool. Increase a pool only after observing pool wait time and the database’s own connection capacity; multiply the setting by every running application replica.
| Adapter | Configuration | Useful controls |
|---|---|---|
| PostgreSQL | postgres.OpenWithConfig(ctx, postgres.PoolConfig{...}) |
MaxConnections, MinConnections, connection lifetime/idle bounds, statement and lock timeouts, and the separate MaxUploadLockConnections pool. |
| MongoDB | mongodb.OpenWithConfig(ctx, mongodb.Config{...}) |
MaxPoolSize, MinPoolSize, connection idle time, connect timeout, and server-selection timeout. |
| SQLite | sqlite.OpenWithConfig(ctx, sqlite.Config{...}) |
MaxConnections, BusyTimeout, and WAL mode. SQLite still permits one writer at a time. |
Keep request deadlines shorter than the amount of time a caller is willing to wait. PostgreSQL statement, lock, and idle-transaction timeouts put server-side bounds around work that survives a client disconnect or waits on a lock. The generated server already propagates request cancellation into operations and adapter calls.
Index frequent filters and sorts
Add .Index() to a stored field used frequently in equality filters or sorting. .Unique() also
creates the constraint needed to enforce uniqueness; do not add it merely as a performance hint.
package content
import (
"github.com/riducms/ridu"
"github.com/riducms/ridu/field"
)
var Posts = ridu.Collection{
Slug: "posts",
Fields: field.Fields{
field.Text("title").Required(),
field.Text("slug").Required().Unique(),
field.Select("status", "draft", "published").Index(),
field.Date("publishedAt").Index(),
},
}After changing indexes, create, review, and apply the migration. An index speeds some reads by adding storage and write work, so index the paths your production queries actually use. Nested, repeated, JSON, Blocks, and opaque plugin paths do not become generally sortable or indexable just because a database can represent them internally.
| Field method | Default | Effect |
|---|---|---|
.Index() |
Off | Adds a non-unique index for supported stored scalar/reference fields. |
.Index(false) |
— | Removes an explicitly inherited or composed index setting. |
.Unique() |
Off | Requires distinct non-empty values and creates adapter-owned uniqueness support. |
.Unique(false) |
— | Removes the field’s uniqueness setting; review the generated migration. |
Request only the data you use
The same read controls are available through the SDK, REST, and local Go API. Selection reduces the authored fields in the output. Population expands references into documents and can multiply work, so prefer explicit paths over a blanket depth.
const page = await ridu.list('posts', {
where: { status: { equals: 'published' } },
sort: ['-publishedAt'],
page: 1,
limit: 20,
select: { title: true, slug: true, author: true },
populate: {
author: {
depth: 1,
select: { name: true }
}
}
});| Read option | Default or limit | Cost and use |
|---|---|---|
where |
No caller filter; HTTP accepts at most 100 comparisons | Filter in the database. Prefer indexed direct paths for frequent queries. |
sort |
Store-defined stable order; at most 16 unique terms over HTTP | Sort supported stored fields. Ridu adds id as a stable tie-breaker when needed. |
page |
1 |
Choose a positive result page. Deep offset pagination can still cost more as the page grows. |
limit |
10; HTTP maximum 100 |
Bound documents read, processed, serialized, and rendered. |
select |
All readable authored fields; at most 256 entries over HTTP | Return only needed top-level fields plus framework identity metadata. |
populate |
No population; at most 64 explicit paths and depth 5 |
Expand named relationships with per-target access and redaction. Add a target select. |
depth |
0; maximum 5 |
Expand every root reference uniformly. Prefer populate when cost and output shape matter. |
locale / fallbackLocale |
Project configuration | Choose exact or fallback content. locale: 'all' returns every translation and does more projection work. |
draft / trash mode |
Published, non-trashed reads | Read the lifecycle state the caller needs; these modes still apply access and query rules. |
Do not combine depth and populate. A selection does not automatically populate a selected
relationship. Explicit population also produces a more precise generated TypeScript result than a
numeric depth.
Count without loading documents
Use count when the caller needs only a total. It applies the same access predicate, caller
filter, locale, draft, and trash mode without constructing and serializing a page of documents.
const { totalDocs } = await ridu.count('posts', {
where: { status: { equals: 'published' } }
});Do not request a list with a large limit and discard its documents. For distinct values in Go,
LocalAPI.Distinct supports one direct singular stored field and bounded pagination; it is not a
general aggregation API.
Keep access filters in the query
Collection access can return a predicate. Ridu combines it with the caller’s filter in the atomic store request for reads, updates, and deletes. Preserve that behavior: fetching broadly and filtering in application code both wastes work and risks exposing unauthorized rows.
Continue with Querying data for operators and exact limits, or Hook and value performance when the database query is fast but operation processing is expensive.