---
title: "Redis Caching Patterns - Cache-Aside &amp; Write-Through"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/redis-caching-patterns-architecture
---

![Blog post image for Redis Caching Patterns: Cache-Aside, Write-Through & Cache Invalidation - Master production-ready Redis caching patterns with practical examples. Learn cache-aside (lazy loading), write-through, consistency patterns, TTL strategies, and cache invalidation techniques to reduce database load and improve application performance.](/_astro/hero.Ck2oLF89_Z1jFDQ9.webp)

[Home](/)›[Codesnippets](/codesnippets)›[All Categories](/codesnippets/categories)›[Backend](/codesnippets/categories/backend)

Codesnippets

[Prev in BackendNode.js Environment Variable Validation with Zod at Startup](/codesnippets/post/nodejs-env-validation-zod-startup)

[Backend](/codesnippets/categories/backend)[Performance](/codesnippets/categories/performance)[Python, Node.js and Go](/codesnippets/python-nodejs-and-go)

# Redis Caching Patterns: Cache-Aside, Write-Through & Cache Invalidation

[Mohammad Abu Mattar](/authors/mohammad-abu-mattar)Published: 07 Apr 202605 Mins read04 Mins listen

[Markdown for AI(opens in a new tab)](/post/redis-caching-patterns-architecture/index.md "Open the plain-Markdown version of this page, for pasting into an AI tool")

TL;DR

Master production-ready Redis caching patterns with practical examples. Learn cache-aside (lazy loading), write-through, consistency patterns, TTL strategies, and cache invalidation techniques to reduce database load and improve application performance.

Series

[Performance & Scaling](/series/performance--scaling)1/1

All posts in this series (1)

Code Snippets1

1.  [Redis Caching Patterns: Cache-Aside, Write-Through & Cache InvalidationYou are here](/codesnippets/post/redis-caching-patterns-architecture)

### Redis Caching Patterns: Cache-Aside, Write-Through & Cache Invalidation

Contents

