> 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".

fetch
$curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/algolia-performance-tuning?format=md"
SKILL.mdalgolia-performance-tuning

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

MetricGoodWarningAction Needed
Search latency (P50)< 20ms20-100ms> 100ms
Search latency (P95)< 50ms50-200ms> 200ms
Indexing time per 1K records< 2s2-10s> 10s
Record size (avg)< 5KB5-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

IssueCauseSolution
P95 > 200msOversized recordsTrim records, use unretrievableAttributes
Facet queries slowToo many facet valuesUse filterOnly() or maxValuesPerFacet
Indexing slowLarge batch + complex settingsReduce batch size, simplify searchableAttributes
Cache stampedeTTL expired, burst trafficUse stale-while-revalidate pattern

Resources

Next Steps

For cost optimization, see algolia-cost-tuning.

┌ stats

installs/wk0
░░░░░░░░░░
github stars1.7K
██████████
first seenMar 23, 2026
└────────────

┌ repo

jeremylongshore/claude-code-plugins-plus-skills
by jeremylongshore
└────────────