Browse all documentation

Performance

Make Ridu applications faster by improving queries, indexes, hooks, database placement, and admin code.

Most Ridu performance work comes down to four choices: keep the application close to its database, index the fields used to find content, request only the data a caller needs, and keep custom code proportional to the document being processed. Measure before and after each change with a restored copy of your own data.

Ridu serves the API and embedded admin from one Go process. It also avoids repeated copies of unchanged field values and reuses locale projections during an operation. Those framework optimizations work best when hooks return operation.Keep, queries use deliberate selections and population, and application code reads immutable values without first copying their containers.

Start with the largest wins

Check What to change Why it matters
Database round-trip time Run Ridu and the database in the same region and private network. Every list, relationship population, access lookup, and nested operation can wait on the database.
Frequent filters and sorts Add .Index() to suitable stored fields, then create and apply the migration. An index avoids scanning every document for common query paths.
Response shape Use select; use explicit populate with the smallest useful depth and target select. Less data is read, authorized, transformed, serialized, and transferred.
Lists and counts Set a bounded limit; call count when the caller only needs a total; pass pagination: false when it never shows one. Large pages increase database, operation-engine, JSON, and browser work.
Hooks and validators Return operation.Keep when unchanged, read with Get, Lookup, Entries, or Elements, and move external effects to AfterCommit. Ridu can retain immutable backing and avoids holding a database transaction open for unrelated work.
Shared blocks Register reusable definitions in Config.Blocks and select them with .References(...). Ridu resolves, stores, types and binds each definition once, however many containers place it.
Application lifetime Construct one ridu.App for the process and reuse app.Local() and app.Handler(...). Rebuilding config, runtime bindings, and database pools per request wastes work and connections.
Admin extensions Keep custom components focused and inspect production chunks after adding a large browser dependency. Static imports become part of the admin build even though Node is absent at runtime.

Choose the relevant guide

You are seeing Read First action
Slow filters, sorts, lists, or relationship-heavy reads Database and query performance Inspect the query shape and its indexes.
High allocation, slow saves, or expensive nested content callbacks Hook and value performance Profile the callback and remove unnecessary container copies.
Slow author interactions or a large custom admin bundle Admin performance Profile the browser task and inspect production chunks.
Unclear capacity, memory, bundle, or regression results Measure performance Reproduce a representative workload with repeated trials.

Use the operation engine

Call the local Go API for server-side reads and writes. It enters the same operation engine as REST and the TypeScript SDK, without making a loopback HTTP request:

content/published_posts.go
package content

import (
  "context"

  "github.com/riducms/ridu"
  "github.com/riducms/ridu/query"
  "github.com/riducms/ridu/store"
)

func PublishedPosts(
  ctx context.Context,
  app *ridu.App,
  actor *store.Document,
) (store.Page, error) {
  return app.Local().List(ctx, "posts", ridu.ListOptions{
    Where: query.Equal("status", "published"),
    Page:  1,
    Limit: 20,
    Actor: actor,
  })
}

Keep one *ridu.App and pass it to the code that needs it. A local API call still enforces access, field validation, hooks, transactions, localization, selection, population, and final redaction. Do not call a store.Store or official adapter from application features to make a request faster: that bypasses those guarantees and couples the feature to storage details.

Know what Ridu already reuses

Within one operation, Ridu:

  • shares immutable object and list branches that have not changed;
  • applies a field-hook replacement to the affected branch instead of materializing the complete surrounding list for every occurrence;
  • reuses matching locale projections while still checking embedded input each time; and
  • retains a value between operation passes when its immutable backing is unchanged.

This is bounded, operation-local reuse. It is not a cross-request response cache. A hook that rebuilds an equal object still supplies new backing, a changed wide root may need a full projection, and requested population still performs access-checked target reads. Use an application or edge cache only when its invalidation and authorization model are explicit.

Across the network, Ridu gzips read responses of 1 KiB or more, such as collection pages, the schema and the admin’s pages, for clients that accept gzip; see Harden HTTP and proxies.

Avoid performance shortcuts that change behavior

Shortcut Problem Use instead
Query an adapter directly Skips collection access, field redaction, hooks, versions, localization, and operation transactions. app.Local() or a generated typed handle.
Populate every relationship to a high depth Work grows with every reachable reference and returned document. Explicit populate paths with target select.
Start a goroutine from a transaction hook The operation may commit, roll back, or exit before that work is safely owned. AfterCommit; use a durable task when work needs retries.
Mutate a store.Value container Nested value backing is immutable and may be shared. CopyObject, CopyList, CopyDocument, WithMembers, or WithListItem.
Cache a caller-specific response globally Responses can differ by actor, field access, locale, draft mode, and population. Cache a result only with those dimensions and a clear invalidation rule.

Continue with production readiness

Performance does not replace readiness, graceful shutdown, migration verification, or security bounds. Use Production for the deployment checklist and the relevant PostgreSQL, SQLite, or MongoDB guide for the adapter’s operating envelope.