> algolia-performance-tuning
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".
curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/algolia-performance-tuning?format=md"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!);
// BAD: Full record with unnecessary data
const badRecord = {
objectID: '1',
name: 'Running Shoes',
full_html_description: '<div>...5000 chars of HTML...</div>', // Too big
internal_notes: 'Supplier ref: ABC-123', // Not searchable
all_reviews: [/* 200 reviews */], // Huge array
};
// GOOD: Lean record for search
const goodRecord = {
objectID: '1',
name: 'Running Shoes',
description: 'Lightweight running shoes with cushioned sole', // Plain text, truncated
category: 'shoes',
brand: 'Nike',
price: 129.99,
rating: 4.5,
review_count: 200, // Count, not full reviews
in_stock: true,
image_url: '/images/1.jpg', // URL, not base64
};
Step 2: Optimize Searchable Attributes
await client.setSettings({
indexName: 'products',
indexSettings: {
// Order matters: first attribute = highest priority in ranking
// Fewer searchable attributes = faster search
searchableAttributes: [
'name', // Highest priority
'brand',
'category',
'unordered(description)', // unordered = position in attribute doesn't affect ranking
],
// DON'T make IDs, URLs, or numeric fields searchable
// unretrievableAttributes: fields searchable but never returned in hits
// Use for fields users should match against but not see
unretrievableAttributes: ['internal_tags'],
// attributesToRetrieve: limit what comes back (smaller response = faster)
attributesToRetrieve: ['name', 'brand', 'price', 'image_url', 'category'],
},
});
Step 3: Optimize Faceting
await client.setSettings({
indexName: 'products',
indexSettings: {
attributesForFaceting: [
'category', // Regular facet: counts computed
'brand', // Regular facet
'filterOnly(price)', // filterOnly: no counts = faster
'filterOnly(in_stock)', // Use for boolean/numeric filters
'filterOnly(created_at)',
],
// filterOnly() saves CPU — use it when you don't need facet counts
// searchable(brand) lets users search within facet values
},
});
Step 4: Client-Side Response Caching
import { LRUCache } from 'lru-cache';
const searchCache = new LRUCache<string, any>({
max: 500, // Max cached queries
ttl: 60 * 1000, // 1 minute TTL
});
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',
// Reduce response size
attributesToRetrieve: ['name', 'price', 'image_url'], // Only what UI needs
attributesToHighlight: ['name'], // Fewer = faster
attributesToSnippet: [], // Skip snippets if not used
responseFields: ['hits', 'nbHits', 'page', 'nbPages'], // Skip unnecessary metadata
// Limit processing
hitsPerPage: 20, // Don't over-fetch
maxValuesPerFacet: 10, // Limit facet values returned
// Disable features you don't use
// typoTolerance: false, // Uncomment if exact matching is fine
// removeStopWords: false, // Keep stop words in query
},
});
Step 6: Replica Strategy for Sort Orders
// Standard replicas share data but have their own ranking
// Virtual replicas share data AND ranking config (less storage cost)
await client.setSettings({
indexName: 'products',
indexSettings: {
replicas: [
'virtual(products_price_asc)', // Virtual: cheaper, limited customization
'virtual(products_price_desc)',
'products_newest', // Standard: full ranking control
],
},
});
// Virtual replica can only override: customRanking and ranking
// Standard replica can override all settings
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).toFixed(1),
});
}
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.
> related_skills --same-repo
> fathom-cost-tuning
Optimize Fathom API usage and plan selection. Trigger with phrases like "fathom cost", "fathom pricing", "fathom plan".
> fathom-core-workflow-b
Sync Fathom meeting data to CRM and build automated follow-up workflows. Use when integrating Fathom with Salesforce, HubSpot, or custom CRMs, or creating automated post-meeting email summaries. Trigger with phrases like "fathom crm sync", "fathom salesforce", "fathom follow-up", "fathom post-meeting workflow".
> fathom-core-workflow-a
Build a meeting analytics pipeline with Fathom transcripts and summaries. Use when extracting insights from meetings, building CRM sync, or creating automated meeting follow-up workflows. Trigger with phrases like "fathom analytics", "fathom meeting pipeline", "fathom transcript analysis", "fathom action items sync".
> fathom-common-errors
Diagnose and fix Fathom API errors including auth failures and missing data. Use when API calls fail, transcripts are empty, or webhooks are not firing. Trigger with phrases like "fathom error", "fathom not working", "fathom api failure", "fix fathom".