SiamCafe · Blog
Payload CMS Capacity Planning — วางแผน
บทความ

Payload CMS Capacity Planning — วางแผน

เผยแพร่ 28 พฤษภาคม 2569

Payload CMS Capacity Planning

Payload CMS Capacity Planning — วางแผน

Payload CMS Capacity Planning TypeScript Node.js MongoDB PostgreSQL CDN Redis Caching Sizing API Performance Production

SizeCPU/RAMDatabaseInstancesRPS Target
Small1 vCPU / 1GBMongoDB 1GB1 Node.js50-100
Medium2 vCPU / 4GBMongoDB 10GB RS2 + LB500-1,000
Large4 vCPU / 8GBMongoDB 50GB RS4 + LB + CDN5,000-10,000
Enterprise8+ vCPU / 16GB+MongoDB ShardedAuto-scale + CDN10,000+

Sizing Calculator

# === Payload CMS Capacity Calculator ===

from dataclasses import dataclass

@dataclass
class SizingInput:
    content_count: int
    avg_doc_size_kb: float
    media_count: int
    avg_media_size_mb: float
    api_rps: int
    admin_users: int

@dataclass
class SizingResult:
    db_storage_gb: float
    media_storage_gb: float
    node_instances: int
    cpu_per_instance: int
    ram_per_instance_gb: int
    db_connections: int
    redis_memory_gb: float

def calculate_sizing(inp: SizingInput) -> SizingResult:
    db_gb = (inp.content_count * inp.avg_doc_size_kb * 3) / (1024 * 1024)
    media_gb = (inp.media_count * inp.avg_media_size_mb * 1.5) / 1024
    if inp.api_rps <= 100:
        instances, cpu, ram = 1, 1, 1
    elif inp.api_rps <= 1000:
        instances, cpu, ram = 2, 2, 4
    elif inp.api_rps <= 10000:
        instances, cpu, ram = 4, 4, 8
    else:
        instances, cpu, ram = 8, 8, 16
    db_conn = instances * 10
    redis_gb = max(0.5, inp.api_rps * 0.001)
    return SizingResult(round(db_gb, 2), round(media_gb, 2),
        instances, cpu, ram, db_conn, round(redis_gb, 2))

scenarios = [
    ("Blog (Personal)", SizingInput(500, 5, 200, 2, 50, 2)),
    ("Corporate Site", SizingInput(5000, 10, 2000, 3, 500, 10)),
    ("E-commerce", SizingInput(50000, 15, 20000, 5, 5000, 50)),
    ("Enterprise Platform", SizingInput(500000, 20, 100000, 4, 20000, 200)),
]

print("=== Capacity Planning ===")
for name, inp in scenarios:
    r = calculate_sizing(inp)
    print(f"\n  [{name}]")
    print(f"    Content: {inp.content_count:,} docs | Media: {inp.media_count:,} files")
    print(f"    DB: {r.db_storage_gb} GB | Media: {r.media_storage_gb} GB")
    print(f"    Instances: {r.node_instances}x ({r.cpu_per_instance} vCPU {r.ram_per_instance_gb}GB)")
    print(f"    DB Connections: {r.db_connections} | Redis: {r.redis_memory_gb} GB")

Caching Architecture

Payload CMS Capacity Planning — วางแผน
# === Caching Strategy ===

# Payload Hook for Cache Invalidation
# // collections/Posts.ts
# const Posts: CollectionConfig = {
#   slug: 'posts',
#   hooks: {
#     afterChange: [
#       async ({ doc }) => {
#         // Purge CDN cache
#         await fetch(`https://api.cloudflare.com/client/v4/zones//purge_cache`, {
#           method: 'POST',
#           headers: { Authorization: `Bearer ` },
#           body: JSON.stringify({ files: [`https://example.com/api/posts/`] })
#         });
#         // Invalidate Redis cache
#         await redis.del(`posts:`);
#         await redis.del('posts:list');
#         // Trigger Next.js revalidation
#         await fetch(`https://frontend.com/api/revalidate?secret=&path=/blog/`);
#       }
#     ]
#   }
# };

@dataclass
class CacheLayer:
    layer: str
    tool: str
    ttl: str
    invalidation: str
    hit_rate: str

layers = [
    CacheLayer("CDN Edge",
        "Cloudflare / CloudFront",
        "60s (API) 1y (Media)",
        "Webhook Purge on afterChange",
        "70-90% (Static content)"),
    CacheLayer("Application (Redis)",
        "Redis 6+",
        "60-300s per content type",
        "redis.del on afterChange Hook",
        "50-80% (Dynamic queries)"),
    CacheLayer("Full Page (ISR)",
        "Next.js ISR / Vercel",
        "revalidate: 60-3600",
        "On-demand revalidation API",
        "90%+ (Static pages)"),
    CacheLayer("Browser",
        "HTTP Cache-Control ETag",
        "60s (API) 1y (Assets)",
        "ETag mismatch → 200 else 304",
        "High (repeat visitors)"),
    CacheLayer("Database",
        "MongoDB WiredTiger / PG shared_buffers",
        "Automatic (LRU)",
        "Automatic on write",
        "Depends on working set vs RAM"),
]

print("=== Caching Layers ===")
for c in layers:
    print(f"  [{c.layer}] Tool: {c.tool}")
    print(f"    TTL: {c.ttl}")
    print(f"    Invalidation: {c.invalidation}")
    print(f"    Hit Rate: {c.hit_rate}")

Monitoring

# === Production Monitoring ===

@dataclass
class MonitorMetric:
    metric: str
    source: str
    target: str
    alert: str

monitoring = [
    MonitorMetric("API Response Time P99",
        "Prometheus / APM (Datadog NewRelic)",
        "< 200ms (cached) < 500ms (uncached)",
        "> 1s → P2 Check DB Cache Instances"),
    MonitorMetric("API Throughput (RPS)",
        "Prometheus http_requests_total",
        "ตาม Sizing Target",
        "< 50% target → Check if underprovisioned"),
    MonitorMetric("Error Rate",
        "Prometheus http_errors_total / total",
        "< 0.1%",
        "> 1% → P1 Check Logs DB Connection"),
    MonitorMetric("Database Connection Pool",
        "MongoDB/PG metrics",
        "< 80% pool usage",
        "> 90% → P2 Increase Pool or Scale"),
    MonitorMetric("Cache Hit Rate",
        "Redis INFO stats / CDN Analytics",
        "> 60% Redis > 80% CDN",
        "< 30% → Review Cache Keys TTL"),
    MonitorMetric("Media Storage Usage",
        "S3/Disk metrics",
        "< 80% allocated",
        "> 90% → P3 Expand Storage Archive Old"),
]

print("=== Monitoring ===")
for m in monitoring:
    print(f"  [{m.metric}] Target: {m.target}")
    print(f"    Source: {m.source}")
    print(f"    Alert: {m.alert}")

เคล็ดลับ

  • CDN: ใช้ CDN Cache API Response ลด Load 70-90%
  • Redis: Cache Popular Queries ใน Redis TTL 60-300s
  • Hook: ใช้ afterChange Hook Invalidate Cache อัตโนมัติ
  • ISR: ใช้ Next.js ISR + On-demand Revalidation ลด API Load 90%+
  • Index: สร้าง Database Index ตาม Query Pattern ทุกตัว

Payload CMS คืออะไร

Open Source Headless CMS TypeScript Node.js MongoDB PostgreSQL REST GraphQL Admin Panel Auth Upload Versioning Localization Hooks