Tech

Redis Caching Pattern: Akselerasi API 10x dengan Cache yang Benar

API lambat? Cache dulu. Redis pattern yang bener bisa bikin response time turun drastis.
12
2 bulan lalu
Zainul Fanani
Redis Caching Pattern: Akselerasi API 10x dengan Cache yang Benar
📅 11 Mar 2026🤍 0 👁 0 🔗 0

📎 Source:redis-caching-pattern.md — view on GitHub & star ⭐

⚡ Redis Caching Pattern for Speed

Speed up your OpenClaw automations 20x with Redis caching — from 1 second to 50ms! 🚀


📊 Cache Flow Architecture

A Incoming Request  B Check Redis Cache
A Incoming Request B Check Redis Cache
}}%% flowchart TD A🚀 Incoming Request --> B{🔍 Check Redis Cache} style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px style B fill:#fff3e0,stroke:#e65100,stroke-width:2px

text
B -->|Cache HIT ✅| C[⚡ Return Cached Data<br/>~5ms]
B -->|Cache MISS ❌| D[🌐 Fetch from External API<br/>~800ms]
style C fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style D fill:#ffcdd2,stroke:#c62828,stroke-width:2px

D --> E[💾 Store in Redis<br/>with TTL]
style E fill:#e1bee7,stroke:#6a1b9a,stroke-width:2px

E --> F[📤 Return Fresh Data]
C --> G[✅ Client Response]
F --> G
style F fill:#b3e5fc,stroke:#0277bd,stroke-width:2px
style G fill:#a5d6a7,stroke:#388e3c,stroke-width:2px'}

🔄 Cache Lifecycle Sequence

participant C as Client
participant C as Client
}}%% sequenceDiagram participant C as Client participant A as App Script participant R as Redis Cache participant API as External API

text
C->>A: Request Data
A->>R: GET cache:key

alt Cache Exists
    R-->>A: Return cached value
    A-->>C: Response (50ms) ⚡
else Cache Expired/Missing
    R-->>A: nil (miss)
    A->>API: HTTP GET
    API-->>A: JSON Response
    A->>R: SETEX key TTL value
    R-->>A: OK
    A-->>C: Response (1s) 🐢
end'}

🎯 Before vs After

text
┌─────────────────────────────────────────────────────────────┐
│                  SPEED COMPARISON                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  WITHOUT CACHE ❌                    WITH CACHE ✅          │
│                                                             │
│  ┌──────────────┐                   ┌──────────────┐        │
│  │ Request      │                   │ Request      │        │
│  └──────┬───────┘                   └──────┬───────┘        │
│         │                                  │                │
│         ▼                                  ▼                │
│  ┌──────────────┐                   ┌──────────────┐        │
│  │ API Call     │  800ms            │ Check Redis  │  5ms   │
│  │ (External)   │                   └──────┬───────┘        │
│         │                                  │                │
│         ▼                                  ▼                │
│  ┌──────────────┐                   ┌──────────────┐        │
│  │ Parse Data   │  200ms            │ Cache Hit!   │  50ms  │
│  └──────┬───────┘                   └──────┬───────┘        │
│         │                                  │                │
│         ▼                                  ▼                │
│  ┌──────────────┐                   ┌──────────────┐        │
│  │ Total: 1s    │                   │ Total: 50ms  │        │
│  │ 😫 Slow      │                   │ 🚀 20x Faster│        │
│  └──────────────┘                   └──────────────┘        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Real Numbers


🎯 What You'll Build

text
┌─────────────────────────────────────────────────────────────┐
│              REDIS CACHING ARCHITECTURE                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   YOUR SCRIPT                    REDIS SERVER               │
│   ┌──────────────┐              ┌──────────────┐           │
│   │              │              │              │           │
│   │ 1. Check     │─────────────▶│ Key exists?  │           │
│   │    Cache     │              │              │           │
│   │              │◀─────────────│ YES → Return │           │
│   └──────┬───────┘              │      value   │           │
│          │                      │              │           │
│          │ NO                   └──────────────┘           │
│          ▼                             ▲                    │
│   ┌──────────────┐                     │                    │
│   │ 2. Fetch from│                     │                    │
│   │    External  │                     │                    │
│   │    API       │                     │                    │
│   └──────┬───────┘                     │                    │
│          │                             │                    │
│          ▼                             │                    │
│   ┌──────────────┐                     │                    │
│   │ 3. Store in  │─────────────────────┘                    │
│   │    Cache     │         (with TTL)                        │
│   │              │                                          │
│   └──────────────┘                                          │
│                                                             │
│   TTL = Time To Live (auto-expire)                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

