Skip to content
Back to skills

Knack Cache Optimizer

ASecurity

Caches API results and enforces Knack's 10-requests-per-second rate limit. Critical for dashboard performance and cost reduction.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 9, 2026
businessjavascriptjavaapiperformance

Works with

  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill knack-cache-optimizer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Knack Cache Optimizer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Knack Cache Optimizer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-knack-cache-optimizer/badge)](https://www.skillsdirectory.com/skills/majiayu000-knack-cache-optimizer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Knack Cache Optimizer
description: Caches API results and enforces Knack's 10-requests-per-second rate limit. Critical for dashboard performance and cost reduction.
allowed-tools: Read, Edit
---

# Knack Cache Optimizer

## Purpose
Caches API results and enforces Knack's 10-requests-per-second rate limit. Critical for dashboard performance and cost reduction.

## Core Constraint
**Knack Rate Limit**: 10 requests/second = 100ms minimum between requests

## Cache Strategy

### What to Cache
1. **Frequently accessed data**: Dashboard metrics, dropdown options
2. **Slow queries**: Paginated full datasets (3,500+ laptops)
3. **Computed aggregates**: County totals, quarterly summaries
4. **Static reference data**: County lists, status options

### What NOT to Cache
1. **User-specific data**: Individual training registrations
2. **Real-time critical data**: Live donation notifications
3. **Sub-second updates**: Active editing sessions

## Core Functions

### cache_results
**Purpose**: Store API response with TTL (time-to-live)

**Parameters**:
- `data_key` (string, required): Unique identifier for cached data
- `data_value` (object, required): Data to cache
- `ttl_seconds` (integer, optional): Cache lifetime (default: 300 = 5 minutes)

**Example**:
```javascript
// Cache all laptops for 5 minutes
const laptops = await fetch_all_pages("object_1");
cache_results({
  data_key: "all_laptops",
  data_value: laptops,
  ttl_seconds: 300
});
```

**Implementation**:
```javascript
const cache = new Map();

function cache_results({ data_key, data_value, ttl_seconds = 300 }) {
  const expiry = Date.now() + (ttl_seconds * 1000);
  cache.set(data_key, {
    data: data_value,
    expiry: expiry
  });
}

function get_cached(data_key) {
  const cached = cache.get(data_key);

  if (!cached) return null;

  if (Date.now() > cached.expiry) {
    cache.delete(data_key);
    return null;
  }

  return cached.data;
}
```

### get_or_fetch
**Purpose**: Check cache first, fetch if miss

**Example**:
```javascript
const laptops = await get_or_fetch({
  data_key: "all_laptops",
  fetch_function: async () => await fetch_all_pages("object_1"),
  ttl_seconds: 300
});
```

**Logic**:
```javascript
async function get_or_fetch({ data_key, fetch_function, ttl_seconds }) {
  const cached = get_cached(data_key);

  if (cached) {
    console.log(`Cache HIT: ${data_key}`);
    return cached;
  }

  console.log(`Cache MISS: ${data_key}, fetching...`);
  const data = await fetch_function();
  cache_results({ data_key, data_value: data, ttl_seconds });

  return data;
}
```

### invalidate_cache
**Purpose**: Clear specific or all cached data

**Example**:
```javascript
// Invalidate specific cache entry
invalidate_cache("all_laptops");

// Clear all cache
invalidate_all_cache();
```

### handle_rate_limits
**Purpose**: Enforce 10 req/sec limit with auto-retry

**Implementation**:
```javascript
class RateLimiter {
  constructor(requests_per_second = 10) {
    this.delay = 1000 / requests_per_second; // 100ms for 10 req/s
    this.last_request = 0;
  }

  async throttle() {
    const now = Date.now();
    const time_since_last = now - this.last_request;

    if (time_since_last < this.delay) {
      const wait = this.delay - time_since_last;
      await sleep(wait);
    }

    this.last_request = Date.now();
  }
}

const limiter = new RateLimiter(10);

async function rate_limited_fetch(object_key) {
  await limiter.throttle();
  return await get_records(object_key);
}
```

### exponential_backoff
**Purpose**: Retry failed requests with increasing delays

**Example**:
```javascript
const data = await exponential_backoff(async () => {
  return await get_records("object_1");
}, {
  max_retries: 3,
  initial_delay: 1000 // Start with 1 second
});
```

**Implementation**:
```javascript
async function exponential_backoff(fn, { max_retries = 3, initial_delay = 1000 }) {
  for (let i = 0; i < max_retries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429) {
        const delay = initial_delay * Math.pow(2, i);
        console.warn(`Rate limit hit, retrying in ${delay}ms...`);
        await sleep(delay);
      } else {
        throw error;
      }
    }
  }
  throw new Error("Max retries exceeded");
}
```

## HTI-Specific Cache Patterns

### Dashboard Metrics (5-minute TTL)
```javascript
// Cache county breakdown
const county_totals = await get_or_fetch({
  data_key: "county_breakdown",
  fetch_function: async () => {
    const all = await fetch_all_pages("object_1");
    return groupBy(all, "county");
  },
  ttl_seconds: 300
});
```

### Quarterly Reports (1-hour TTL)
```javascript
// Cache quarterly data once generated
const q1_report = await get_or_fetch({
  data_key: "q1_2025_report",
  fetch_function: async () => await generate_quarterly_report("Q1", "2025"),
  ttl_seconds: 3600 // 1 hour
});
```

### Reference Data (24-hour TTL)
```javascript
// Cache static lists
const counties = await get_or_fetch({
  data_key: "county_list",
  fetch_function: async () => await get_records("object_counties"),
  ttl_seconds: 86400 // 24 hours
});
```

### Real-Time Critical (30-second TTL)
```javascript
// Short TTL for near-real-time data
const ready_devices = await get_or_fetch({
  data_key: "ready_devices",
  fetch_function: async () => {
    return await get_records("object_1", {
      filters: { status: "Ready for Donation" }
    });
  },
  ttl_seconds: 30
});
```

## Cache Storage Options

### 1. In-Memory (Development)
```javascript
const cache = new Map();
// Pros: Fast, simple
// Cons: Lost on restart, not shared across instances
```

### 2. Redis (Production)
```javascript
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

async function cache_results({ data_key, data_value, ttl_seconds }) {
  await redis.setex(data_key, ttl_seconds, JSON.stringify(data_value));
}

async function get_cached(data_key) {
  const cached = await redis.get(data_key);
  return cached ? JSON.parse(cached) : null;
}
```

### 3. Vercel KV (Serverless)
```javascript
import { kv } from '@vercel/kv';

async function cache_results({ data_key, data_value, ttl_seconds }) {
  await kv.set(data_key, data_value, { ex: ttl_seconds });
}

async function get_cached(data_key) {
  return await kv.get(data_key);
}
```

## Cache Invalidation Strategies

### Time-Based (TTL)
```javascript
// Automatic expiration after TTL
cache_results({
  data_key: "laptops",
  data_value: data,
  ttl_seconds: 300 // Expires in 5 minutes
});
```

### Event-Based (Webhooks)
```javascript
// Invalidate when Knack data changes
register_webhook({
  trigger: "record_updated",
  object_key: "object_1",
  url: "/api/cache-invalidate",
  on_receive: () => {
    invalidate_cache("all_laptops");
    invalidate_cache("county_breakdown");
  }
});
```

### Manual Refresh
```javascript
// User-triggered refresh
app.post('/api/refresh-cache', async (req, res) => {
  invalidate_cache("all_laptops");
  const fresh = await fetch_all_pages("object_1");
  cache_results({ data_key: "all_laptops", data_value: fresh });
  res.json({ refreshed: true });
});
```

## Performance Metrics

### Cache Hit Rate
```javascript
let hits = 0;
let misses = 0;

function get_cached(data_key) {
  const cached = cache.get(data_key);

  if (cached && Date.now() < cached.expiry) {
    hits++;
    return cached.data;
  }

  misses++;
  return null;
}

function cache_hit_rate() {
  const total = hits + misses;
  return total > 0 ? (hits / total) * 100 : 0;
}

console.log(`Cache hit rate: ${cache_hit_rate().toFixed(1)}%`);
```

### API Cost Savings
```javascript
// Track API calls saved by caching
let api_calls_saved = 0;

async function get_or_fetch({ data_key, fetch_function }) {
  const cached = get_cached(data_key);

  if (cached) {
    api_calls_saved++;
    console.log(`API calls saved: ${api_calls_saved}`);
    return cached;
  }

  return await fetch_function();
}
```

## Monitoring

### Cache Statistics
```javascript
function cache_stats() {
  return {
    size: cache.size,
    hit_rate: cache_hit_rate(),
    api_calls_saved: api_calls_saved,
    memory_usage: process.memoryUsage().heapUsed
  };
}

// Log every 5 minutes
setInterval(() => {
  console.log("Cache stats:", cache_stats());
}, 300000);
```

## Best Practices

1. **Cache expensive operations**: Full pagination, complex filters
2. **Use appropriate TTLs**: 5min for dashboards, 1hr for reports, 24hr for static
3. **Invalidate on updates**: Use webhooks to clear stale cache
4. **Monitor hit rate**: Target >80% for dashboard queries
5. **Implement rate limiting**: Always, even with cache
6. **Handle cache failures**: Gracefully fall back to API

## Integration Points

- **knack_reader**: Wrap all get_records calls with caching
- **knack_pagination**: Cache full paginated datasets
- **knack_realtime**: Invalidate cache on webhook triggers
- **knack_dashboard_ai**: Cache computed metrics
- **knack_reporting_sync**: Cache quarterly report data

## Grant Compliance
- Reduce API costs to stay within NCDIT budget
- Improve dashboard responsiveness for stakeholder demos
- Enable offline report generation from cached data
- Log cache metrics for performance reporting

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.