AACsearch
Dépannage

Slow search

Search latency above expected — diagnose cold starts, oversized `perPage`, complex `filterBy`, missing facets, and upstream search engine pressure.

Healthy median search latency at AACsearch is < 50 ms inside the cluster region, < 150 ms over the public internet. If you are seeing > 500 ms or sporadic timeouts, work through these checks.

Symptom

What you seeFirst check
First request is slow, subsequent ones fastCold start — warm with a no-op query
All requests slow, even the second onePer-request shape (perPage, filterBy)
Slow only at peak hoursPer-key concurrency or upstream cluster pressure
Random spikesNetwork path; check from a second client location
Timeouts, 502, 503See errors and rate limits for retry strategy

Decision tree

First request after deploy / restart slow?
  └─ yes → cold start, warm with a no-op:
            client.search({ q: "*", perPage: 1 })

All requests slow?
  ├─ perPage > 100      → reduce
  ├─ filterBy complex   → simplify or precompute
  ├─ facets > 10 fields → reduce or move to per-page
  └─ everything minimal → check status page; open ticket

Check 1: cold start

The first request to a freshly-deployed worker can take 200–500 ms longer than steady-state because it has to establish connections and compile the query plan. This is normal.

If your traffic pattern includes long idle periods (e.g., internal dashboard hit twice a day), you will see this on every visit.

Mitigation: warm the worker before the user request:

// At app startup, or on a cron/edge prewarm
await client.search({ q: "*", perPage: 1 });

For Vercel / Cloudflare Workers / similar, use a scheduled prewarm call.

Check 2: oversized perPage

perPage directly drives serialization cost. The server scales linearly past ~100; past ~250 it is dominated by JSON encoding.

// ❌ Slow even on a healthy cluster
client.search({ q: "shoes", perPage: 1000 });

// ✅ Use pagination instead
client.search({ q: "shoes", perPage: 50, page: 1 });

If you genuinely need every match, use documents/export with an admin key — it streams and is paginated server-side.

Check 3: complex filterBy

Filters with many || (OR) terms or deep parentheses are slow because each branch is evaluated.

// ❌ One big OR over many ids
filterBy: "id:=[id1,id2,...,id500]";

// ✅ Use anyOf which uses the inverted index directly
filterBy: "id:[id1,id2,...,id500]";

// ❌ Nested groups
filterBy: "(brand:=Nike && price:<100) || (brand:=Adidas && price:<150) || ...";

// ✅ Pre-compute a tag field at ingest time
filterBy: "discount_tier:=tier_a";

When in doubt, simplify the filter and benchmark with /search?stats=true (response includes searchTimeMs).

Check 4: too many facets

Each facet field requires a sort and bucket aggregation. Past ~10 facets per request, latency rises noticeably.

// ❌
facetBy: "brand,category,subcategory,size,color,material,season,fit,collection,store,warehouse";

// ✅ Show top-level facets only on the first page; load secondary facets lazily
facetBy: "brand,category,price";

You can render the rest with separate multi-search calls only when the user opens that filter group.

Check 5: query logging

The response includes a searchTimeMs field if you opt in. Compare it to your end-to-end latency to isolate where the time is going.

const result = await client.search({ q: "shoes", debug: true });
console.log("server:", result.searchTimeMs, "ms");
console.log("end-to-end:", endTime - startTime, "ms");
// Difference > 100 ms suggests network or proxy overhead, not the engine

Check 6: upstream cluster pressure

If searchTimeMs itself is > 500 ms, the issue is upstream. Check:

  • https://status.aacsearch.com for ongoing incidents
  • Search → Analytics → "p95 latency" graph (last 24h)
  • If you self-host: AACSearch instance memory and CPU

If status is green and your own metrics are abnormal, open a ticket — see diagnostics packet below.

Check 7: connection pool exhaustion (server-only)

For server-side high-concurrency callers (10k+ qps from one Node process), the default fetch connection pool can throttle under load. Tune:

import { Agent } from "undici";

const agent = new Agent({
	connections: 256, // default is 50
	pipelining: 10,
});

const client = new SearchClient({
	baseUrl: "...",
	apiKey: "...",
	indexSlug: "products",
	dispatcher: agent,
});

For browsers and Workers, this does not apply.

Fix matrix

DiagnosisFix
Cold startPrewarm with q: "*" no-op
perPage too highReduce to ≤ 50, paginate
Complex filterSimplify; precompute tag fields at ingest time
Too many facetsLimit to top-level on first paint, lazy-load the rest
Server actually slowCheck status page; share searchTimeMs with support
Network overheadUse a region closer to the cluster; reuse keep-alive connections
Connection poolTune undici Agent for server callers

Diagnostics packet

FieldNotes
Organization IDrequired
Index slugrequired
Time window (UTC)start and end of the slow period
searchTimeMs from sample responserequired
Median end-to-end latencyfrom your own monitoring
Sample request bodyminimal failing case
Region of callerdatacenter / city / cloud region
Concurrencyrequests per second from this caller

If you can reproduce the slowness from a single curl, paste it. Reproducible cases are resolved roughly 4× faster than "it sometimes feels slow."

On this page