🛠️ Installation

Install Redis

bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install redis-server

# macOS
brew install redis

# Start Redis
sudo systemctl start redis    # Linux
brew services start redis     # macOS

# Verify
redis-cli ping
# Should return: PONG

Install Redis Client (Bash)

bash
# redis-cli included with server install
# For scripts, use redis-cli directly

# Test connection
redis-cli set test "hello"
redis-cli get test
# Returns: hello

redis-cli del test

📋 Step 1: Create Helper Functions

Save this as ~/scripts/redis-utils.sh:

bash
#!/bin/bash

# =============================================================================
# ⚡ Redis Helper Functions for OpenClaw
# =============================================================================

# Default Redis connection
REDIS_HOST="${REDIS_HOST:-localhost}"
REDIS_PORT="${REDIS_PORT:-6379}"

# =============================================================================
# 🔧 CORE FUNCTIONS
# =============================================================================

# Set a key with optional TTL (Time To Live in seconds)
redis_set() {
    local key="$1"
    local value="$2"
    local ttl="${3:-}"
    
    if [ -n "$ttl" ]; then
        redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" setex "$key" "$ttl" "$value" >/dev/null
    else
        redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" set "$key" "$value" >/dev/null
    fi
}

# Get a key value
redis_get() {
    local key="$1"
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" get "$key"
}

# Delete a key
redis_delete() {
    local key="$1"
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" del "$key" >/dev/null
}

# Check if key exists (returns 1 if exists, 0 if not)
redis_exists() {
    local key="$1"
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" exists "$key"
}

# Get TTL of a key (returns seconds remaining, -1 if no TTL, -2 if not exists)
redis_ttl() {
    local key="$1"
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ttl "$key"
}

# List keys matching pattern (default: all)
redis_keys() {
    local pattern="${1:-*}"
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" keys "$pattern"
}

# =============================================================================
# 🎯 CONVENIENCE FUNCTIONS
# =============================================================================

# Cache JSON data with TTL
redis_cache_json() {
    local key="$1"
    local json_data="$2"
    local ttl="${3:-300}"  # Default 5 minutes
    
    # Compress JSON to single line
    local compressed
    compressed=$(echo "$json_data" | jq -c . 2>/dev/null || echo "$json_data")
    
    redis_set "$key" "$compressed" "$ttl"
}

# Get and parse cached JSON
redis_get_json() {
    local key="$1"
    local value
    value=$(redis_get "$key")
    
    if [ -n "$value" ] && [ "$value" != "nil" ]; then
        echo "$value" | jq . 2>/dev/null || echo "$value"
    else
        echo "null"
    fi
}

# Cache with automatic expiration for different data types
redis_cache_weather() {
    local location="$1"
    local data="$2"
    # Cache weather for 30 minutes
    redis_cache_json "weather:$location" "$data" 1800
}

redis_cache_price() {
    local item="$1"
    local data="$2"
    # Cache prices for 5 minutes
    redis_cache_json "price:$item" "$data" 300
}

redis_cache_health() {
    local service="$1"
    local data="$2"
    # Cache health for 1 minute
    redis_cache_json "health:$service" "$data" 60
}

redis_cache_session() {
    local session_id="$1"
    local data="$2"
    # Cache sessions for 1 hour
    redis_cache_json "session:$session_id" "$data" 3600
}

# =============================================================================
# 📊 MONITORING FUNCTIONS
# =============================================================================