[Need to scale your backend without throwing money at servers? Start with Redis caching patterns.](#need-to-scale-your-backend-without-throwing-money-at-servers-start-with-redis-caching-patterns)[The Problem](#the-problem)[Database load under scale](#database-load-under-scale)[The Solution](#the-solution)[Redis caching patterns](#redis-caching-patterns)[TL;DR](#tldr)[Pattern 1: Cache-Aside (Lazy Loading)](#pattern-1-cache-aside-lazy-loading)[How it works](#how-it-works)[Python implementation](#python-implementation)[Node.js implementation](#nodejs-implementation)[Go implementation](#go-implementation)[Pattern 2: Write-Through](#pattern-2-write-through)[How it works](#how-it-works)[When to use it](#when-to-use-it)[Python implementation](#python-implementation)[Node.js implementation](#nodejs-implementation)[Pattern 3: TTL Strategy](#pattern-3-ttl-strategy)[Automatic expiration](#automatic-expiration)[Sliding Window TTL](#sliding-window-ttl)[Pattern 4: Cache Invalidation](#pattern-4-cache-invalidation)[Event-driven invalidation](#event-driven-invalidation)[Tag-based invalidation](#tag-based-invalidation)[Preventing cache stampedes](#preventing-cache-stampedes)[The Problem](#the-problem)[Solution: probabilistic regeneration](#solution-probabilistic-regeneration)[Distributed lock pattern](#distributed-lock-pattern)[Serialization strategies](#serialization-strategies)[JSON vs MessagePack vs Protocol Buffers](#json-vs-messagepack-vs-protocol-buffers)[Monitoring cache performance](#monitoring-cache-performance)[Track hit rate](#track-hit-rate)[Connection pooling](#connection-pooling)[Best practices](#best-practices)[1\. Consistent key naming](#1-consistent-key-naming)[2\. Set Reasonable TTLs](#2-set-reasonable-ttls)[3\. Plan for failures](#3-plan-for-failures)[Conclusion](#conclusion)[Resources](#resources)

### [Need to scale your backend without throwing money at servers? Start with Redis caching patterns.](#need-to-scale-your-backend-without-throwing-money-at-servers-start-with-redis-caching-patterns)

Most databases can handle hundreds of queries per second, but thousands? Your app slows to a crawl. Adding another database server just moves the problem. Serving data from memory 99% of the time with Redis fixes it instead.

## [The Problem](#the-problem)

### [Database load under scale](#database-load-under-scale)

```
1100 requests/sec for "get product details":2• Database: 200ms per query3• 100 × 200ms = serious bottleneck4• Add 1000 concurrent users = complete collapse5• Throwing more servers doesn't help (they all queue at the database)
```

## [The Solution](#the-solution)

### [Redis caching patterns](#redis-caching-patterns)

Redis stores data in memory, so results come back in microseconds instead of milliseconds. The caching pattern you choose decides whether the cache helps or hurts.

### [TL;DR](#tldr)

-   **Cache-Aside**: Check cache first, miss triggers database load + populate cache (most flexible)
-   **Write-Through**: Update cache and database together (most consistent)
-   **TTL Strategy**: Keys expire automatically, so stale data clears without manual invalidation
-   **Cache Invalidation**: Delete the cache entry when data updates so the next request refreshes it
-   **Stampede Prevention**: Stop the thundering herd that follows a popular key expiring

## [Pattern 1: Cache-Aside (Lazy Loading)](#pattern-1-cache-aside-lazy-loading)

### [How it works](#how-it-works)

```
11. Request comes in22. Check Redis cache33. Cache HIT → return immediately (microseconds)44. Cache MISS → query database (milliseconds)55. Populate cache with TTL66. Next request hits cache
```

### [Python implementation](#python-implementation)

cache\_aside\_python.py

```
1import redis2import json3from functools import wraps4import time5
6redis_client = redis.Redis(host='localhost', port=6379, decode_responses=True)7
8def cache_aside(ttl=3600):9    """Decorator implementing cache-aside pattern"""10    def decorator(func):11        @wraps(func)12        def wrapper(key, *args, **kwargs):13            # Check cache first14            cached = redis_client.get(key)15            if cached:16                # Cache hit17                return json.loads(cached)18
19            # Cache miss: call function (hits database)20            result = func(*args, **kwargs)21
22            # Populate cache with TTL23            redis_client.setex(key, ttl, json.dumps(result))24
25            return result26        return wrapper27    return decorator28
29# Database query (expensive)30def get_user_from_db(user_id):31    # Simulated database query32    time.sleep(0.2)  # 200ms33    return {"id": user_id, "name": f"User {user_id}", "email": f"user{user_id}@example.com"}34
35# Decorated function36@cache_aside(ttl=3600)37def get_user(user_id):38    return get_user_from_db(user_id)39
40# Usage41print(get_user(f"user:1"))  # First call: 200ms (cache miss)42print(get_user(f"user:1"))  # Second call: <1ms (cache hit)
```

### [Node.js implementation](#nodejs-implementation)

cache\_aside\_node.ts

```
1import {createClient} from 'redis';2import {promisify} from 'util';3
4const redis = createClient();5redis.connect();6
7const getAsync = promisify(redis.get).bind(redis);8const setexAsync = promisify(redis.setex).bind(redis);9
10// Database query (expensive)11async function getUserFromDB(userId: string) {12  // Simulated database query13  await new Promise((resolve) => setTimeout(resolve, 200));14  return {15    id: userId,16    name: `User ${userId}`,17    email: `user${userId}@example.com`,18  };19}20
21// Cache-aside implementation22async function getUser(userId: string, ttl = 3600) {23  const cacheKey = `user:${userId}`;24
25  // Check cache26  const cached = await getAsync(cacheKey);27  if (cached) {28    return JSON.parse(cached);29  }30
31  // Cache miss: hit database32  const user = await getUserFromDB(userId);33
34  // Populate cache35  await setexAsync(cacheKey, ttl, JSON.stringify(user));36
37  return user;38}39
40// Usage41(async () => {42  console.time('first');43  await getUser('user:1'); // 200ms (miss)44  console.timeEnd('first');45
46  console.time('second');47  await getUser('user:1'); // <1ms (hit)48  console.timeEnd('second');49})();
```

### [Go implementation](#go-implementation)

cache\_aside\_go.go

```
1package main2
3import (4  "context"5  "encoding/json"6  "fmt"7  "github.com/redis/go-redis/v9"8  "time"9)10
11type User struct {12  ID    int    `json:"id"`13  Name  string `json:"name"`14  Email string `json:"email"`15}16
17var rdb = redis.NewClient(&redis.Options{18  Addr: "localhost:6379",19})20
21func getUserFromDB(ctx context.Context, userID int) (User, error) {22  // Simulated database query23  time.Sleep(200 * time.Millisecond)24  return User{25    ID:    userID,26    Name:  fmt.Sprintf("User %d", userID),27    Email: fmt.Sprintf("user%d@example.com", userID),28  }, nil29}30
31func getUser(ctx context.Context, userID int) (User, error) {32  cacheKey := fmt.Sprintf("user:%d", userID)33
34  // Check cache35  val, err := rdb.Get(ctx, cacheKey).Result()36  if err == nil {37    var user User38    json.Unmarshal([]byte(val), &user)39    return user, nil40  }41
42  // Cache miss: hit database43  user, err := getUserFromDB(ctx, userID)44  if err != nil {45    return user, err46  }47
48  // Populate cache (1 hour TTL)49  data, _ := json.Marshal(user)50  rdb.SetEx(ctx, cacheKey, string(data), time.Hour)51
52  return user, nil53}
```

## [Pattern 2: Write-Through](#pattern-2-write-through)

### [How it works](#how-it-works-1)

```
11. Data update arrives22. Update cache AND database together33. Both succeed or both fail (atomic)44. Next read hits cache (always consistent)
```

### [When to use it](#when-to-use-it)

-   Critical data (payments, user accounts)
-   Data that changes frequently
-   When consistency is more important than speed

### [Python implementation](#python-implementation-1)

write\_through\_python.py

```
1import redis2import json3
4redis_client = redis.Redis(host='localhost', port=6379, decode_responses=True)5
6def update_user_write_through(user_id: int, user_data: dict):7    """Update cache AND database together"""8    cache_key = f"user:{user_id}"9
10    try:11        # Update cache first (faster)12        redis_client.setex(cache_key, 3600, json.dumps(user_data))13
14        # Then update database15        # Assuming `db.update_user(user_id, user_data)` exists16        db.update_user(user_id, user_data)17
18        return True19    except Exception as e:20        # If database fails, invalidate cache21        redis_client.delete(cache_key)22        raise e23
24# Usage25update_user_write_through(42, {26    "name": "Alice",27    "email": "alice@example.com"28})29
30# Next read hits cache31user = redis_client.get("user:42")
```

### [Node.js implementation](#nodejs-implementation-1)

write\_through\_node.ts

```
1async function updateUserWriteThrough(userId: string, userData: any) {2  const cacheKey = `user:${userId}`;3
4  try {5    // Update cache first6    await redis.setex(cacheKey, 3600, JSON.stringify(userData));7
8    // Then update database9    await db.updateUser(userId, userData);10
11    return true;12  } catch (error) {13    // Rollback: invalidate cache on database failure14    await redis.del(cacheKey);15    throw error;16  }17}
```

## [Pattern 3: TTL Strategy](#pattern-3-ttl-strategy)

### [Automatic expiration](#automatic-expiration)

Redis automatically deletes keys after TTL expires. No manual invalidation needed.

ttl\_strategies.py

```
1import redis2from datetime import datetime, timedelta3
4redis_client = redis.Redis()5
6# Different TTL for different data types7def cache_with_ttl(key: str, value: str, data_type: str):8    ttl_map = {9        "user_profile": 3600,           # 1 hour (changes infrequently)10        "product": 86400,                # 24 hours (stable data)11        "product_price": 300,            # 5 minutes (changes frequently)12        "session": 1800,                 # 30 minutes (security)13        "statistics": 60,                # 1 minute (real-time)14    }15
16    ttl = ttl_map.get(data_type, 3600)17    redis_client.setex(key, ttl, value)18
19# Usage20cache_with_ttl("product:123", "iPhone 15", "product")        # 24 hours21cache_with_ttl("price:apple", "$999", "product_price")       # 5 minutes22cache_with_ttl("stats:daily", "1000 users", "statistics")    # 1 minute
```

### [Sliding Window TTL](#sliding-window-ttl)

Reset TTL on each access (useful for sessions):

sliding\_ttl.py

```
1def get_with_sliding_ttl(key: str, ttl: int):2    """Get value and reset TTL"""3    value = redis_client.get(key)4    if value:5        # Reset TTL on access6        redis_client.expire(key, ttl)7    return value8
9# Usage: Session stays alive as long as user is active10session = get_with_sliding_ttl("session:abc123", 1800)  # 30 min
```

## [Pattern 4: Cache Invalidation](#pattern-4-cache-invalidation)

### [Event-driven invalidation](#event-driven-invalidation)

Delete cache when data changes:

invalidation\_event.py

```
1def update_product(product_id: int, new_data: dict):2    # Update database3    db.update_product(product_id, new_data)4
5    # Invalidate cache immediately6    redis_client.delete(f"product:{product_id}")7
8    # Also invalidate related caches (cascade invalidation)9    redis_client.delete(f"category:{new_data['category']}")10    redis_client.delete("products:all")11
12# When product price changes13update_product(123, {"price": "$899", "category": "electronics"})14# → Deletes "product:123", "category:electronics", "products:all"
```

### [Tag-based invalidation](#tag-based-invalidation)

Invalidate multiple keys with one operation:

tag\_invalidation.py

```
1import json2
3def cache_with_tags(key: str, value: str, tags: list):4    """Cache value and tag it for bulk invalidation"""5    # Store value6    redis_client.setex(key, 3600, value)7
8    # Store tag reference (set of keys with this tag)9    for tag in tags:10        redis_client.sadd(f"tag:{tag}", key)11
12def invalidate_by_tag(tag: str):13    """Invalidate all keys with a tag"""14    # Get all keys with this tag15    keys = redis_client.smembers(f"tag:{tag}")16
17    # Delete all tagged keys18    if keys:19        redis_client.delete(*keys)20
21    # Clean up tag22    redis_client.delete(f"tag:{tag}")23
24# Usage25cache_with_tags("product:1", "iPhone", ["electronics", "apple"])26cache_with_tags("product:2", "MacBook", ["electronics", "apple"])27cache_with_tags("product:3", "Apple Watch", ["wearables", "apple"])28
29# Invalidate all Apple products at once30invalidate_by_tag("apple")
```

## [Preventing cache stampedes](#preventing-cache-stampedes)

### [The Problem](#the-problem-1)

```
1Thousands of requests for a popular key2↓3Key expires4↓5ALL requests hit database simultaneously (thundering herd)6↓7Database overload, service degradation
```

### [Solution: probabilistic regeneration](#solution-probabilistic-regeneration)

stampede\_prevention.py

```
1import redis2import random3import time4
5redis_client = redis.Redis()6
7def get_with_low_ttl_regen(key: str, expensive_calculation, ttl=3600):8    """9    Regenerate cache probabilistically near expiration10    Avoids thundering herd when TTL reaches 011    """12    value = redis_client.get(key)13
14    if value:15        # Check TTL16        remaining_ttl = redis_client.ttl(key)17
18        # Near end of life? Regenerate probabilistically19        if remaining_ttl < 0:20            # Key expired21            pass  # Recalculate below22        elif remaining_ttl < ttl * 0.2:  # Last 20% of life23            # Probability of regeneration increases as TTL decreases24            prob = 1 - (remaining_ttl / (ttl * 0.2))25            if random.random() < prob:26                # First request regenerates, others get stale data temporarily27                new_value = expensive_calculation()28                redis_client.setex(key, ttl, new_value)29                return new_value30
31        return value32
33    # Cache miss: calculate fresh data34    result = expensive_calculation()35    redis_client.setex(key, ttl, result)36    return result
```

### [Distributed lock pattern](#distributed-lock-pattern)

Prevent parallel cache recalculations:

lock\_pattern.py

```
1import time2import uuid3
4def get_with_lock(key: str, expensive_calculation, lock_timeout=10):5    """Use lock to prevent multiple calculations"""6    lock_key = f"lock:{key}"7    cache_key = key8
9    # Check cache10    cached = redis_client.get(cache_key)11    if cached:12        return cached13
14    # Try to acquire lock15    lock_id = str(uuid.uuid4())16    acquired = redis_client.set(lock_key, lock_id, nx=True, ex=lock_timeout)17
18    if acquired:19        # We got the lock, calculate20        try:21            result = expensive_calculation()22            redis_client.setex(cache_key, 3600, result)23            return result24        finally:25            # Release lock26            if redis_client.get(lock_key) == lock_id:27                redis_client.delete(lock_key)28    else:29        # Someone else got the lock, wait for result30        for _ in range(10):31            time.sleep(0.1)32            cached = redis_client.get(cache_key)33            if cached:34                return cached35
36        # Timeout: calculate ourselves37        return expensive_calculation()
```

## [Serialization strategies](#serialization-strategies)

### [JSON vs MessagePack vs Protocol Buffers](#json-vs-messagepack-vs-protocol-buffers)

serialization.py

```
1import json2import msgpack3
4def store_json(key, obj):5    """Simple but larger"""6    redis_client.set(key, json.dumps(obj))7
8def store_msgpack(key, obj):9    """Smaller, faster"""10    redis_client.set(key, msgpack.packb(obj))11
12# Benchmark: 1000 products13products = [{"id": i, "name": f"Product {i}", "price": 99.99} for i in range(1000)]14
15json_size = len(json.dumps(products))          # ~50KB16msgpack_size = len(msgpack.packb(products))    # ~30KB (40% smaller)17
18# Choice depends on:19# - JSON: Human readable, broad ecosystem20# - MsgPack: Smaller, faster21# - Protobuf: Strongly typed, enterprise
```

## [Monitoring cache performance](#monitoring-cache-performance)

### [Track hit rate](#track-hit-rate)

monitoring.py

```
1import redis2
3redis_client = redis.Redis()4
5def get_cache_stats():6    """Get cache performance metrics"""7    info = redis_client.info('stats')8
9    total_commands = info.get('total_commands_processed', 0)10    hits = info.get('keyspace_hits', 0)11    misses = info.get('keyspace_misses', 0)12
13    hit_rate = (hits / (hits + misses)) * 100 if (hits + misses) > 0 else 014
15    return {16        "hit_rate": hit_rate,17        "total_hits": hits,18        "total_misses": misses,19        "evictions": info.get('evicted_keys', 0),20        "used_memory": info.get('used_memory_human', 'N/A'),21    }22
23# Target: >80% hit rate24# <50%: Cache is ineffective, reconsider keys/TTLs25# >95%: Good fit for caching strategy
```

## [Connection pooling](#connection-pooling)

Reuse connections instead of creating new ones:

connection\_pooling.py

```
1import redis2from redis.connection import ConnectionPool3
4# Without pooling (inefficient)5conn = redis.Redis()6
7# With pooling (efficient)8pool = ConnectionPool.from_url(9    'redis://localhost:6379',10    max_connections=50,11    decode_responses=True12)13redis_client = redis.Redis(connection_pool=pool)14
15# Reuses connections automatically16for i in range(1000):17    redis_client.get(f"key:{i}")
```

## [Best practices](#best-practices)

### [1\. Consistent key naming](#1-consistent-key-naming)

```
1# Good: Hierarchical, searchable2"user:123"3"user:123:settings"4"product:456"5"product:456:reviews"6"order:789:items"7
8# Bad: Ambiguous9"u123"10"prod_456"11"item-789"
```

### [2\. Set Reasonable TTLs](#2-set-reasonable-ttls)

```
1TTL_STRATEGY = {2    "user_profile": 3600,           # 1 hour3    "product_catalog": 86400,       # 24 hours4    "session": 1800,                # 30 minutes5    "api_response": 300,            # 5 minutes6    "real_time_data": 60,           # 1 minute7}
```

### [3\. Plan for failures](#3-plan-for-failures)

```
1def safe_cache_get(key: str, fallback_fn):2    """Gracefully handle Redis failures"""3    try:4        value = redis_client.get(key)5        if value:6            return value7    except redis.ConnectionError:8        # Redis down? Use fallback9        pass10
11    # No cache: call function12    return fallback_fn()
```

## [Conclusion](#conclusion)

Redis caching is the difference between a responsive application and a slow one.

Choose the right pattern (cache-aside for flexibility, write-through for consistency), set appropriate TTLs, prevent stampedes, and monitor hit rates. Combined with database optimization, caching is what keeps a high-traffic system fast.

## [Resources](#resources)

-   [Redis Documentation](https://redis.io/documentation)
-   [REDIS Caching Strategies](https://redis.io/docs/manual/client-side-caching/)
-   [redis-py Documentation](https://redis-py.readthedocs.io/)
-   [node-redis Documentation](https://node-redis.io/)
-   [go-redis Documentation](https://github.com/redis/go-redis)

Was this useful?

## Tags

[#Redis](/codesnippets/tags/redis)[#Caching](/codesnippets/tags/caching)[#Performance](/codesnippets/tags/performance)[#Backend](/codesnippets/tags/backend)[#Microservices](/codesnippets/tags/microservices)[#Python](/codesnippets/tags/python)[#NodeJS](/codesnippets/tags/nodejs)[#Go](/codesnippets/tags/go)[#Optimization](/codesnippets/tags/optimization)

## Share

[Facebook](https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture "Share on Facebook")[Twitter](https://twitter.com/intent/tweet/?text=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation&url=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture "Share on Twitter")[LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture&title=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation&summary=Master%20production-ready%20Redis%20caching%20patterns%20with%20practical%20examples.%20Learn%20cache-aside%20\(lazy%20loading\)%2C%20write-through%2C%20consistency%20patterns%2C%20TTL%20strategies%2C%20and%20cache%20invalidation%20techniques%20to%20reduce%20database%20load%20and%20improve%20application%20performance.&source=https://mkabumattar.com "Share on LinkedIn")[WhatsApp](https://wa.me/?text=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation%20https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture "Share on WhatsApp")[Telegram](https://t.me/share/url?url=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture&text=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation "Share on Telegram")[Reddit](https://www.reddit.com/submit?url=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture&title=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation "Share on Reddit")[Hacker News](http://news.ycombinator.com/submitlink?u=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture&t=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation "Share on Hacker News")[Pinterest](https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture&media=&description=Master%20production-ready%20Redis%20caching%20patterns%20with%20practical%20examples.%20Learn%20cache-aside%20\(lazy%20loading\)%2C%20write-through%2C%20consistency%20patterns%2C%20TTL%20strategies%2C%20and%20cache%20invalidation%20techniques%20to%20reduce%20database%20load%20and%20improve%20application%20performance. "Share on Pinterest")[Email](<mailto:?subject=Redis%20Caching%20Patterns%3A%20Cache-Aside%2C%20Write-Through%20%26%20Cache%20Invalidation&body=Check out this article: https%3A%2F%2Fmkabumattar.com%2Fcodesnippets%2Fpost%2Fredis-caching-patterns-architecture>)

## Comments

## You might also enjoy

More posts on similar topics

[![PostgreSQL Query Optimization: Indexes, EXPLAIN ANALYZE & Execution Plans](/_astro/hero.WD7Zwlat_2dqoYO.webp)](/codesnippets/post/postgresql-query-optimization-indexes-explain)

## [PostgreSQL Query Optimization: Indexes, EXPLAIN ANALYZE & Execution Plans](/codesnippets/post/postgresql-query-optimization-indexes-explain)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Database](/codesnippets/categories/database)
-   [Performance](/codesnippets/categories/performance)

Need to optimize slow PostgreSQL queries? Use EXPLAIN ANALYZE and targeted indexing. Slow database queries kill application performance. Most developers don't know where the actual bottleneck is,

[#PostgreSQL](/codesnippets/tags/postgresql)[#QueryOptimization](/codesnippets/tags/queryoptimization)[#Indexes](/codesnippets/tags/indexes)+4 tags

[read more](/codesnippets/post/postgresql-query-optimization-indexes-explain)

[![Node.js Environment Variable Validation with Zod at Startup](/_astro/hero.DEQ8-R8h_1M1QX3.webp)](/codesnippets/post/nodejs-env-validation-zod-startup)

## [Node.js Environment Variable Validation with Zod at Startup](/codesnippets/post/nodejs-env-validation-zod-startup)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Nodejs](/codesnippets/categories/nodejs)
-   [Typescript](/codesnippets/categories/typescript)
-   [Backend](/codesnippets/categories/backend)

Most Node.js apps treat process.env like a trusted friend. You reach into it whenever you need a value, assume the key is there, assume it's spelled right, and assume the string is actually the type

[#Node.js](/codesnippets/tags/nodejs)[#TypeScript](/codesnippets/tags/typescript)[#Zod](/codesnippets/tags/zod)+5 tags

[read more](/codesnippets/post/nodejs-env-validation-zod-startup)

[![Python Async HTTP Requests with aiohttp: Fetch Multiple URLs Concurrently](/_astro/hero.DFXqyT8a_Zqhl7o.webp)](/codesnippets/post/python-async-http-aiohttp-concurrent-requests)

## [Python Async HTTP Requests with aiohttp: Fetch Multiple URLs Concurrently](/codesnippets/post/python-async-http-aiohttp-concurrent-requests)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Python](/codesnippets/categories/python)
-   [Async](/codesnippets/categories/async)
-   [Networking](/codesnippets/categories/networking)

Quick Tip Reuse one aiohttp session, fan your requests out with asyncio.gather, and cap them with a semaphore to fetch hundreds of URLs in the time one loop would take. The Problem \*\*The

[#Python](/codesnippets/tags/python)[#Aiohttp](/codesnippets/tags/aiohttp)[#Asyncio](/codesnippets/tags/asyncio)+3 tags

[read more](/codesnippets/post/python-async-http-aiohttp-concurrent-requests)

[![Optimizing your python code with \_\_slots\_\_?](/_astro/hero.DP_vYsHU_gYDXn.webp)](/codesnippets/post/python-slots-optimization)

## [Optimizing your python code with \_\_slots\_\_?](/codesnippets/post/python-slots-optimization)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Python](/codesnippets/categories/python)
-   [Productivity](/codesnippets/categories/productivity)

Memory optimization with slots Understanding the problem Optimizing data models in big data workflows with slots In big data and MLOps workflows, you often work with massive

[#Python](/codesnippets/tags/python)[#MemoryOptimization](/codesnippets/tags/memoryoptimization)[#DataScience](/codesnippets/tags/datascience)+4 tags

[read more](/codesnippets/post/python-slots-optimization)

[![AWS EC2 Instance Management with Boto3: Start, Stop, and Query Instances](/_astro/hero.PnHlvJay_ZkFqWG.webp)](/codesnippets/post/aws-ec2-instance-management-boto3-python)

## [AWS EC2 Instance Management with Boto3: Start, Stop, and Query Instances](/codesnippets/post/aws-ec2-instance-management-boto3-python)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Cloud](/codesnippets/categories/cloud)
-   [Aws](/codesnippets/categories/aws)
-   [Devops](/codesnippets/categories/devops)
-   [Automation](/codesnippets/categories/automation)

If you've ever spent 20 minutes clicking through the AWS Console just to stop a handful of dev instances, you already know the pain. It's tedious, it doesn't scale, and one wrong click can ruin your a

[#AWS](/codesnippets/tags/aws)[#EC2](/codesnippets/tags/ec2)[#Boto3](/codesnippets/tags/boto3)+6 tags

[read more](/codesnippets/post/aws-ec2-instance-management-boto3-python)

[![Top 7 Open Source OCR Models for Document Processing](/_astro/hero.DIJ2knuO_ah4wu.webp)](/codesnippets/post/top-7-open-source-ocr-models)

## [Top 7 Open Source OCR Models for Document Processing](/codesnippets/post/top-7-open-source-ocr-models)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Ai ml](/codesnippets/categories/ai-ml)
-   [Computer vision](/codesnippets/categories/computer-vision)
-   [Document processing](/codesnippets/categories/document-processing)

AI Tool Turn your documents into accurate digital copies with these open source OCR models. Instead of fighting messy text extraction, you get clean markdown from PDFs, images, and scanned docume

[#OCR](/codesnippets/tags/ocr)[#Computer Vision](/codesnippets/tags/computer-vision)[#Document Processing](/codesnippets/tags/document-processing)+5 tags

[read more](/codesnippets/post/top-7-open-source-ocr-models)

6 related posts
