pg-smart-search
Guides

Caching

How to configure and use Memory and Redis caching providers in pg-smart-search for distributed environments.

Caching

Caching is critical for search performance. pg-smart-search comes with two built-in caching providers: an in-memory provider for single-instance apps, and a Redis provider for distributed production environments.

MemoryCacheProvider

The default provider. Stores cached results in the Node.js process memory. Fast, but isolated to a single instance.

Memory Leak Protection (v1.4.1+)

To prevent Out-Of-Memory (OOM) failures under heavy loads of unique queries, MemoryCacheProvider holds a default capacity limit of 10,000 entries and automatically evicts oldest cached keys using a Least Recently Used (LRU) eviction policy.

Furthermore, a background sweep timer daemon (defaulting to scan every 60,000ms) runs in the background to automatically clear expired keys from RAM.

import { TrigramSearchEngine, MemoryCacheProvider } from "pg-smart-search";

const engine = new TrigramSearchEngine(adapter, {
  tableName: "products",
  searchColumns: ["name"],
  cacheProvider: new MemoryCacheProvider({
    maxEntries: 10000, // Capacity limit
    sweepIntervalMs: 60000, // RAM sweep frequency
  }),
  defaultTTL: 3600, // 1 hour
});

RedisCacheProvider

For production environments running multiple Node.js instances, use the RedisCacheProvider. It ensures all instances share the same cache state — it's a thin get/set/delete/clear wrapper around your Redis client, with no distributed locking.

import Redis from "ioredis";
import { TrigramSearchEngine, RedisCacheProvider } from "pg-smart-search";

const redisClient = new Redis(process.env.REDIS_URL);

const engine = new TrigramSearchEngine(adapter, {
  tableName: "products",
  searchColumns: ["name"],
  cacheProvider: new RedisCacheProvider(redisClient),
  defaultTTL: 3600,
});
import { createClient } from "redis";
import { TrigramSearchEngine, RedisCacheProvider } from "pg-smart-search";

const redisClient = createClient({ url: process.env.REDIS_URL });
await redisClient.connect();

const engine = new TrigramSearchEngine(adapter, {
  tableName: "products",
  searchColumns: ["name"],
  cacheProvider: new RedisCacheProvider(redisClient),
  defaultTTL: 3600,
});

In-Flight Request Deduplication

The engine deduplicates concurrent identical searches within a single process: if the same cache key is requested again while a search for it is already running, the second caller attaches to the first one's in-flight promise instead of issuing a new query (reference-counted, so cancelling one caller's abortSignal only aborts the shared query once every attached caller has cancelled).

This is engine-level, not Redis-level — it works the same whether you're using MemoryCacheProvider or RedisCacheProvider. What it does not do is coordinate across separate Node.js instances: if two different processes miss the cache for the same key at the same time, each will still query the database independently. There is no distributed lock (Redlock or otherwise) preventing that.

Cache Key Determinism & Page Compatibility

  • Deterministic Keys: To prevent duplicate database queries or mismatched cache states, search parameters (such as the dynamic filters object) are sorted lexicographically before forming the unique cache key hash.
  • Deep Pagination Caching: In v1.4.1+, the engine has been optimized to save cached pages whenever either total > 0 or data.length > 0 is true, ensuring that hot pages continue to cache seamlessly even when skipTotalCount is active.