# Show cache statistics
redis_stats() {
    echo "📊 Redis Statistics"
    echo "=================="
    
    # Memory usage
    echo -n "Memory Used: "
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" info memory | grep "used_memory_human" | cut -d: -f2
    
    # Number of keys
    echo -n "Total Keys: "
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" dbsize
    
    # Connected clients
    echo -n "Connected Clients: "
    redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" info clients | grep "connected_clients" | cut -d: -f2
}

# Clear all cache (use with caution!)
redis_flush() {
    echo "⚠️  This will delete ALL cached data!"
    read -p "Type 'yes' to confirm: " confirm
    
    if [ "$confirm" = "yes" ]; then
        redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" flushdb
        echo "✅ Cache cleared"
    else
        echo "❌ Cancelled"
    fi
}

# Show keys by prefix
redis_list_by_prefix() {
    local prefix="$1"
    echo "🔑 Keys with prefix '$prefix':"
    redis_keys "${prefix}*" | while read -r key; do
        local ttl
        ttl=$(redis_ttl "$key")
        printf "  %-40s (TTL: %s)\n" "$key" "$ttl"
    done
}

Make it executable:

bash
chmod +x ~/scripts/redis-utils.sh

📋 Step 2: Use Cases with Code Examples

Use Case 1: Gold/Price Caching

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

fetch_gold_price() {
    local cache_key="price:gold:xauusd"
    
    # 1. Check cache first
    local cached
    cached=$(redis_get_json "$cache_key")
    
    if [ "$cached" != "null" ]; then
        echo "💰 Cache HIT! Gold price (cached):"
        echo "$cached" | jq -r '.price'
        return 0
    fi
    
    echo "🔄 Cache MISS — Fetching from API..."
    
    # 2. Fetch from external API
    local api_response
    api_response=$(curl -s "https://api.goldapi.io/v1/XAU/USD" \
        -H "x-access-token: YOUR_API_KEY")
    
    # 3. Parse and format
    local price
    price=$(echo "$api_response" | jq -r '.price')
    
    local formatted_data
    formatted_data=$(jq -n \
        --arg price "$price" \
        --arg time "$(date -Iseconds)" \
        '{price: $price, timestamp: $time, source: "goldapi"}')
    
    # 4. Store in cache (5 minutes)
    redis_cache_price "gold:xauusd" "$formatted_data"
    
    echo "💰 Gold price (fresh): $price"
    echo "✅ Cached for 5 minutes"
}

# Run
fetch_gold_price

Use Case 2: Weather Caching

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

fetch_weather() {
    local city="${1:-Jakarta}"
    local cache_key="weather:$city"
    
    # Check cache
    local cached
    cached=$(redis_get_json "$cache_key")
    
    if [ "$cached" != "null" ]; then
        echo "🌤️  Weather for $city (cached):"
        echo "$cached" | jq -r '.condition, .temperature'
        return 0
    fi
    
    echo "🔄 Fetching weather for $city..."
    
    # API call (example)
    local weather_data
    weather_data=$(curl -s "https://api.weather.com/v1/current?city=$city" \
        -H "Authorization: Bearer YOUR_KEY")
    
    # Cache for 30 minutes
    redis_cache_weather "$city" "$weather_data"
    
    echo "🌤️  Weather for $city:"
    echo "$weather_data" | jq -r '.condition, .temperature'
}

fetch_weather "Singapore"

Use Case 3: Health Status Caching

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

check_service_health() {
    local service="$1"
    local url="$2"
    local cache_key="health:$service"
    
    # Check cache first (1 minute TTL)
    local cached
    cached=$(redis_get_json "$cache_key")
    
    if [ "$cached" != "null" ]; then
        local status
        status=$(echo "$cached" | jq -r '.status')
        echo "[$service] $status (cached)"
        return 0
    fi
    
    # Check service
    local start_time end_time duration
    start_time=$(date +%s%N)
    
    if curl -s --max-time 5 "$url" >/dev/null 2>&1; then
        end_time=$(date +%s%N)
        duration=$(( (end_time - start_time) / 1000000 ))
        
        local result
        result=$(jq -n \
            --arg status "UP" \
            --argjson response_time "$duration" \
            --arg checked_at "$(date -Iseconds)" \
            '{status: $status, response_time: $response_time, checked_at: $checked_at}')
        
        redis_cache_health "$service" "$result"
        echo "[$service] UP (${duration}ms)"
    else
        local result
        result=$(jq -n \
            --arg status "DOWN" \
            --arg checked_at "$(date -Iseconds)" \
            '{status: $status, checked_at: $checked_at}')
        
        redis_cache_health "$service" "$result"
        echo "[$service] DOWN"
    fi
}

