| name | algolia-performance-tuning |
| description | Optimize Algolia search performance: record size, searchable attributes,
replica strategy, response caching, and query-time parameter tuning.
Trigger: "algolia performance", "optimize algolia", "algolia latency",
"algolia slow", "algolia caching", "algolia response time".
|
| allowed-tools | Read, Write, Edit |
| version | 1.6.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","search","algolia"] |
| compatibility | Designed for Claude Code |
Algolia Performance Tuning
Overview
Algolia's edge infrastructure typically delivers search in < 50ms globally. When performance degrades, the causes are usually: oversized records, too many searchable attributes, unoptimized faceting, or missing client-side caching. This skill covers server-side and client-side optimizations.
Performance Baselines
| Metric | Good | Warning | Action Needed |
|---|
| Search latency (P50) | < 20ms | 20-100ms | > 100ms |
| Search latency (P95) | < 50ms | 50-200ms | > 200ms |
| Indexing time per 1K records | < 2s | 2-10s | > 10s |
| Record size (avg) | < 5KB | 5-50KB | > 50KB |
Instructions
Step 1: Optimize Record Size
import { algoliasearch } from 'algoliasearch';
const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);
const badRecord = {
objectID: '1',
name: 'Running Shoes',
full_html_description: '<div>...5000 chars of HTML...</div>',
internal_notes: 'Supplier ref: ABC-123',
all_reviews: [],
};
const goodRecord = {
objectID: '1',
name: 'Running Shoes',
description: 'Lightweight running shoes with cushioned sole',
category: 'shoes',
brand: 'Nike',
price: 129.99,
rating: 4.5,
review_count: 200,
in_stock: true,
image_url: '/images/1.jpg',
};
Step 2: Optimize Searchable Attributes
await client.setSettings({
indexName: 'products',
indexSettings: {
searchableAttributes: [
'name',
'brand',
'category',
'unordered(description)',
],
unretrievableAttributes: ['internal_tags'],
attributesToRetrieve: ['name', 'brand', 'price', 'image_url', 'category'],
},
});
Step 3: Optimize Faceting
await client.setSettings({
indexName: 'products',
indexSettings: {
attributesForFaceting: [
'category',
'brand',
'filterOnly(price)',
'filterOnly(in_stock)',
'filterOnly(created_at)',
],
},
});
Step 4: Client-Side Response Caching
import { LRUCache } from 'lru-cache';
const searchCache = new LRUCache<string, any>({
max: 500,
ttl: 60 * 1000,
});
async function cachedSearch(query: string, filters?: string) {
const cacheKey = `${query}|${filters || ''}`;
const cached = searchCache.get(cacheKey);
if (cached) return cached;
const result = await client.searchSingleIndex({
indexName: 'products',
searchParams: { query, filters, hitsPerPage: 20 },
});
searchCache.set(cacheKey, result);
return result;
}
Step 5: Query-Time Optimization Parameters
const { hits } = await client.searchSingleIndex({
indexName: 'products',
searchParams: {
query: 'laptop',
attributesToRetrieve: ['name', 'price', 'image_url'],
attributesToHighlight: ['name'],
attributesToSnippet: [],
responseFields: ['hits', 'nbHits', 'page', 'nbPages'],
hitsPerPage: 20,
maxValuesPerFacet: 10,
},
});
Step 6: Replica Strategy for Sort Orders
await client.setSettings({
indexName: 'products',
indexSettings: {
replicas: [
'virtual(products_price_asc)',
'virtual(products_price_desc)',
'products_newest',
],
},
});
Performance Monitoring
async function measureSearchLatency(query: string, iterations = 10) {
const latencies: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
await client.searchSingleIndex({
indexName: 'products',
searchParams: { query, hitsPerPage: 20 },
});
latencies.push(performance.now() - start);
}
latencies.sort((a, b) => a - b);
console.log({
p50: latencies[Math.floor(iterations * 0.5)].toFixed(1),
p95: latencies[Math.floor(iterations * 0.95)].toFixed(1),
p99: latencies[Math.floor(iterations * 0.99)].toFixed(1),
avg: (latencies.reduce((a, b) => a + b) / iterations).(),
});
}
Error Handling
| Issue | Cause | Solution |
|---|
| P95 > 200ms | Oversized records | Trim records, use unretrievableAttributes |
| Facet queries slow | Too many facet values | Use filterOnly() or maxValuesPerFacet |
| Indexing slow | Large batch + complex settings | Reduce batch size, simplify searchableAttributes |
| Cache stampede | TTL expired, burst traffic | Use stale-while-revalidate pattern |
Resources
Next Steps
For cost optimization, see algolia-cost-tuning.