As33
@periodic/
arsenic
redis_object
⚠️ Warning

Inspects internal Redis metadata — adds overhead when called in hot paths

The OBJECT command inspects internal representation details of a Redis key: encoding type (ENCODING), reference count (REFCOUNT), idle time since last access (IDLETIME), and access frequency for LFU eviction (FREQ). These subcommands are diagnostic tools — they have measurable overhead compared to data commands and serve no purpose in application hot paths.

Common Causes

  • Debugging code left in production that checks key encoding or refcount
  • Memory optimisation checks added to hot paths during a performance audit
  • Monitoring code that calls OBJECT IDLETIME on every request to detect stale keys
  • Application code that inspects ENCODING to decide serialisation format at runtime

How to Fix

  1. 1.Move OBJECT calls to offline scripts, monitoring sidecars, or admin-only endpoints
  2. 2.Use DEBUG SLEEP or INFO for server-wide diagnostics rather than per-key OBJECT calls
  3. 3.Design serialisation format decisions at deploy time, not at runtime per request
  4. 4.Use Redis keyspace notifications for idle key detection instead of polling IDLETIME

Diagnostic tool, not a data command

OBJECT is useful for understanding Redis internals, memory optimisation, and debugging. It should never appear in application request paths — only in offline diagnostics, monitoring jobs, or admin scripts.

Correct usage

typescript
// BAD — calling OBJECT in a request handler
app.get('/api/items/:id', async (req, res) => {
  const encoding = await redis.object('ENCODING', `item:${req.params.id}`);
  const value = encoding === 'embstr'
    ? await redis.get(`item:${req.params.id}`)
    : await redis.get(`item:${req.params.id}`); // same either way
  res.json(value);
});

// GOOD — diagnostic script, run offline
async function auditKeyEncodings(redis: Redis, pattern: string) {
  const encodingCounts: Record<string, number> = {};
  let cursor = '0';
  do {
    const [next, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
    cursor = next;
    for (const key of keys) {
      const enc = await redis.object('ENCODING', key);
      encodingCounts[enc as string] = (encodingCounts[enc as string] ?? 0) + 1;
    }
  } while (cursor !== '0');
  return encodingCounts;
}

// GOOD — OBJECT IDLETIME in a background eviction helper
async function evictIdleKeys(redis: Redis, pattern: string, idleSeconds: number) {
  let cursor = '0';
  do {
    const [next, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 50);
    cursor = next;
    for (const key of keys) {
      const idle = await redis.object('IDLETIME', key);
      if ((idle as number) > idleSeconds) {
        await redis.unlink(key);
      }
    }
  } while (cursor !== '0');
}