# Check multiple services
echo "🏥 Health Check (with caching):"
check_service_health "api" "https://api.example.com/health"
check_service_health "database" "https://db.example.com/ping"
check_service_health "website" "https://example.com"

Use Case 4: Session Caching

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

# Store user session
save_session() {
    local session_id="$1"
    local user_data="$2"
    
    redis_cache_session "$session_id" "$user_data"
    echo "✅ Session saved (1 hour)"
}

# Retrieve user session
get_session() {
    local session_id="$1"
    local session_data
    
    session_data=$(redis_get_json "session:$session_id")
    
    if [ "$session_data" != "null" ]; then
        echo "$session_data"
    else
        echo "{}"
    fi
}

# Example usage
user_session='{"user_id": "123", "name": "Alex", "preferences": {"theme": "dark"}}'
save_session "sess_abc123" "$user_session"

retrieved=$(get_session "sess_abc123")
echo "User: $(echo "$retrieved" | jq -r '.name')"

📋 Step 3: Complete Working Example

Save this as ~/scripts/cached-api-call.sh:

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

# =============================================================================
# ⚡ Generic Cached API Caller
# =============================================================================

cached_api_call() {
    local cache_key="$1"
    local api_url="$2"
    local cache_seconds="${3:-300}"  # Default 5 minutes
    local api_headers="${4:-}"
    
    echo "🔍 Checking cache for: $cache_key"
    
    # Try cache first
    local cached_data
    cached_data=$(redis_get_json "$cache_key")
    
    if [ "$cached_data" != "null" ]; then
        local cache_age
        cache_age=$(redis_ttl "$cache_key")
        echo "✅ Cache HIT! (expires in ${cache_age}s)"
        echo "$cached_data"
        return 0
    fi
    
    echo "🔄 Cache miss — calling API..."
    
    # Make API call
    local response
    if [ -n "$api_headers" ]; then
        response=$(curl -s -H "$api_headers" "$api_url")
    else
        response=$(curl -s "$api_url")
    fi
    
    # Validate response (simple JSON check)
    if ! echo "$response" | jq -e . >/dev/null 2>&1; then
        echo "❌ Invalid API response" >&2
        return 1
    fi
    
    # Cache the response
    redis_cache_json "$cache_key" "$response" "$cache_seconds"
    echo "✅ Cached for ${cache_seconds} seconds"
    
    echo "$response"
}

# Example usage
echo "Fetching data with caching..."
result=$(cached_api_call "users:list" "https://jsonplaceholder.typicode.com/users" 600)
echo "$result" | jq '.[0].name'

🔧 TTL (Time To Live) Guidelines

text
┌─────────────────────────────────────────────────────────────┐
│              RECOMMENDED CACHE DURATIONS                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Data Type          TTL          Reason                     │
│  ─────────────────────────────────────────────────────────  │
│  💰 Stock/Prices    5 min        Changes frequently          │
│  🌤️  Weather        30 min       Updates ~hourly             │
│  🏥 Health Status   1 min        Need fresh status          │
│  👤 User Sessions   1 hour       Security + usability       │
│  📊 API Rate Limits 1 hour       Static configuration       │
│  🗺️  Locations      24 hours      Rarely change             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

📊 Performance Testing

Compare cached vs non-cached:

bash
#!/bin/bash

source ~/scripts/redis-utils.sh

API_URL="https://api.example.com/data"
CACHE_KEY="perf:test"

echo "🚀 Performance Test: Cached vs Non-Cached"
echo "=========================================="

# Test 1: Non-cached
echo -e "\n❌ Without Cache:"
for i in 1 2 3; do
    redis_delete "$CACHE_KEY"  # Clear cache
    
    start=$(date +%s%N)
    curl -s "$API_URL" > /dev/null
    end=$(date +%s%N)
    
    duration=$(( (end - start) / 1000000 ))
    echo "  Request $i: ${duration}ms"
done

# Test 2: Cached
echo -e "\n✅ With Cache:"
# Pre-populate cache
cached_api_call "$CACHE_KEY" "$API_URL" 300 >/dev/null

for i in 1 2 3; do
    start=$(date +%s%N)
    redis_get "$CACHE_KEY" > /dev/null
    end=$(date +%s%N)
    
    duration=$(( (end - start) / 1000000 ))
    echo "  Request $i: ${duration}ms"
done

🎓 Best Practices

1. Cache Key Naming

bash
# Good: Hierarchical, descriptive
cache_key="weather:singapore:daily"
cache_key="user:123:profile"
cache_key="api:github:rate_limit"

# Bad: Vague, collision-prone
cache_key="data"
cache_key="temp"

2. Error Handling

bash
fetch_with_cache() {
    local key="$1"
    local url="$2"
    
    # Try cache first
    local cached
    cached=$(redis_get_json "$key")
    
    if [ "$cached" != "null" ]; then
        echo "$cached"
        return 0
    fi
    
    # Fetch with error handling
    local response
    response=$(curl -s --max-time 10 "$url")
    
    if [ $? -ne 0 ] || [ -z "$response" ]; then
        # Return stale cache if available (optional)
        echo "⚠️  API failed, no cache" >&2
        return 1
    fi
    
    # Cache successful response
    redis_cache_json "$key" "$response" 300
    echo "$response"
}

3. Cache Warming

bash
# Pre-populate cache before peak hours
warm_cache() {
    echo "🔥 Warming cache..."
    
    # Pre-fetch common data
    cached_api_call "config:main" "$API_BASE/config" 3600 >/dev/null
    cached_api_call "users:top" "$API_BASE/users/top" 300 >/dev/null
    cached_api_call "prices:all" "$API_BASE/prices" 300 >/dev/null
    
    echo "✅ Cache warmed"
}

# Run on cron at 8 AM
0 8 * * * ~/scripts/warm-cache.sh

✅ Verification Checklist

  • Redis installed and running (redis-cli ping returns PONG)
  • Helper functions saved and executable
  • API calls include error handling
  • Appropriate TTL selected for each data type
  • Cache keys follow naming convention
  • Performance tested (cached vs non-cached)
  • Memory usage monitored (redis_stats)

🐛 Troubleshooting

Redis not running

bash
# Check status
sudo systemctl status redis

# Start Redis
sudo systemctl start redis

# Auto-start on boot
sudo systemctl enable redis

Connection refused

bash
# Check Redis is listening
netstat -tlnp | grep 6379

# Check firewall
sudo ufw allow 6379  # If needed locally

Memory issues

bash
# Check memory usage
redis-cli info memory

# Set max memory in redis.conf
maxmemory 256mb
maxmemory-policy allkeys-lru  # Evict least recently used


Questions? Join the OpenClaw Discord

Ada Pertanyaan? Yuk Ngobrol!

Butuh bantuan setup OpenClaw, konsultasi IT, atau mau diskusi project engineering? Book a call langsung — gratis.

Book a Call — Gratis

via Cal.com • WITA (UTC+8)

📬 Subscribe Newsletter

Free

Dapat alert setiap ada artikel baru langsung ke inbox kamu. Free, no spam. 🚀

👥 Join 0+ engineers & tech enthusiasts

F

Zainul Fanani

Founder, Radian Group. Engineering & tech enthusiast.

💬 Komentar

Catatan Fanani

Ngutak-ngatik teknologi, nulis pengalaman.

Perusahaan

  • CV Radian Fokus Mandiri — Balikpapan
  • PT UNO Solusi Teknik — Balikpapan
  • PT Reka Formasi Elektrika — Jakarta
  • PT Raya Fokus Solusi — Sidoarjo
© 2026 Catatan Fanani. All rights reserved.